Index: .fossil-settings/ignore-glob
==================================================================
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -1,3 +1,2 @@
-appfsd
-appfsd.o
-appfsd.tcl.h
+workdir-*
+ARCHIVE

DELETED Makefile
Index: Makefile
==================================================================
--- Makefile
+++ /dev/null
@@ -1,43 +0,0 @@
-CC = gcc
-PKG_CONFIG = pkg-config
-CFLAGS = -Wall $(shell $(PKG_CONFIG) --cflags fuse) $(shell $(PKG_CONFIG) --cflags sqlite3) $(TCL_CFLAGS)
-LDFLAGS = $(TCL_LDFLAGS)
-LIBS = $(shell $(PKG_CONFIG) --libs fuse) $(shell $(PKG_CONFIG) --libs sqlite3) $(TCL_LIBS)
-PREFIX = /usr/local
-prefix = $(PREFIX)
-bindir = $(prefix)/bin
-sbindir = $(prefix)/sbin
-
-ifneq ($(TCLKIT_SDK_DIR),)
-TCLCONFIG_SH_PATH = $(TCLKIT_SDK_DIR)/lib/tclConfig.sh
-TCL_LDFLAGS = -Wl,-R,$(TCLKIT_SDK_DIR)/lib
-export TCLKIT_SDK_DIR
-else
-TCLCONFIG_SH_PATH = $(shell echo 'puts [::tcl::pkgconfig get libdir,install]' | tclsh)/tclConfig.sh
-endif
-TCL_CFLAGS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_INCLUDE_SPEC}")
-TCL_LIBS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_LIB_SPEC}")
-
-all: appfsd
-
-appfsd: appfsd.o
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o $(LIBS)
-
-appfsd.o: appfsd.c appfsd.tcl.h
-	$(CC) $(CPPFLAGS) $(CFLAGS) -o appfsd.o -c appfsd.c
-
-appfsd.tcl.h: appfsd.tcl
-	sed 's@[\\"]@\\&@g;s@^@   "@;s@$$@\\n"@' appfsd.tcl > appfsd.tcl.h.new
-	mv appfsd.tcl.h.new appfsd.tcl.h
-
-install: appfsd
-	if [ ! -d '$(DESTDIR)$(sbindir)' ]; then mkdir -p '$(DESTDIR)$(sbindir)'; chmod 755 '$(DESTDIR)$(sbindir)'; fi
-	cp appfsd '$(DESTDIR)$(sbindir)/'
-
-clean:
-	rm -f appfsd appfsd.o
-	rm -f appfsd.tcl.h
-
-distclean: clean
-
-.PHONY: all test clean distclean install

DELETED README.md
Index: README.md
==================================================================
--- README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-AppFS
-=====
-It's sort of like LazyFS.
-
-Usage
------
-Run:
-	1. # mkdir /tmp/appfs-cache /opt/appfs
-	2. # appfsd /opt/appfs
-
-
-Paths
------
-    AppFS should normally be mounted on "/opt/appfs".
-
-    /opt/appfs/hostname
-    	Fetches: http://hostname/appfs/index
-    	Contains CSV file: hash,extraData
-    	Fetches: http://hostname/appfs/sha1/<hash>
-    	Contains CSV file: package,version,os,cpuArch,sha1,isLatest
-
-    /opt/appfs/hostname/package/os-cpuArch/version
-    /opt/appfs/hostname/sha1/
-    	Fetches: http://hostname/appfs/sha1/<sha1>
-    	Contains CSV file:
-    		type,time,extraData,name
-    		type == directory; extraData = (null)
-    		type == symlink; extraData = source
-    		type == file; extraData = size,perms,sha1
-
-    /opt/appfs/hostname/{sha1,package/os-cpuArch/version}/file
-    	Fetches: http://hostname/appfs/sha1/<sha1>
-
-Database
---------
-    packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest)
-    files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory)
-
-Resources
----------
-http://appfs.rkeene.org/

DELETED appfs-mkfs
Index: appfs-mkfs
==================================================================
--- appfs-mkfs
+++ /dev/null
@@ -1,134 +0,0 @@
-#! /usr/bin/env bash
-
-pkgsdir="$1"
-appfsdir="$2"
-
-if [ -z  "${pkgsdir}" -o -z "${appfsdir}" ]; then
-	echo 'Usage: appfs-mk <pkgsdir> <appfsdir>' >&2
-
-	exit 1
-fi
-
-appfsdir="$(cd "${appfsdir}" && pwd)"
-if [ -z "${appfsdir}" ]; then
-	echo "Unable to find appfs directory." >&2
-
-	exit 1
-fi
-
-mkdir -p "${appfsdir}/sha1"
-
-function sha1() {
-	local filename
-
-	filename="$1"
-
-	openssl sha1 "${filename}" | sed 's@.*= @@'
-}
-
-function emit_manifest() {
-	find . -print0 | while IFS='' read -r -d $'\0' filename; do
-		if [ "${filename}" = '.' ]; then
-			continue
-		fi
-
-		filename="$(echo "${filename}" | sed 's@^\./@@' | head -n 1)"
-
-		if [ ! -e "${filename}" ]; then
-			continue
-		fi
-
-		if [ -h "${filename}" ]; then
-			type='symlink'
-		elif [ -d "${filename}" ]; then
-			type='directory'
-		elif [ -f "${filename}" ]; then
-			type='file'
-		else
-			continue
-		fi
-
-		case "${type}" in
-			directory)
-				stat_format='%Y'
-				extra_data=''
-				;;
-			symlink)
-				stat_format='%Y'
-				extra_data="$(readlink "${filename}")"
-				;;
-			file)
-				if [ -x "${filename}" ]; then
-					extra_data='x'
-				else
-					extra_data=''
-				fi
-
-				stat_format='%Y,%s'
-				filename_hash="$(sha1 "${filename}")"
-				extra_data="${extra_data},${filename_hash}"
-
-				filename_intree="${appfsdir}/sha1/${filename_hash}"
-
-				if [ ! -e "${filename_intree}" ]; then
-					cat "${filename}" > "${filename_intree}.tmp"
-
-					mv "${filename_intree}.tmp" "${filename_intree}"
-				fi
-				;;
-		esac
-		stat_data="$(stat --format="${stat_format}" "${filename}")"
-
-		if [ -z "${extra_data}" ]; then
-			echo "${type},${stat_data},${filename}"
-		else
-			echo "${type},${stat_data},${extra_data},${filename}"
-		fi
-	done
-}
-
-cd "${pkgsdir}" || exit 1
-
-packagelistfile="${appfsdir}/sha1/${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}.tmp"
-for package in *; do
-	(
-		cd "${package}" || exit 1
-
-		for os_cpuArch in *; do
-			os="$(echo "${os_cpuArch}" | cut -f 1 -d '-')"
-			cpuArch="$(echo "${os_cpuArch}" | cut -f 2- -d '-')"
-
-			(
-				cd "${os_cpuArch}" || exit 1
-
-				for version in *; do
-					if [ -h "${version}" ]; then
-						continue
-					fi
-
-					manifestfile="${appfsdir}/sha1/${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}.tmp"
-
-					(
-						cd "${version}" || exit 1
-
-						emit_manifest
-					) > "${manifestfile}"
-
-					manifestfile_hash="$(sha1 "${manifestfile}")"
-					mv "${manifestfile}" "${appfsdir}/sha1/${manifestfile_hash}"
-
-					# XXX:TODO: Determine if this is the latest version
-					isLatest='0'
-
-					echo "${package},${version},${os},${cpuArch},${manifestfile_hash},${isLatest}"
-				done
-
-			)
-		done
-	)
-
-done > "${packagelistfile}"
-packagelistfile_hash="$(sha1 "${packagelistfile}")"
-mv "${packagelistfile}" "${appfsdir}/sha1/${packagelistfile_hash}"
-
-echo "${packagelistfile_hash},sha1" > "${appfsdir}/index"

DELETED appfsd.c
Index: appfsd.c
==================================================================
--- appfsd.c
+++ /dev/null
@@ -1,910 +0,0 @@
-#define FUSE_USE_VERSION 26
-
-#include <sys/types.h>
-#include <sqlite3.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <fuse.h>
-#include <tcl.h>
-
-#define APPFS_CACHEDIR "/var/cache/appfs"
-
-#define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }
-
-static pthread_key_t interpKey;
-
-struct appfs_thread_data {
-	sqlite3 *db;
-	const char *cachedir;
-	time_t boottime;
-};
-
-struct appfs_thread_data globalThread;
-
-typedef enum {
-	APPFS_PATHTYPE_INVALID,
-	APPFS_PATHTYPE_FILE,
-	APPFS_PATHTYPE_DIRECTORY,
-	APPFS_PATHTYPE_SYMLINK
-} appfs_pathtype_t;
-
-struct appfs_children {
-	struct appfs_children *_next;
-	int counter;
-
-	char name[256];
-};
-
-struct appfs_pathinfo {
-	appfs_pathtype_t type;
-	time_t time;
-	char hostname[256];
-	union {
-		struct {
-			int childcount;
-		} dir;
-		struct {
-			int executable;
-			off_t size;
-			char sha1[41];
-		} file;
-		struct {
-			off_t size;
-			char source[256];
-		} symlink;
-	} typeinfo;
-};
-
-struct appfs_sqlite3_query_cb_handle {
-	struct appfs_children *head;
-	int argc;
-	const char *fmt;
-};
-
-static Tcl_Interp *appfs_create_TclInterp(const char *cachedir) {
-	Tcl_Interp *interp;
-	int tcl_ret;
-
-	interp = Tcl_CreateInterp();
-	if (interp == NULL) {
-		fprintf(stderr, "Unable to create Tcl Interpreter.  Aborting.\n");
-
-		return(NULL);
-	}
-
-	tcl_ret = Tcl_Init(interp);
-	if (tcl_ret != TCL_OK) {
-		fprintf(stderr, "Unable to initialize Tcl.  Aborting.\n");
-
-		return(NULL);
-	}
-
-	tcl_ret = Tcl_Eval(interp, ""
-#include "appfsd.tcl.h"
-	"");
-	if (tcl_ret != TCL_OK) {
-		fprintf(stderr, "Unable to initialize Tcl AppFS script.  Aborting.\n");
-		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
-
-		return(NULL);
-	}
-
-	if (Tcl_SetVar(interp, "::appfs::cachedir", cachedir, TCL_GLOBAL_ONLY) == NULL) {
-		fprintf(stderr, "Unable to set cache directory.  This should never fail.\n");
-
-		return(NULL);
-	}
-
-	tcl_ret = Tcl_Eval(interp, "::appfs::init");
-	if (tcl_ret != TCL_OK) {
-		fprintf(stderr, "Unable to initialize Tcl AppFS script (::appfs::init).  Aborting.\n");
-		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
-
-		return(NULL);
-	}
-
-	return(interp);
-}
-
-static int appfs_Tcl_Eval(Tcl_Interp *interp, int objc, const char *cmd, ...) {
-	Tcl_Obj **objv;
-	const char *arg;
-	va_list argp;
-	int retval;
-	int i;
-
-	objv = (void *) ckalloc(sizeof(*objv) * objc);
-	objv[0] = Tcl_NewStringObj(cmd, -1);
-	Tcl_IncrRefCount(objv[0]);
-
-	va_start(argp, cmd);
-	for (i = 1; i < objc; i++) {
-		arg = va_arg(argp, const char *);
-		objv[i] = Tcl_NewStringObj(arg, -1);
-		Tcl_IncrRefCount(objv[i]);
-	}
-	va_end(argp);
-
-	retval = Tcl_EvalObjv(interp, objc, objv, 0);
-
-	for (i = 0; i < objc; i++) {
-		Tcl_DecrRefCount(objv[i]);
-	}
-
-	ckfree((void *) objv);
-
-	if (retval != TCL_OK) {
-		APPFS_DEBUG("Tcl command failed, ::errorInfo contains: %s\n", Tcl_GetVar(interp, "::errorInfo", 0));
-	}
-
-	return(retval);
-}
-
-static void appfs_update_index(const char *hostname) {
-	Tcl_Interp *interp;
-	int tcl_ret;
-
-	APPFS_DEBUG("Enter: hostname = %s", hostname);
-
-	interp = pthread_getspecific(interpKey);
-	if (interp == NULL) {
-		interp = appfs_create_TclInterp(globalThread.cachedir);
-
-		pthread_setspecific(interpKey, interp);
-	}
-
-	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getindex", hostname);
-	if (tcl_ret != TCL_OK) {
-		APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(interp));
-
-		return;
-	}
-
-	return;
-}
-
-static const char *appfs_getfile(const char *hostname, const char *sha1) {
-	Tcl_Interp *interp;
-	char *retval;
-	int tcl_ret;
-
-	interp = pthread_getspecific(interpKey);
-	if (interp == NULL) {
-		interp = appfs_create_TclInterp(globalThread.cachedir);
-
-		pthread_setspecific(interpKey, interp);
-	}
-
-	tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::download", hostname, sha1);
-	if (tcl_ret != TCL_OK) {
-		APPFS_DEBUG("Call to ::appfs::download failed: %s", Tcl_GetStringResult(interp));
-
-		return(NULL);
-	}
-
-	retval = strdup(Tcl_GetStringResult(interp));
-
-	return(retval);
-}
-
-static void appfs_update_manifest(const char *hostname, const char *sha1) {
-	Tcl_Interp *interp;
-	int tcl_ret;
-
-	interp = pthread_getspecific(interpKey);
-	if (interp == NULL) {
-		interp = appfs_create_TclInterp(globalThread.cachedir);
-
-		pthread_setspecific(interpKey, interp);
-	}
-
-	tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::getpkgmanifest", hostname, sha1);
-	if (tcl_ret != TCL_OK) {
-		APPFS_DEBUG("Call to ::appfs::getpkgmanifest failed: %s", Tcl_GetStringResult(interp));
-
-		return;
-	}
-
-	return;
-}
-
-#define appfs_free_list_type(id, type) static void appfs_free_list_ ## id(type *head) { \
-	type *obj, *next; \
-	for (obj = head; obj; obj = next) { \
-		next = obj->_next; \
-		ckfree((void *) obj); \
-	} \
-}
-
-appfs_free_list_type(children, struct appfs_children)
-
-static int appfs_getchildren_cb(void *_head, int columns, char **values, char **names) {
-	struct appfs_children **head_p, *obj;
-
-	head_p = _head;
-
-	obj = (void *) ckalloc(sizeof(*obj));
-
-	snprintf(obj->name, sizeof(obj->name), "%s", values[0]);
-
-	if (*head_p == NULL) {
-		obj->counter = 0;
-	} else {
-		obj->counter = (*head_p)->counter + 1;
-	}
-
-	obj->_next = *head_p;
-	*head_p = obj;
-
-	return(0);
-	
-}
-
-static struct appfs_children *appfs_getchildren(const char *hostname, const char *package_hash, const char *path, int *children_count_p) {
-	struct appfs_children *head = NULL;
-	char *sql;
-	int sqlite_ret;
-
-	if (children_count_p == NULL) {
-		return(NULL);
-	}
-
-	appfs_update_index(hostname);
-	appfs_update_manifest(hostname, package_hash);
-
-	sql = sqlite3_mprintf("SELECT file_name FROM files WHERE package_sha1 = %Q AND file_directory = %Q;", package_hash, path);
-	if (sql == NULL) {
-		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
-
-		return(NULL);
-	}
-
-	APPFS_DEBUG("SQL: %s", sql);
-	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getchildren_cb, &head, NULL);
-	sqlite3_free(sql);
-
-	if (sqlite_ret != SQLITE_OK) {
-		APPFS_DEBUG("Call to sqlite3_exec failed.");
-
-		return(NULL);
-	}
-
-	if (head != NULL) {
-		*children_count_p = head->counter + 1;
-	}
-
-	return(head);
-}
-
-static int appfs_sqlite3_query_cb(void *_cb_handle, int columns, char **values, char **names) {
-	struct appfs_sqlite3_query_cb_handle *cb_handle;
-	struct appfs_children *obj;
-
-	cb_handle = _cb_handle;
-
-	obj = (void *) ckalloc(sizeof(*obj));
-
-	switch (cb_handle->argc) {
-		case 1:
-			snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0]);
-			break;
-		case 2:
-			snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0], values[1]);
-			break;
-		case 3:
-			snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0], values[1], values[2]);
-			break;
-		case 4:
-			snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0], values[1], values[2], values[3]);
-			break;
-	}
-
-	if (cb_handle->head == NULL) {
-		obj->counter = 0;
-	} else {
-		obj->counter = cb_handle->head->counter + 1;
-	}
-
-	obj->_next = cb_handle->head;
-	cb_handle->head = obj;
-
-	return(0);
-}
-
-static struct appfs_children *appfs_sqlite3_query(char *sql, int argc, const char *fmt, int *results_count_p) {
-	struct appfs_sqlite3_query_cb_handle cb_handle;
-	int sqlite_ret;
-
-	if (results_count_p == NULL) {
-		return(NULL);
-	}
-
-	if (sql == NULL) {
-		APPFS_DEBUG("Call to sqlite3_mprintf probably failed.");
-
-		return(NULL);
-	}
-
-	if (fmt == NULL) {
-		fmt = "%s";
-	}
-
-	cb_handle.head = NULL;
-	cb_handle.argc = argc;
-	cb_handle.fmt  = fmt;
-
-	APPFS_DEBUG("SQL: %s", sql);
-	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_sqlite3_query_cb, &cb_handle, NULL);
-	sqlite3_free(sql);
-
-	if (sqlite_ret != SQLITE_OK) {
-		APPFS_DEBUG("Call to sqlite3_exec failed.");
-
-		return(NULL);
-	}
-
-	if (cb_handle.head != NULL) {
-		*results_count_p = cb_handle.head->counter + 1;
-	}
-
-	return(cb_handle.head);
-}
-
-static int appfs_lookup_package_hash_cb(void *_retval, int columns, char **values, char **names) {
-	char **retval = _retval;
-
-	*retval = strdup(values[0]);
-
-	return(0);
-}
-
-static char *appfs_lookup_package_hash(const char *hostname, const char *package, const char *os, const char *cpuArch, const char *version) {
-	char *sql;
-	char *retval = NULL;
-	int sqlite_ret;
-
-	appfs_update_index(hostname);
-
-	sql = sqlite3_mprintf("SELECT sha1 FROM packages WHERE hostname = %Q AND package = %Q AND os = %Q AND cpuArch = %Q AND version = %Q;",
-		hostname,
-		package,
-		os,
-		cpuArch,
-		version
-	);
-	if (sql == NULL) {
-		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
-
-		return(NULL);
-	}
-
-	APPFS_DEBUG("SQL: %s", sql);
-	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_lookup_package_hash_cb, &retval, NULL);
-	sqlite3_free(sql);
-
-	if (sqlite_ret != SQLITE_OK) {
-		APPFS_DEBUG("Call to sqlite3_exec failed.");
-
-		return(NULL);
-	}
-
-	return(retval);
-}
-
-static int appfs_getfileinfo_cb(void *_pathinfo, int columns, char **values, char **names) {
-	struct appfs_pathinfo *pathinfo = _pathinfo;
-	const char *type, *time, *source, *size, *perms, *sha1;
-
-	type = values[0];
-	time = values[1];
-	source = values[2];
-	size = values[3];
-	perms = values[4];
-	sha1 = values[5];
-
-	pathinfo->time = strtoull(time, NULL, 10);
-
-	if (strcmp(type, "file") == 0) {
-		pathinfo->type = APPFS_PATHTYPE_FILE;
-
-		if (!size) {
-			size = "0";
-		}
-
-		if (!perms) {
-			perms = "";
-		}
-
-		if (!sha1) {
-			sha1 = "";
-		}
-
-		pathinfo->typeinfo.file.size = strtoull(size, NULL, 10);
-		snprintf(pathinfo->typeinfo.file.sha1, sizeof(pathinfo->typeinfo.file.sha1), "%s", sha1);
-
-		if (strcmp(perms, "x") == 0) {
-			pathinfo->typeinfo.file.executable = 1;
-		} else {
-			pathinfo->typeinfo.file.executable = 0;
-		}
-
-		return(0);
-	}
-
-	if (strcmp(type, "directory") == 0) {
-		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
-		pathinfo->typeinfo.dir.childcount = 0;
-
-		return(0);
-	}
-
-	if (strcmp(type, "symlink") == 0) {
-		pathinfo->type = APPFS_PATHTYPE_SYMLINK;
-		pathinfo->typeinfo.dir.childcount = 0;
-
-		if (!source) {
-			source = ".BADLINK";
-		}
-
-		pathinfo->typeinfo.symlink.size = strlen(source);
-		snprintf(pathinfo->typeinfo.symlink.source, sizeof(pathinfo->typeinfo.symlink.source), "%s", source);
-
-		return(0);
-	}
-
-	return(0);
-
-	/* Until this is used, prevent the compiler from complaining */
-	source = source;
-}
-
-static int appfs_getfileinfo(const char *hostname, const char *package_hash, const char *_path, struct appfs_pathinfo *pathinfo) {
-	char *directory, *file, *path;
-	char *sql;
-	int sqlite_ret;
-
-	if (pathinfo == NULL) {
-		return(-EIO);
-	}
-
-	appfs_update_index(hostname);
-	appfs_update_manifest(hostname, package_hash);
-
-	path = strdup(_path);
-	directory = path;
-	file = strrchr(path, '/');
-	if (file == NULL) {
-		file = path;
-		directory = "";
-	} else {
-		*file = '\0';
-		file++;
-	}
-
-	sql = sqlite3_mprintf("SELECT type, time, source, size, perms, file_sha1 FROM files WHERE package_sha1 = %Q AND file_directory = %Q AND file_name = %Q;", package_hash, directory, file);
-	if (sql == NULL) {
-		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
-
-		free(path);
-
-		return(-EIO);
-	}
-
-	free(path);
-
-	pathinfo->type = APPFS_PATHTYPE_INVALID;
-
-	APPFS_DEBUG("SQL: %s", sql);
-	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getfileinfo_cb, pathinfo, NULL);
-	sqlite3_free(sql);
-
-	if (sqlite_ret != SQLITE_OK) {
-		APPFS_DEBUG("Call to sqlite3_exec failed.");
-
-		return(-EIO);
-	}
-
-	if (pathinfo->type == APPFS_PATHTYPE_INVALID) {
-		return(-ENOENT);
-	}
-
-	return(0);
-}
-
-static int appfs_get_path_info_sql(char *sql, int argc, const char *fmt, struct appfs_pathinfo *pathinfo, struct appfs_children **children) {
-	struct appfs_children *node, *dir_children, *dir_child;
-	int dir_children_count = 0;
-
-	dir_children = appfs_sqlite3_query(sql, argc, fmt, &dir_children_count);
-
-	if (dir_children == NULL || dir_children_count == 0) {
-		return(-ENOENT);
-	}
-
-	/* Request for a single hostname */
-	pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
-	pathinfo->typeinfo.dir.childcount = dir_children_count;
-	pathinfo->time = globalThread.boottime;
-
-	if (children) {
-		for (dir_child = dir_children; dir_child; dir_child = dir_child->_next) {
-			node = (void *) ckalloc(sizeof(*node));
-			node->_next = *children;
-			strcpy(node->name, dir_child->name);
-			*children = node;
-		}
-	}
-
-	appfs_free_list_children(dir_children);
-
-	return(0);
-}
-/* Get information about a path, and optionally list children */
-static int appfs_get_path_info(const char *_path, struct appfs_pathinfo *pathinfo, struct appfs_children **children) {
-	struct appfs_children *dir_children;
-	char *hostname, *packagename, *os_cpuArch, *os, *cpuArch, *version;
-	char *path, *path_s;
-	char *package_hash;
-	char *sql;
-	int files_count;
-	int fileinfo_ret, retval;
-
-	if (children) {
-		*children = NULL;
-	}
-
-	if (_path == NULL) {
-		return(-ENOENT);
-	}
-
-	if (_path[0] != '/') {
-		return(-ENOENT);
-	}
-
-	if (_path[1] == '\0') {
-		/* Request for the root directory */
-		pathinfo->hostname[0] = '\0';
-
-		sql = sqlite3_mprintf("SELECT DISTINCT hostname FROM packages;");
-
-		retval = appfs_get_path_info_sql(sql, 1, NULL, pathinfo, children);
-
-		/* The root directory always exists, even if it has no subordinates */
-		if (retval != 0) {
-			pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
-			pathinfo->typeinfo.dir.childcount = 0;
-			pathinfo->time = globalThread.boottime;
-
-			retval = 0;
-		}
-
-		return(retval);
-	}
-
-	path = strdup(_path);
-	path_s = path;
-
-	hostname = path + 1;
-	packagename = strchr(hostname, '/');
-
-	if (packagename != NULL) {
-		*packagename = '\0';
-		packagename++;
-	}
-
-	snprintf(pathinfo->hostname, sizeof(pathinfo->hostname), "%s", hostname);
-
-	if (packagename == NULL) {
-		appfs_update_index(hostname);
-
-		sql = sqlite3_mprintf("SELECT DISTINCT package FROM packages WHERE hostname = %Q;", hostname);
-
-		free(path_s);
-
-		return(appfs_get_path_info_sql(sql, 1, NULL, pathinfo, children));
-	}
-
-	os_cpuArch = strchr(packagename, '/');
-
-	if (os_cpuArch != NULL) {
-		*os_cpuArch = '\0';
-		os_cpuArch++;
-	}
-
-	if (os_cpuArch == NULL) {
-		appfs_update_index(hostname);
-
-		sql = sqlite3_mprintf("SELECT DISTINCT os, cpuArch FROM packages WHERE hostname = %Q AND package = %Q;", hostname, packagename);
-
-		free(path_s);
-
-		return(appfs_get_path_info_sql(sql, 2, "%s-%s", pathinfo, children));
-	}
-
-	version = strchr(os_cpuArch, '/');
-
-	if (version != NULL) {
-		*version = '\0';
-		version++;
-	}
-
-	os = os_cpuArch;
-	cpuArch = strchr(os_cpuArch, '-');
-	if (cpuArch) {
-		*cpuArch = '\0';
-		cpuArch++;
-	}
-
-	if (version == NULL) {
-		/* Request for version list for a package on an OS/CPU */
-		appfs_update_index(hostname);
-
-		sql = sqlite3_mprintf("SELECT DISTINCT version FROM packages WHERE hostname = %Q AND package = %Q AND os = %Q and cpuArch = %Q;", hostname, packagename, os, cpuArch);
-
-		free(path_s);
-
-		return(appfs_get_path_info_sql(sql, 1, NULL, pathinfo, children));
-	}
-
-	path = strchr(version, '/');
-	if (path == NULL) {
-		path = "";
-	} else {
-		*path = '\0';
-		path++;
-	}
-
-	/* Request for a file in a specific package */
-	APPFS_DEBUG("Requesting information for hostname = %s, package = %s, os = %s, cpuArch = %s, version = %s, path = %s", 
-		hostname, packagename, os, cpuArch, version, path
-	);
-
-	package_hash = appfs_lookup_package_hash(hostname, packagename, os, cpuArch, version);
-	if (package_hash == NULL) {
-		free(path_s);
-
-		return(-ENOENT);
-	}
-
-	APPFS_DEBUG("  ... which hash a hash of %s", package_hash);
-
-	appfs_update_manifest(hostname, package_hash);
-
-	if (strcmp(path, "") == 0) {
-		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
-		pathinfo->time = globalThread.boottime;
-	} else {
-		fileinfo_ret = appfs_getfileinfo(hostname, package_hash, path, pathinfo);
-		if (fileinfo_ret != 0) {
-			free(path_s);
-
-			return(fileinfo_ret);
-		}
-	}
-
-	if (pathinfo->type == APPFS_PATHTYPE_DIRECTORY) {
-		dir_children = appfs_getchildren(hostname, package_hash, path, &files_count);
-
-		if (dir_children != NULL) {
-			pathinfo->typeinfo.dir.childcount = files_count;
-		}
-
-		if (children) {
-			*children = dir_children;
-		}
-	}
-
-	free(path_s);
-
-	return(0);
-}
-
-static int appfs_fuse_readlink(const char *path, char *buf, size_t size) {
-	struct appfs_pathinfo pathinfo;
-	int res = 0;
-
-	APPFS_DEBUG("Enter (path = %s, ...)", path);
-
-	pathinfo.type = APPFS_PATHTYPE_INVALID;
-
-	res = appfs_get_path_info(path, &pathinfo, NULL);
-	if (res != 0) {
-		return(res);
-	}
-
-	if (pathinfo.type != APPFS_PATHTYPE_SYMLINK) {
-		return(-EINVAL);
-	}
-
-	if ((strlen(pathinfo.typeinfo.symlink.source) + 1) > size) {
-		return(-ENAMETOOLONG);
-	}
-
-	memcpy(buf, pathinfo.typeinfo.symlink.source, strlen(pathinfo.typeinfo.symlink.source) + 1);
-
-	return(0);
-}
-
-static int appfs_fuse_getattr(const char *path, struct stat *stbuf) {
-	struct appfs_pathinfo pathinfo;
-	int res = 0;
-
-	APPFS_DEBUG("Enter (path = %s, ...)", path);
-
-	pathinfo.type = APPFS_PATHTYPE_INVALID;
-
-	res = appfs_get_path_info(path, &pathinfo, NULL);
-	if (res != 0) {
-		return(res);
-	}
-
-	memset(stbuf, 0, sizeof(struct stat));
-
-	stbuf->st_mtime = pathinfo.time;
-	stbuf->st_ctime = pathinfo.time;
-	stbuf->st_atime = pathinfo.time;
-
-	switch (pathinfo.type) {
-		case APPFS_PATHTYPE_DIRECTORY:
-			stbuf->st_mode = S_IFDIR | 0555;
-			stbuf->st_nlink = 2 + pathinfo.typeinfo.dir.childcount;
-			break;
-		case APPFS_PATHTYPE_FILE:
-			if (pathinfo.typeinfo.file.executable) {
-				stbuf->st_mode = S_IFREG | 0555;
-			} else {
-				stbuf->st_mode = S_IFREG | 0444;
-			}
-
-			stbuf->st_nlink = 1;
-			stbuf->st_size = pathinfo.typeinfo.file.size;
-			break;
-		case APPFS_PATHTYPE_SYMLINK:
-			stbuf->st_mode = S_IFLNK | 0555;
-			stbuf->st_nlink = 1;
-			stbuf->st_size = pathinfo.typeinfo.symlink.size;
-			break;
-		case APPFS_PATHTYPE_INVALID:
-			res = -EIO;
-
-			break;
-	}
-
-	return res;
-}
-
-static int appfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
-	struct appfs_pathinfo pathinfo;
-	struct appfs_children *children, *child;
-	int retval;
-
-	APPFS_DEBUG("Enter (path = %s, ...)", path);
-
-	retval = appfs_get_path_info(path, &pathinfo, &children);
-	if (retval != 0) {
-		return(retval);
-	}
-
-	filler(buf, ".", NULL, 0);
-	filler(buf, "..", NULL, 0);
-
-	for (child = children; child; child = child->_next) {
-		filler(buf, child->name, NULL, 0);
-	}
-
-	appfs_free_list_children(children);
-
-	return(0);
-}
-
-static int appfs_fuse_open(const char *path, struct fuse_file_info *fi) {
-	struct appfs_pathinfo pathinfo;
-	const char *real_path;
-	int fh;
-	int gpi_ret;
-
-	APPFS_DEBUG("Enter (path = %s, ...)", path);
-
-	if ((fi->flags & 3) != O_RDONLY) {
-                return(-EACCES);
-	}
-
-	gpi_ret = appfs_get_path_info(path, &pathinfo, NULL);
-	if (gpi_ret != 0) {
-		return(gpi_ret);
-	}
-
-	if (pathinfo.type == APPFS_PATHTYPE_DIRECTORY) {
-		return(-EISDIR);
-	}
-
-	real_path = appfs_getfile(pathinfo.hostname, pathinfo.typeinfo.file.sha1);
-	if (real_path == NULL) {
-		return(-EIO);
-	}
-
-	fh = open(real_path, O_RDONLY);
-	free((void *) real_path);
-	if (fh < 0) {
-		return(-EIO);
-	}
-
-	fi->fh = fh;
-
-	return(0);
-}
-
-static int appfs_fuse_close(const char *path, struct fuse_file_info *fi) {
-	int close_ret;
-
-	close_ret = close(fi->fh);
-	if (close_ret != 0) {
-		return(-EIO);
-	}
-
-	return(0);
-}
-
-static int appfs_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
-	off_t lseek_ret;
-	ssize_t read_ret;
-
-	APPFS_DEBUG("Enter (path = %s, ...)", path);
-
-	lseek_ret = lseek(fi->fh, offset, SEEK_SET);
-	if (lseek_ret != offset) {
-		return(-EIO);
-	}
-
-	read_ret = read(fi->fh, buf, size);
-
-	return(read_ret);
-}
-
-static struct fuse_operations appfs_oper = {
-	.getattr   = appfs_fuse_getattr,
-	.readdir   = appfs_fuse_readdir,
-	.readlink  = appfs_fuse_readlink,
-	.open      = appfs_fuse_open,
-	.release   = appfs_fuse_close,
-	.read      = appfs_fuse_read
-};
-
-int main(int argc, char **argv) {
-	const char *cachedir = APPFS_CACHEDIR;
-	char dbfilename[1024];
-	int pthread_ret, snprintf_ret, sqlite_ret;
-
-	globalThread.cachedir = cachedir;
-	globalThread.boottime = time(NULL);
-
-	pthread_ret = pthread_key_create(&interpKey, NULL);
-	if (pthread_ret != 0) {
-		fprintf(stderr, "Unable to create TSD key for Tcl.  Aborting.\n");
-
-		return(1);
-	}
-
-	snprintf_ret = snprintf(dbfilename, sizeof(dbfilename), "%s/%s", cachedir, "cache.db");
-	if (snprintf_ret >= sizeof(dbfilename)) {
-		fprintf(stderr, "Unable to set database filename.  Aborting.\n");
-
-		return(1);
-	}
-
-	sqlite_ret = sqlite3_open(dbfilename, &globalThread.db);
-	if (sqlite_ret != SQLITE_OK) {
-		fprintf(stderr, "Unable to open database: %s\n", dbfilename);
-
-		return(1);
-	}
-
-	return(fuse_main(argc, argv, &appfs_oper, NULL));
-}
- 

DELETED appfsd.tcl
Index: appfsd.tcl
==================================================================
--- appfsd.tcl
+++ /dev/null
@@ -1,314 +0,0 @@
-#! /usr/bin/env tclsh
-
-package require http 2.7
-package require sqlite3
-
-namespace eval ::appfs {
-	variable cachedir "/tmp/appfs-cache"
-	variable ttl 3600
-	variable nttl 60
-
-	proc _hash_sep {hash {seps 4}} {
-		for {set idx 0} {$idx < $seps} {incr idx} {
-			append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/"
-		}
-		append retval "[string range $hash [expr {$idx * 2}] end]"
-
-		return $retval
-	}
-
-	proc _cachefile {url key {keyIsHash 1}} {
-		set filekey $key
-		if {$keyIsHash} {
-			set filekey [_hash_sep $filekey]
-		}
-
-		set file [file join $::appfs::cachedir $filekey]
-
-		file mkdir [file dirname $file]
-
-		if {![file exists $file]} {
-			set tmpfile "${file}.new"
-
-			set fd [open $tmpfile "w"]
-			fconfigure $fd -translation binary
-
-			catch {
-				set token [::http::geturl $url -channel $fd -binary true]
-			}
-
-			if {[info exists token]} {
-				set ncode [::http::ncode $token]
-				::http::reset $token
-			} else {
-				set ncode "900"
-			}
-
-			close $fd
-
-			if {$keyIsHash} {
-				catch {
-					set hash [string tolower [exec openssl sha1 $tmpfile]]
-					regsub {.*= *} $hash {} hash
-				}
-			} else {
-				set hash $key
-			}
-
-			if {$ncode == "200" && $hash == $key} {
-				file rename -force -- $tmpfile $file
-			} else {
-				file delete -force -- $tmpfile
-			}
-		}
-
-		return $file
-	}
-
-
-	proc _isHash {value} {
-		set value [string tolower $value]
-
-		if {[string length $value] != 40} {
-			return false
-		}
-
-		if {![regexp {^[0-9a-f]*$} $value]} {
-			return false
-		}
-
-		return true
-	}
-
-	proc _db {args} {
-		return [uplevel 1 [list ::appfs::db {*}$args]]
-	}
-
-	proc _normalizeOS {os} {
-		set os [string tolower [string trim $os]]
-
-		switch -- $os {
-			"linux" - "freebsd" - "openbsd" - "netbsd" {
-				return $os
-			}
-			"sunos" {
-				return "solaris"
-			}
-		}
-
-		return -code error "Unable to normalize OS: $os"
-	}
-
-	proc _normalizeCPU {cpu} {
-		set cpu [string tolower [string trim $cpu]]
-
-		switch -glob -- $cpu {
-			"i?86" {
-				return "ix86"
-			}
-			"x86_64" {
-				return $cpu
-			}
-		}
-
-		return -code error "Unable to normalize CPU: $cpu"
-	}
-
-	proc init {} {
-		if {[info exists ::appfs::init_called]} {
-			return
-		}
-
-		set ::appfs::init_called 1
-
-		if {![info exists ::appfs::db]} {
-			file mkdir $::appfs::cachedir
-
-			sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db]
-		}
-
-		_db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);}
-		_db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);}
-		_db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);}
-	}
-
-	proc download {hostname hash {method sha1}} {
-		set url "http://$hostname/appfs/$method/$hash"
-		set file [_cachefile $url $hash]
-
-		if {![file exists $file]} {
-			return -code error "Unable to fetch"
-		}
-
-		return $file
-	}
-
-	proc getindex {hostname} {
-		set now [clock seconds]
-
-		set lastUpdates [_db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}]
-		if {[llength $lastUpdates] == 0} {
-			set lastUpdate 0
-			set ttl 0
-		} else {
-			set lastUpdate [lindex $lastUpdates 0]
-			set ttl [lindex $lastUpdates 1]
-		}
-
-		if {$now < ($lastUpdate + $ttl)} {
-			return COMPLETE
-		}
-
-		if {[string match "*\[/~\]*" $hostname]} {
-			return -code error "Invalid hostname"
-		}
-
-		set url "http://$hostname/appfs/index"
-
-		catch {
-			set token [::http::geturl $url]
-			if {[::http::ncode $token] == "200"} {
-				set indexhash_data [::http::data $token]
-			}
-			::http::reset $token
-			$token cleanup
-		}
-
-		if {![info exists indexhash_data]} {
-			# Cache this result for 60 seconds
-			_db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);}
-
-			return -code error "Unable to fetch $url"
-		}
-
-		set indexhash [lindex [split $indexhash_data ","] 0]
-
-		if {![_isHash $indexhash]} {
-			return -code error "Invalid hash: $indexhash"
-		}
-
-		set file [download $hostname $indexhash]
-		set fd [open $file]
-		set data [read $fd]
-		close $fd
-
-		set curr_packages [list]
-		foreach line [split $data "\n"] {
-			set line [string trim $line]
-
-			if {[string match "*/*" $line]} {
-				continue
-			}
-
-			if {$line == ""} {
-				continue
-			}
-
-			set work [split $line ","]
-
-			unset -nocomplain pkgInfo
-			set pkgInfo(package)  [lindex $work 0]
-			set pkgInfo(version)  [lindex $work 1]
-			set pkgInfo(os)       [_normalizeOS [lindex $work 2]]
-			set pkgInfo(cpuArch)  [_normalizeCPU [lindex $work 3]]
-			set pkgInfo(hash)     [string tolower [lindex $work 4]]
-			set pkgInfo(hash_type) "sha1"
-			set pkgInfo(isLatest) [expr {!![lindex $work 5]}]
-
-			if {![_isHash $pkgInfo(hash)]} {
-				continue
-			}
-
-			lappend curr_packages $pkgInfo(hash)
-
-			# Do not do any additional work if we already have this package
-			set existing_packages [_db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}]
-			if {[lsearch -exact $existing_packages $pkgInfo(package)] != -1} {
-				continue
-			}
-
-			if {$pkgInfo(isLatest)} {
-				_db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);}
-			}
-
-			_db eval {INSERT INTO packages (hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest) VALUES ($hostname, $pkgInfo(hash), $pkgInfo(package), $pkgInfo(version), $pkgInfo(os), $pkgInfo(cpuArch), $pkgInfo(isLatest), 0);}
-		}
-
-		# Look for packages that have been deleted
-		set found_packages [_db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}]
-		foreach package $found_packages {
-			set found_packages_arr($package) 1
-		}
-
-		foreach package $curr_packages {
-			unset -nocomplain found_packages_arr($package)
-		}
-
-		foreach package [array names found_packages_arr] {
-			_db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;}
-		}
-
-		_db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);}
-
-		return COMPLETE
-	}
-
-	proc getpkgmanifest {hostname package_sha1} {
-		set haveManifests [_db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}]
-		set haveManifest [lindex $haveManifests 0]
-
-		if {$haveManifest} {
-			return COMPLETE
-		}
-
-		if {![_isHash $package_sha1]} {
-			return FAIL
-		}
-
-		set file [download $hostname $package_sha1]
-		set fd [open $file]
-		set pkgdata [read $fd]
-		close $fd
-
-		_db transaction {
-			foreach line [split $pkgdata "\n"] {
-				set line [string trim $line]
-
-				if {$line == ""} {
-					continue
-				}
-
-				set work [split $line ","]
-
-				unset -nocomplain fileInfo
-				set fileInfo(type) [lindex $work 0]
-				set fileInfo(time) [lindex $work 1]
-
-				set work [lrange $work 2 end]
-				switch -- $fileInfo(type) {
-					"file" {
-						set fileInfo(size) [lindex $work 0]
-						set fileInfo(perms) [lindex $work 1]
-						set fileInfo(sha1) [lindex $work 2]
-
-						set work [lrange $work 3 end]
-					}
-					"symlink" {
-						set fileInfo(source) [lindex $work 0]
-						set work [lrange $work 1 end]
-					}
-				}
-
-				set fileInfo(name) [join $work ","]
-				set fileInfo(name) [split [string trim $fileInfo(name) "/"] "/"]
-				set fileInfo(directory) [join [lrange $fileInfo(name) 0 end-1] "/"]
-				set fileInfo(name) [lindex $fileInfo(name) end]
-
-				_db eval {INSERT INTO files (package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory) VALUES ($package_sha1, $fileInfo(type), $fileInfo(time), $fileInfo(source), $fileInfo(size), $fileInfo(perms), $fileInfo(sha1), $fileInfo(name), $fileInfo(directory) );}
-				_db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;}
-			}
-		}
-
-		return COMPLETE
-	}
-}

ADDED   build
Index: build
==================================================================
--- /dev/null
+++ build
@@ -0,0 +1,415 @@
+#! /usr/bin/env bash
+
+# Set timezone to default
+TZ=UTC
+export TZ
+
+targetmode='install'
+if [ "$1" == '--cpio' ]; then
+	targetmode='archive'
+
+	shift
+fi
+
+pkg="$(echo "$1" | sed 's@/*$@@;s@^\.*/*@@')"
+
+if [ -z "${pkg}" ]; then
+	echo "Usage: build [--cpio] <package>" 2>&1
+
+	exit 1
+fi
+
+function download() {
+	if [ ! -e "${pkgarchive}" ]; then
+		# Download
+		## Cleanup
+		rm -f src.new
+
+		## Fetch file
+		wget -O src.new "${url}" || exit 1
+
+		## Verify signature
+		src_sha256="$(openssl sha256 'src.new' | sed 's@^.*= @@')"
+		if [ "${src_sha256}" != "${sha256}" ]; then
+			echo "SHA256 mismatch:  Downloaded: ${src_sha256} != Expected: ${sha256}" >&2
+
+			exit 1
+		fi
+
+		## Move file into place
+		mv src.new "${pkgarchive}"
+	fi
+}
+
+function extract() {
+	local decompress
+
+	# Decompress archive
+	## Determine type of archive
+	case "${url}" in
+		*.tar.xz|*.tar.xz'?'*|*.txz)
+			decompress='xz'
+			;;
+		*.tar.gz|*.tar.gz'?'*|*.tgz)
+			decompress='gzip'
+			;;
+		*.tar.bz2|*.tar.bz2'?'*|*.tbz2)
+			decompress='bzip2'
+			;;
+		*.zip|*.zip'?'*)
+			decompress='unzip'
+			;;
+		*)
+			echo "Unknown compression method: ${url}" >&2
+
+			exit 1
+			;;
+	esac
+
+	## Do decompression
+	case "${decompress}" in
+		unzip)
+			unzip "${pkgarchive}" || die 'Unable to uncompress archive'
+			;;
+		*)
+			"${decompress}" -dc "${pkgarchive}" | tar -xf - || die 'Unable to uncompress archive'
+			;;
+	esac
+}
+
+function apply_patches() {
+	local patch
+
+	for patch in "${pkgdir}/patches"/*; do
+		if [ ! -e "${patch}" ]; then
+			continue
+		fi
+
+		case "${patch}" in
+			*.diff|*.patch)
+				;;
+			*)
+				continue
+				;;
+		esac
+
+		if [ -e "${patch}.sh" ]; then
+			if ! sh "${patch}.sh"; then
+				continue
+			fi
+		fi
+
+		patch -p1 < "${patch}"
+	done
+}
+
+function verifyRequiredPackages() {
+	local pkg pkgdomain pkgversion
+	local pkgdir pkgconfigdir pkgfound
+
+	for pkg in "${require[@]}"; do
+		pkgdomain=''
+		pkgversion=''
+		pkgchanges=(CFLAGS LDFLAGS PATH PKG_CONFIG_PATH)
+
+		case "${pkg}" in
+			*:*)
+				pkgchanges=($(echo "${pkg}" | cut -f 2 -d ':'))
+				pkg="$(echo "${pkg}" | cut -f 1 -d ':')"
+				;;
+		esac
+
+		case "${pkg}" in
+			*/*@*)
+				pkgdomain="$(echo "${pkg}" | cut -f 2 -d '@')"
+				pkgversion="$(echo "${pkg}" | cut -f 2 -d '/' | cut -f 1 -d '@')"
+				pkg="$(echo "${pkg}" | cut -f 1 -d '/')"
+				;;
+			*/*)
+				pkgversion="$(echo "${pkg}" | cut -f 2 -d '/')"
+				pkg="$(echo "${pkg}" | cut -f 1 -d '/')"
+				;;
+			*@*)
+				pkgdomain="$(echo "${pkg}" | cut -f 2 -d '@')"
+				pkg="$(echo "${pkg}" | cut -f 1 -d '@')"
+				;;
+		esac
+
+		if [ -z "${pkgdomain}" ]; then
+			pkgdomain="${domain}"
+		fi
+
+		pkgfound='0'
+		for pkgdir in "/opt/appfs/${pkgdomain}/${pkg}/platform"/${pkgversion:-latest} "/opt/appfs/${pkgdomain}/${pkg}/platform"/${pkgversion:-*}; do
+			pkgconfigdir="${pkgdir}/lib/pkgconfig"
+
+			if [ ! -d "${pkgdir}" ]; then
+				continue
+			fi
+
+			# If the package version was unspecified, fully resolve
+			# the directory that we found
+			if [ -z "${pkgversion}" ]; then
+				pkgdir="$(readlink -f "${pkgdir}")"
+			fi
+
+			pkgfound='1'
+
+			for pkgchange in "${pkgchanges[@]}"; do
+				case "${pkgchange}" in
+					CFLAGS)
+						CFLAGS="${CFLAGS} -isystem ${pkgdir}/include"
+						CPPFLAGS="${CPPFLAGS} -isystem ${pkgdir}/include"
+						export CFLAGS CPPFLAGS
+						;;
+					LDFLAGS)
+						LDFLAGS="${LDFLAGS} -L${pkgdir}/lib -Wl,-rpath,${pkgdir}/lib"
+						export LDFLAGS
+						;;
+					PATH)
+						PATH="${PATH}:${pkgdir}/bin"
+						export PATH
+						;;
+					PKG_CONFIG_PATH)
+						if [ -d "${pkgconfigdir}" ]; then
+							PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${pkgconfigdir}"
+							export PKG_CONFIG_PATH
+						fi
+						;;
+				esac
+			done
+		done
+
+		if [ "${pkgfound}" = '0' ]; then
+			die "Package ${pkg}/${pkgversion:-*}@${pkgdomain} not found."
+		fi
+	done
+}
+
+function verifyPrerequisites() {
+	:
+}
+
+function determineOsArch() {
+	os="$(uname -s | dd conv=lcase 2>/dev/null)"
+	arch="$(uname -m | dd conv=lcase 2>/dev/null)"
+
+	case "${arch}" in
+		i?86)
+			arch='ix86'
+			;;
+	esac
+}
+
+function determinePrefix() {
+	determineOsArch
+
+	prefixsuffix="${pkg}/${os}-${arch}/${version}"
+	prefix="/opt/appfs/${domain}/${prefixsuffix}"
+	destdir="$(pwd)/INST"
+
+	mkdir "${destdir}" || die
+}
+
+function preconfigure() {
+	:
+}
+
+function postconfigure() {
+	:
+}
+
+function configure_gcc() {
+	local glibcdir linuxheadersdir
+	local dynlinker
+	local gcc_default_headers
+
+	glibcdir="/opt/appfs/core.appfs.rkeene.org/glibc/platform/latest"
+	glibcdir="$(readlink -f "${glibcdir}")"
+
+	linuxheadersdir="/opt/appfs/core.appfs.rkeene.org/linux-headers/platform/latest"
+	linuxheadersdir="$(readlink -f "${linuxheadersdir}")"
+
+	dynlinker="$(ls "${glibcdir}"/lib/ld-linux*.so.* | tail -n 1)"
+
+	if [ ! -f "${dynlinker}" ]; then
+		die 'glibc directory is not available (appfs running/working?)'
+	fi
+
+	gcc_default_headers="$(echo '' | ${CPP:-cpp} -v 2>&1 | sed '/^End of search list/,$ d;0,/search starts here:$/ d' | grep '/gcc/' | sed 's@^ *@-isystem @' | tr $'\n' ' ')"
+
+	CC="${CC:-gcc} -nostdinc ${gcc_default_headers} -isystem ${glibcdir}/include"
+
+	if ! echo " ${require[*]} " | grep ' linux-headers[/@: ]' >/dev/null; then
+		CC="${CC} -isystem ${linuxheadersdir}/include"
+	fi
+
+	BUILD_CC="${CC}"
+	HOST_CC="${CC}"
+
+	LDFLAGS="${LDFLAGS} -L${glibcdir}/lib -Wl,--rpath,${glibcdir}/lib -Wl,--dynamic-linker,${dynlinker}"
+	if [ -z "${appfs_build_no_default_symver}" ]; then
+		LDFLAGS="${LDFLAGS} -Wl,--default-symver"
+	fi
+
+	PKG_CONFIG_LIBDIR="${glibcdir}/lib/pkgconfig"
+
+	export CC BUILD_CC HOST_CC LDFLAGS PKG_CONFIG_LIBDIR
+}
+
+function configure() {
+	configure_gcc
+	./configure --prefix="${prefix}" --sysconfdir="${prefix}/etc" --libdir="${prefix}/lib" --localstatedir=/var "${configure_extra[@]}"
+}
+
+function prebuild() {
+	:
+}
+
+function postbuild() {
+	:
+}
+
+function build() {
+	grep "DESTDIR" Makefile >/dev/null || die "Don't know how to build this software"
+
+	make
+}
+
+function preinstall() {
+	:
+}
+
+function postinstall() {
+	:
+}
+
+function install() {
+	make install DESTDIR="${destdir}"
+}
+
+function cleanup() {
+	cd "${workdir}" || exit 1
+	cd .. || exit 1
+	rm -rf "${workdir}"
+}
+
+function die() {
+	local message
+
+	message="$1"
+
+	if [ -n "${message}" ]; then
+		echo "error: ${message}" >&2
+	fi
+
+	exit 1
+}
+
+scriptdir="$(cd "$(dirname "$(which "$0")")" && pwd)"
+if [ -z "${scriptdir}" ]; then
+	echo "Unable to locate script directory" >&2
+
+	exit 1
+fi
+
+cd "${scriptdir}" || exit 1
+
+if [ -f 'build.conf' ]; then
+	. 'build.conf'
+fi
+
+if [ -d "pkgs/${pkg}" ]; then
+	pkgdir="$(pwd)/pkgs/${pkg}"
+	pkgfile="${pkgdir}/info"
+else
+	pkgfile="$(pwd)/pkgs/${pkg}"
+	pkgdir="${pkgfile}"
+fi
+
+if [ ! -e "${pkgfile}" ]; then
+	echo "Invalid package." >&2
+
+	exit 1
+fi
+
+pkgdate="$(for artifact in $(find "${pkgdir}" -type f -print0 | xargs -n 1 -0 fossil finfo --limit 1 --width 0 2>/dev/null | grep '^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ' | sed 's@^[^ ]* \[@@;s@\].*@@' | sort -u); do fossil info "${artifact}" | awk '/^uuid:/{ print $3 $4 }' | sed 's@[-:]@@g;s@..$@\.&@'; done | sort -n | tail -n 1)"
+if [ -z "${pkgdate}" ]; then
+	pkgdate="$(find "${pkgdir}" -type f -printf '%TY%Tm%Td%TH%TM.%TS\n' 2>/dev/null | cut -f 1-2 -d '.' | sort -n | tail -n 1)"
+fi
+
+. "${pkgfile}"
+
+archivedir="$(pwd)/ARCHIVE"
+workdir="workdir-$$${RANDOM}${RANDOM}${RANDOM}"
+pkgarchive="${archivedir}/${pkg}-${version}"
+mkdir "${archivedir}" >/dev/null 2>/dev/null
+mkdir "${workdir}" || exit 1
+cd "${workdir}" || exit 1
+workdir="$(pwd)"
+
+# Download
+download
+
+# Extract
+extract
+
+# If we just have one directory, use that directory
+dir="$(echo *)"
+if [ -e "${dir}" ]; then
+	mv "${dir}"/* .
+fi
+
+# Verify pre-requisites are met
+verifyRequiredPackages || die 'Required packages missing'
+verifyPrerequisites || die 'Prerequisities failed'
+
+# Start logging
+set -x
+
+# Determine properties
+determinePrefix
+
+# Apply patches
+apply_patches
+
+# Start the build
+preconfigure || die 'preconfigure failed'
+configure || die 'configure failed'
+postconfigure || die 'postconfigure failed'
+
+prebuild || die 'prebuild failed'
+build || die 'build failed'
+postbuild || die 'postbuild failed'
+
+preinstall || die 'preinstall failed'
+install || die 'install failed'
+postinstall || die 'postinstall failed'
+
+(
+	case "${targetmode}" in
+		install)
+			appdir="$(appfsinstalldir)/${prefixsuffix}"
+			mkdir -p "${appdir}"
+
+			cd "${destdir}/${prefix}" || exit 1
+
+			cp -rp * "${appdir}"
+			find "${appdir}" -print0 | xargs -0 touch -t "${pkgdate}"
+			find "${appdir}" -print0 | xargs -0 touch -m -t "${pkgdate}"
+			find "${appdir}" -print0 | xargs -0 touch -a -t "${pkgdate}"
+			;;
+		archive)
+			archivefile="${scriptdir}/${pkg}-${version}-$(echo "${pkgdate}" | sed 's@\.@@g')-${os}-${arch}-${domain}.cpio"
+			cd "${destdir}/${prefix}/../../.." || exit 1
+			find "${prefixsuffix}" -print0 | xargs -0 touch -t "${pkgdate}"
+			find "${prefixsuffix}" -print0 | xargs -0 touch -m -t "${pkgdate}"
+			find "${prefixsuffix}" -print0 | xargs -0 touch -a -t "${pkgdate}"
+			find "${prefixsuffix}" | sort | cpio --owner 0:0 -H newc -o > "${archivefile}"
+			;;
+	esac
+) || die 'final installation failed'
+
+cleanup
+
+exit 0

ADDED   build.conf
Index: build.conf
==================================================================
--- /dev/null
+++ build.conf
@@ -0,0 +1,14 @@
+domain=rkeene.org
+function appfsinstalldir() {
+	case "${domain}" in
+		rkeene.org)
+			echo "/web/rkeene/appfs/applications"
+			;;
+		appfs.rkeene.org)
+			echo "/web/customers/appfs.rkeene.org/appfs/applications"
+			;;
+		core.appfs.rkeene.org)
+			echo "/web/static/core.appfs.rkeene.org/appfs/applications"
+			;;
+	esac
+}

ADDED   pkgs/acl
Index: pkgs/acl
==================================================================
--- /dev/null
+++ pkgs/acl
@@ -0,0 +1,21 @@
+#! /usr/bin/env bash
+
+version='2.2.52'
+url="http://download.savannah.gnu.org/releases/acl/acl-${version}.src.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='179074bb0580c06c4b4137be4c5a92a701583277967acdb5546043c7874e0d23'
+require=(attr)
+
+function prebuild() {
+	echo "# Ignore: DESTDIR" >> Makefile
+}
+
+function install() {
+	make install install-dev install-lib DESTDIR="${destdir}"
+}
+
+function postinstall() {
+	rm -f "${destdir}/${prefix}/libexec/libacl.so"
+	mv "${destdir}/${prefix}"/libexec/* "${destdir}/${prefix}/lib/"
+	rm -rf "${destdir}/${prefix}/libexec"
+}

ADDED   pkgs/attr/info
Index: pkgs/attr/info
==================================================================
--- /dev/null
+++ pkgs/attr/info
@@ -0,0 +1,14 @@
+#! /usr/bin/env bash
+
+version='2.4.47'
+url="http://download.savannah.gnu.org/releases/attr/attr-${version}.src.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='25772f653ac5b2e3ceeb89df50e4688891e21f723c460636548971652af0a859'
+
+function prebuild() {
+	echo "# Ignore: DESTDIR" >> Makefile
+}
+
+function install() {
+	make install install-dev install-lib DESTDIR="${destdir}"
+}

ADDED   pkgs/attr/patches/attr.destdir.diff
Index: pkgs/attr/patches/attr.destdir.diff
==================================================================
--- /dev/null
+++ pkgs/attr/patches/attr.destdir.diff
@@ -0,0 +1,36 @@
+--- ./include/builddefs.in.orig	2011-04-08 11:56:59.000000000 -0500
++++ ./include/builddefs.in	2011-04-18 13:27:35.000000000 -0500
+@@ -40,14 +40,14 @@
+ PKG_VERSION	= @pkg_version@
+ PKG_PLATFORM	= @pkg_platform@
+ PKG_DISTRIBUTION= @pkg_distribution@
+-PKG_BIN_DIR	= @bindir@
+-PKG_SBIN_DIR	= @sbindir@
+-PKG_LIB_DIR	= @libdir@@libdirsuffix@
+-PKG_DEVLIB_DIR	= @libexecdir@@libdirsuffix@
+-PKG_INC_DIR	= @includedir@/attr
+-PKG_MAN_DIR	= @mandir@
+-PKG_DOC_DIR	= @datadir@/doc/@pkg_name@
+-PKG_LOCALE_DIR	= @datadir@/locale
++PKG_BIN_DIR	= $(DESTDIR)@bindir@
++PKG_SBIN_DIR	= $(DESTDIR)@sbindir@
++PKG_LIB_DIR	= $(DESTDIR)@libdir@@libdirsuffix@
++PKG_DEVLIB_DIR	= $(DESTDIR)@libexecdir@@libdirsuffix@
++PKG_INC_DIR	= $(DESTDIR)@includedir@/attr
++PKG_MAN_DIR	= $(DESTDIR)@mandir@
++PKG_DOC_DIR	= $(DESTDIR)@datadir@/doc/@pkg_name@
++PKG_LOCALE_DIR	= $(DESTDIR)@datadir@/locale
+ 
+ CC		= @cc@
+ AWK		= @awk@
+--- ./include/buildmacros.orig	2011-04-08 11:56:59.000000000 -0500
++++ ./include/buildmacros	2011-04-18 13:28:23.000000000 -0500
+@@ -40,7 +40,7 @@
+ 	  $(LFILES:.l=.o) \
+ 	  $(YFILES:%.y=%.tab.o)
+ 
+-INSTALL	= $(TOPDIR)/include/install-sh -o $(PKG_USER) -g $(PKG_GROUP)
++INSTALL	= $(TOPDIR)/include/install-sh
+ 
+ SHELL = /bin/sh
+ IMAGES_DIR = $(TOPDIR)/all-images

ADDED   pkgs/bash
Index: pkgs/bash
==================================================================
--- /dev/null
+++ pkgs/bash
@@ -0,0 +1,7 @@
+#! /usr/bin/env bash
+
+version='4.3.30'
+url="http://ftp.gnu.org/gnu/bash/bash-${version}.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='317881019bbf2262fb814b7dd8e40632d13c3608d2f237800a8828fbb8a640dd'
+require=(zlib readline ncurses)

ADDED   pkgs/binutils
Index: pkgs/binutils
==================================================================
--- /dev/null
+++ pkgs/binutils
@@ -0,0 +1,7 @@
+#! /usr/bin/env bash
+
+version=2.24
+url="http://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='4930b2886309112c00a279483eaef2f0f8e1b1b62010e0239c16b22af7c346d4'
+require=(zlib)

ADDED   pkgs/coreutils
Index: pkgs/coreutils
==================================================================
--- /dev/null
+++ pkgs/coreutils
@@ -0,0 +1,13 @@
+#! /usr/bin/env bash
+
+version='8.23'
+url="http://ftp.gnu.org/gnu/coreutils/coreutils-${version}.tar.xz"
+domain=core.appfs.rkeene.org
+sha256='ec43ca5bcfc62242accb46b7f121f6b684ee21ecd7d075059bf650ff9e37b82d'
+require=(attr acl libcap)
+configure_extra=(--without-gmp)
+
+function preconfigure() {
+	DEFAULT_POSIX2_VERSION=199209
+	export DEFAULT_POSIX2_VERSION
+}

ADDED   pkgs/gcc
Index: pkgs/gcc
==================================================================
--- /dev/null
+++ pkgs/gcc
@@ -0,0 +1,8 @@
+#! /usr/bin/env bash
+
+version=4.9.2
+url="http://ftp.gnu.org/gnu/gcc/gcc-${version}/gcc-${version}.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='3e573826ec8b0d62d47821408fbc58721cd020df3e594cd492508de487a43b5e'
+require=(zlib binutils:PATH linux-headers/2.6.32.63)
+configure_extra=(--disable-multilib)

ADDED   pkgs/glibc
Index: pkgs/glibc
==================================================================
--- /dev/null
+++ pkgs/glibc
@@ -0,0 +1,15 @@
+#! /usr/bin/env bash
+
+version=2.20
+url="http://ftp.gnu.org/gnu/glibc/glibc-${version}.tar.xz"
+domain=core.appfs.rkeene.org
+sha256='f84b6d42aecc288d593c397b0a3d02260a33ee686bce0c634eb9b32798f36ba5'
+
+function configure() {
+	mkdir BUILD
+	cd BUILD
+
+	../configure --prefix="${prefix}" --sysconfdir=/etc --localstatedir=/var || return 1
+
+	echo '# Ignore: DESTDIR' >> Makefile
+}

ADDED   pkgs/gmp
Index: pkgs/gmp
==================================================================
--- /dev/null
+++ pkgs/gmp
@@ -0,0 +1,10 @@
+#! /usr/bin/env bash
+
+version='6.0.0a'
+url="http://ftp.gnu.org/gnu/gmp/gmp-6.0.0a.tar.xz"
+domain=core.appfs.rkeene.org
+sha256='9156d32edac6955bc53b0218f5f3763facb890b73a835d5e1b901dcf8eb8b764'
+
+function postinstall() {
+	rm -f "${destdir}/${prefix}/share/info/dir"
+}

ADDED   pkgs/libcap
Index: pkgs/libcap
==================================================================
--- /dev/null
+++ pkgs/libcap
@@ -0,0 +1,19 @@
+#! /usr/bin/env bash
+
+version='2.24'
+url="http://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-${version}.tar.xz"
+domain=core.appfs.rkeene.org
+sha256='cee4568f78dc851d726fc93f25f4ed91cc223b1fe8259daa4a77158d174e6c65'
+require=(attr)
+
+function configure() {
+	:
+}
+
+function build() {
+	make prefix="${prefix}" lib='lib'
+}
+
+function install() {
+	make prefix="${prefix}" lib='lib' DESTDIR="${destdir}" RAISE_SETFCAP='no' install 
+}

ADDED   pkgs/linux-headers
Index: pkgs/linux-headers
==================================================================
--- /dev/null
+++ pkgs/linux-headers
@@ -0,0 +1,18 @@
+#! /usr/bin/env bash
+
+version='3.17.2'
+url="https://www.kernel.org/pub/linux/kernel/v3.x/linux-${version}.tar.xz"
+domain=core.appfs.rkeene.org
+sha256='f9221dc0878955318ab57b8bb5ea1f26cbbaa39359c91d0f6c8de74d68aea796'
+
+function configure() {
+	:
+}
+
+function build() {
+	make defconfig || die
+}
+
+function install() {
+	make INSTALL_HDR_PATH="${destdir}/${prefix}" headers_install || die
+}

ADDED   pkgs/mpfr
Index: pkgs/mpfr
==================================================================
--- /dev/null
+++ pkgs/mpfr
@@ -0,0 +1,7 @@
+#! /usr/bin/env bash
+
+version='3.1.2'
+url="http://ftp.gnu.org/gnu/mpfr/mpfr-${version}.tar.xz"
+domain=core.appfs.rkeene.org
+sha256='399d0f47ef6608cc01d29ed1b99c7faff36d9994c45f36f41ba250147100453b'
+require=(gmp)

ADDED   pkgs/ncurses
Index: pkgs/ncurses
==================================================================
--- /dev/null
+++ pkgs/ncurses
@@ -0,0 +1,20 @@
+#! /usr/bin/env bash
+
+version='5.9'
+url="http://ftp.gnu.org/gnu/ncurses/ncurses-${version}.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='9046298fb440324c9d4135ecea7879ffed8546dd1b58e59430ea07a4633f563b'
+configure_extra=(--with-termlib --with-ticlib --with-shared)
+
+function postinstall() {
+	# Create libtermcap compatability
+	## Create libtermcap.so shared object
+	cat << \_EOF_ > "${destdir}/${prefix}/lib/libtermcap.so"
+GROUP( libtinfo.so )
+_EOF_
+
+	## Create termcap.h header
+	cat << \_EOF_ > "${destdir}/${prefix}/include/termcap.h"
+#include <ncurses/termcap.h>
+_EOF_
+}

ADDED   pkgs/readline
Index: pkgs/readline
==================================================================
--- /dev/null
+++ pkgs/readline
@@ -0,0 +1,6 @@
+#! /usr/bin/env bash
+
+version=6.3
+url="https://ftp.gnu.org/gnu/readline/readline-${version}.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='56ba6071b9462f980c5a72ab0023893b65ba6debb4eeb475d7a563dc65cafd43'

ADDED   pkgs/tcl
Index: pkgs/tcl
==================================================================
--- /dev/null
+++ pkgs/tcl
@@ -0,0 +1,15 @@
+#! /usr/bin/env bash
+
+version='8.6.3'
+url="http://sourceforge.net/projects/tcl/files/Tcl/${version}/tcl${version}-src.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='6ce0778de0d50daaa9c345d7c1fd1288fb658f674028812e7eeee992e3051005'
+require=(zlib)
+
+function preconfigure() {
+	cd unix
+}
+
+function postinstall() {
+	cd ..
+}

ADDED   pkgs/zlib
Index: pkgs/zlib
==================================================================
--- /dev/null
+++ pkgs/zlib
@@ -0,0 +1,6 @@
+#! /usr/bin/env bash
+
+version=1.2.8
+url="http://zlib.net/zlib-${version}.tar.gz"
+domain=core.appfs.rkeene.org
+sha256='36658cb768a54c1d4dec43c3116c27ed893e88b02ecfcb44f2166f9c0b7f2a0d'