From d04baabafa2ebbad92741d1f87e6ff32999f894a Mon Sep 17 00:00:00 2001
From: Judd Vinet <judd@archlinux.org>
Date: Tue, 15 Mar 2005 01:51:43 +0000
Subject: Initial revision

---
 lib/libalpm/Makefile    |   39 ++
 lib/libalpm/add.c       |  580 ++++++++++++++++++
 lib/libalpm/add.h       |   34 +
 lib/libalpm/alpm.c      |  577 +++++++++++++++++
 lib/libalpm/alpm.h      |  330 ++++++++++
 lib/libalpm/backup.c    |   63 ++
 lib/libalpm/backup.h    |   30 +
 lib/libalpm/cache.c     |  203 ++++++
 lib/libalpm/cache.h     |   42 ++
 lib/libalpm/db.c        |  651 ++++++++++++++++++++
 lib/libalpm/db.h        |   60 ++
 lib/libalpm/deps.c      |  685 +++++++++++++++++++++
 lib/libalpm/deps.h      |   35 ++
 lib/libalpm/error.c     |   90 +++
 lib/libalpm/error.h     |   30 +
 lib/libalpm/group.c     |   67 ++
 lib/libalpm/group.h     |   48 ++
 lib/libalpm/handle.c    |  229 +++++++
 lib/libalpm/handle.h    |   63 ++
 lib/libalpm/list.c      |  210 +++++++
 lib/libalpm/list.h      |   50 ++
 lib/libalpm/log.c       |   52 ++
 lib/libalpm/log.h       |   32 +
 lib/libalpm/md5.c       |  338 ++++++++++
 lib/libalpm/md5.h       |   51 ++
 lib/libalpm/md5driver.c |   81 +++
 lib/libalpm/package.c   |  342 +++++++++++
 lib/libalpm/package.h   |   78 +++
 lib/libalpm/provide.c   |   53 ++
 lib/libalpm/provide.h   |   33 +
 lib/libalpm/remove.c    |  259 ++++++++
 lib/libalpm/remove.h    |   34 +
 lib/libalpm/rpmvercmp.c |  237 +++++++
 lib/libalpm/rpmvercmp.h |   32 +
 lib/libalpm/sync.c      |  248 ++++++++
 lib/libalpm/sync.h      |   47 ++
 lib/libalpm/trans.c     |  187 ++++++
 lib/libalpm/trans.h     |   54 ++
 lib/libalpm/util.c      |  401 ++++++++++++
 lib/libalpm/util.h      |   57 ++
 lib/libftp/Makefile     |   68 ++
 lib/libftp/ftplib.c     | 1569 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/libftp/ftplib.h     |  131 ++++
 43 files changed, 8500 insertions(+)
 create mode 100644 lib/libalpm/Makefile
 create mode 100644 lib/libalpm/add.c
 create mode 100644 lib/libalpm/add.h
 create mode 100644 lib/libalpm/alpm.c
 create mode 100644 lib/libalpm/alpm.h
 create mode 100644 lib/libalpm/backup.c
 create mode 100644 lib/libalpm/backup.h
 create mode 100644 lib/libalpm/cache.c
 create mode 100644 lib/libalpm/cache.h
 create mode 100644 lib/libalpm/db.c
 create mode 100644 lib/libalpm/db.h
 create mode 100644 lib/libalpm/deps.c
 create mode 100644 lib/libalpm/deps.h
 create mode 100644 lib/libalpm/error.c
 create mode 100644 lib/libalpm/error.h
 create mode 100644 lib/libalpm/group.c
 create mode 100644 lib/libalpm/group.h
 create mode 100644 lib/libalpm/handle.c
 create mode 100644 lib/libalpm/handle.h
 create mode 100644 lib/libalpm/list.c
 create mode 100644 lib/libalpm/list.h
 create mode 100644 lib/libalpm/log.c
 create mode 100644 lib/libalpm/log.h
 create mode 100644 lib/libalpm/md5.c
 create mode 100644 lib/libalpm/md5.h
 create mode 100644 lib/libalpm/md5driver.c
 create mode 100644 lib/libalpm/package.c
 create mode 100644 lib/libalpm/package.h
 create mode 100644 lib/libalpm/provide.c
 create mode 100644 lib/libalpm/provide.h
 create mode 100644 lib/libalpm/remove.c
 create mode 100644 lib/libalpm/remove.h
 create mode 100644 lib/libalpm/rpmvercmp.c
 create mode 100644 lib/libalpm/rpmvercmp.h
 create mode 100644 lib/libalpm/sync.c
 create mode 100644 lib/libalpm/sync.h
 create mode 100644 lib/libalpm/trans.c
 create mode 100644 lib/libalpm/trans.h
 create mode 100644 lib/libalpm/util.c
 create mode 100644 lib/libalpm/util.h
 create mode 100644 lib/libftp/Makefile
 create mode 100644 lib/libftp/ftplib.c
 create mode 100644 lib/libftp/ftplib.h

(limited to 'lib')

diff --git a/lib/libalpm/Makefile b/lib/libalpm/Makefile
new file mode 100644
index 00000000..9a517b3b
--- /dev/null
+++ b/lib/libalpm/Makefile
@@ -0,0 +1,39 @@
+
+CXX=gcc
+CFLAGS=-g -Wall -pedantic -D_GNU_SOURCE -I. -I../..
+AR=ar rc
+RAN=ranlib
+
+OBJS=md5driver.o \
+     md5.o \
+     util.o \
+     list.o \
+     log.o \
+     error.o \
+     package.o \
+     group.o \
+     db.o \
+     cache.o \
+     deps.o \
+     provide.o \
+     rpmvercmp.o \
+     backup.o \
+     trans.o \
+     add.o \
+     remove.o \
+     sync.o \
+     handle.o \
+     alpm.o
+
+all: libalpm.a
+
+%.o: %.c %.h
+	$(CXX) -c $(CFLAGS) -o $@ $<
+
+libalpm.a: $(OBJS) alpm.h
+	$(AR) $@ $(OBJS)
+	$(RAN) $@
+
+clean:
+	rm -f *.o *~ core
+	rm -f libalpm.a
diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c
new file mode 100644
index 00000000..2c883f10
--- /dev/null
+++ b/lib/libalpm/add.c
@@ -0,0 +1,580 @@
+/*
+ *  add.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <zlib.h>
+#include <libtar.h>
+/* pacman */
+#include "util.h"
+#include "error.h"
+#include "list.h"
+#include "cache.h"
+#include "rpmvercmp.h"
+#include "md5.h"
+#include "log.h"
+#include "backup.h"
+#include "package.h"
+#include "db.h"
+#include "provide.h"
+#include "trans.h"
+#include "deps.h"
+#include "add.h"
+#include "remove.h"
+#include "handle.h"
+
+extern pmhandle_t *handle;
+
+int add_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name)
+{
+	pmpkg_t *info, *dummy;
+	PMList *j;
+
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1));
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(name != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	/* ORE
+	load_pkg should be done only if pkg has to be added to the transaction */
+
+	_alpm_log(PM_LOG_FLOW2, "reading %s...", name);
+	info = pkg_load(name);
+	if(info == NULL) {
+		/* pm_errno is already set by pkg_load() */
+		return(-1);
+	}
+
+	/* no additional hyphens in version strings */
+	if(strchr(info->version, '-') != strrchr(info->version, '-')) {
+		FREEPKG(info);
+		pm_errno = PM_ERR_INVALID_NAME;
+		return(-1);
+	}
+
+	dummy = db_get_pkgfromcache(db, info->name);
+	/* only freshen this package if it is already installed and at a lesser version */
+	if(trans->flags & PM_TRANS_FLAG_FRESHEN) {
+		if(dummy == NULL || rpmvercmp(dummy->version, info->version) >= 0) {
+			FREEPKG(info);
+			PM_RET_ERR(PM_ERR_PKG_CANT_FRESH, -1);
+		}
+	}
+	/* only install this package if it is not already installed */
+	if(trans->type != PM_TRANS_TYPE_UPGRADE) {
+		if(dummy) {
+			FREEPKG(info);
+			PM_RET_ERR(PM_ERR_PKG_INSTALLED, -1);
+		}
+	}
+
+	/* check if an older version of said package is already in transaction packages.
+	 * if so, replace it in the list */
+	/* ORE
+	we'd better do it before load_pkg. */
+	for(j = trans->packages; j; j = j->next) {
+		pmpkg_t *pkg = j->data;
+
+		if(strcmp(pkg->name, info->name) != 0) {
+			if(rpmvercmp(pkg->version, info->version) < 0) {
+				_alpm_log(PM_LOG_WARNING, "replacing older version of %s in target list", pkg->name);
+				FREEPKG(j->data);
+				j->data = info;
+			}
+		}
+	}
+
+	/* add the package to the transaction */
+	trans->packages = pm_list_add(trans->packages, info);
+
+	return(0);
+}
+
+int add_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data)
+{
+	PMList *lp;
+
+	*data = NULL;
+
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1));
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	/* ORE ???
+	No need to check deps if pacman_add was called during a sync:
+	it is already done in pacman_sync */
+
+	/* Check dependencies
+	 */
+
+	if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
+		PMList *j;
+
+		TRANS_CB(trans, PM_TRANS_CB_DEPS_START, NULL, NULL);
+
+		lp = checkdeps(db, trans->type, trans->packages);
+		if(lp != NULL) {
+			int errorout = 0;
+
+			/* look for unsatisfied dependencies */
+			_alpm_log(PM_LOG_FLOW2, "looking for unsatisfied dependencies...");
+			for(j = lp; j; j = j->next) {
+				pmdepmissing_t* miss = j->data;
+
+				if(miss->type == PM_DEP_DEPEND || miss->type == PM_DEP_REQUIRED) {
+					if(!errorout) {
+						errorout = 1;
+					}
+					if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) {
+						FREELIST(lp);
+						/* ORE, needed or not ?
+						FREELIST(*data);*/
+						PM_RET_ERR(PM_ERR_MEMORY, -1);
+					}
+					*miss = *(pmdepmissing_t*)j->data;
+					*data = pm_list_add(*data, miss);
+				}
+			}
+			if(errorout) {
+				FREELIST(lp);
+				PM_RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1);
+			}
+
+			/* no unsatisfied deps, so look for conflicts */
+			_alpm_log(PM_LOG_FLOW2, "looking for conflicts...");
+			for(j = lp; j; j = j->next) {
+				pmdepmissing_t* miss = (pmdepmissing_t *)j->data;
+				if(miss->type == PM_DEP_CONFLICT) {
+					if(!errorout) {
+						errorout = 1;
+					}
+					MALLOC(miss, sizeof(pmdepmissing_t));
+					*miss = *(pmdepmissing_t*)j->data;
+					*data = pm_list_add(*data, miss);
+				}
+			}
+			if(errorout) {
+				FREELIST(lp);
+				PM_RET_ERR(PM_ERR_CONFLICTING_DEPS, -1);
+			}
+			FREELIST(lp);
+		}
+
+		/* re-order w.r.t. dependencies */
+		_alpm_log(PM_LOG_FLOW2, "sorting by dependencies...");
+		lp = sortbydeps(trans->packages);
+		/* free the old alltargs */
+		for(j = trans->packages; j; j = j->next) {
+			j->data = NULL;
+		}
+		FREELIST(trans->packages);
+		trans->packages = lp;
+
+		TRANS_CB(trans, PM_TRANS_CB_DEPS_DONE, NULL, NULL);
+	}
+
+	/* Check for file conflicts
+	 */
+	if(!(trans->flags & PM_TRANS_FLAG_FORCE)) {
+		TRANS_CB(trans, PM_TRANS_CB_CONFLICTS_START, NULL, NULL);
+
+		lp = db_find_conflicts(db, trans->packages, handle->root);
+		if(lp != NULL) {
+			*data = lp;
+			PM_RET_ERR(PM_ERR_FILE_CONFLICTS, -1);
+		}
+
+		TRANS_CB(trans, PM_TRANS_CB_CONFLICTS_DONE, NULL, NULL);
+	}
+
+	return(0);
+}
+
+int add_commit(pmdb_t *db, pmtrans_t *trans)
+{
+	int i, ret = 0, errors = 0;
+	TAR *tar = NULL;
+	char expath[PATH_MAX];
+	time_t t;
+	pmpkg_t *info = NULL;
+	PMList *targ, *lp;
+
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1));
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+
+	if(trans->packages == NULL) {
+		return(0);
+	}
+
+	for(targ = trans->packages; targ; targ = targ->next) {
+		tartype_t gztype = {
+			(openfunc_t)_alpm_gzopen_frontend,
+			(closefunc_t)gzclose,
+			(readfunc_t)gzread,
+			(writefunc_t)gzwrite
+		};
+		unsigned short pmo_upgrade = (trans->type == PM_TRANS_TYPE_UPGRADE) ? 1 : 0;
+		char pm_install[PATH_MAX];
+		pmpkg_t *oldpkg = NULL;
+		info = (pmpkg_t *)targ->data;
+		errors = 0;
+
+		/* see if this is an upgrade.  if so, remove the old package first */
+		if(pmo_upgrade) {
+			if(pkg_isin(info, db_get_pkgcache(db))) {
+				TRANS_CB(trans, PM_TRANS_CB_UPGRADE_START, info, NULL);
+
+				/* we'll need the full record for backup checks later */
+				if((oldpkg = db_scan(db, info->name, INFRQ_ALL)) != NULL) {
+					pmtrans_t *tr;
+
+					_alpm_log(PM_LOG_FLOW2, "removing old package first...\n");
+					/* ORE
+					set flags to something, but what (nodeps?) ??? */
+					tr = trans_new();
+					if(tr == NULL) {
+						PM_RET_ERR(PM_ERR_TRANS_ABORT, -1);
+					}
+					if(trans_init(tr, PM_TRANS_TYPE_UPGRADE, 0, NULL) == -1) {
+						FREETRANS(tr);
+						PM_RET_ERR(PM_ERR_TRANS_ABORT, -1);
+					}
+					if(remove_loadtarget(db, tr, info->name) == -1) {
+						FREETRANS(tr);
+						PM_RET_ERR(PM_ERR_TRANS_ABORT, -1);
+					}
+					if(remove_commit(db, tr) == -1) {
+						FREETRANS(tr);
+						PM_RET_ERR(PM_ERR_TRANS_ABORT, -1);
+					}
+					FREETRANS(tr);
+				}
+			} else {
+				/* no previous package version is installed, so this is actually just an
+				 * install
+				 */
+				pmo_upgrade = 0;
+			}
+		}
+		if(!pmo_upgrade) {
+			TRANS_CB(trans, PM_TRANS_CB_ADD_START, info, NULL);
+		}
+
+		/* Add the package to the database */
+		t = time(NULL);
+
+		/* Update the requiredby field by scaning the whole database 
+		 * looking for packages depending on the package to add */
+		for(lp = db_get_pkgcache(db); lp; lp = lp->next) {
+			pmpkg_t *tmpp = NULL;
+			PMList *tmppm = NULL;
+
+			tmpp = db_scan(db, ((pmpkg_t *)lp->data)->name, INFRQ_DEPENDS);
+			if(tmpp == NULL) {
+				continue;
+			}
+			for(tmppm = tmpp->depends; tmppm; tmppm = tmppm->next) {
+				pmdepend_t depend;
+				splitdep(tmppm->data, &depend);
+				if(tmppm->data && !strcmp(depend.name, info->name)) {
+					info->requiredby = pm_list_add(info->requiredby, strdup(tmpp->name));
+					continue;
+				}
+			}
+		}
+
+		_alpm_log(PM_LOG_FLOW2, "updating database...");
+		/* Figure out whether this package was installed explicitly by the user
+		 * or installed as a dependency for another package
+		 */
+		/* ORE
+		info->reason = PM_PKG_REASON_EXPLICIT;
+		if(pm_list_is_strin(dependonly, info->data)) {
+			info->reason = PM_PKG_REASON_DEPEND;
+		}*/
+		/* make an install date (in UTC) */
+		strncpy(info->installdate, asctime(gmtime(&t)), sizeof(info->installdate));
+		if(db_write(db, info, INFRQ_ALL)) {
+			_alpm_log(PM_LOG_ERROR, "could not update database for %s", info->name);
+			alpm_logaction(NULL, "error updating database for %s!", info->name);
+			PM_RET_ERR(PM_ERR_DB_WRITE, -1);
+		}
+
+		/* update dependency packages' REQUIREDBY fields */
+		for(lp = info->depends; lp; lp = lp->next) {
+			pmpkg_t *depinfo = NULL;
+			pmdepend_t depend;
+
+			splitdep(lp->data, &depend);
+			depinfo = db_scan(db, depend.name, INFRQ_DESC|INFRQ_DEPENDS);
+			if(depinfo == NULL) {
+				/* look for a provides package */
+				PMList *provides = _alpm_db_whatprovides(db, depend.name);
+				if(provides) {
+					/* use the first one */
+					depinfo = db_scan(db, ((pmpkg_t *)provides->data)->name, INFRQ_DEPENDS);
+					if(depinfo == NULL) {
+						PMList *lp;
+						/* wtf */
+						for(lp = provides; lp; lp = lp->next) {
+							lp->data = NULL;
+						}
+						pm_list_free(provides);
+						continue;
+					}
+				} else {
+					continue;
+				}
+			}
+			depinfo->requiredby = pm_list_add(depinfo->requiredby, strdup(info->name));
+			db_write(db, depinfo, INFRQ_DEPENDS);
+			FREEPKG(depinfo);
+		}
+
+		/* Extract the .tar.gz package */
+		if(tar_open(&tar, info->data, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
+			PM_RET_ERR(PM_ERR_PKG_OPEN, -1);
+		}
+		_alpm_log(PM_LOG_DEBUG, "extracting files...");
+		for(i = 0; !th_read(tar); i++) {
+			int nb = 0;
+			int notouch = 0;
+			char *md5_orig = NULL;
+			char pathname[PATH_MAX];
+			struct stat buf;
+
+			strncpy(pathname, th_get_pathname(tar), PATH_MAX);
+
+			if(!strcmp(pathname, ".PKGINFO") || !strcmp(pathname, ".FILELIST")) {
+				tar_skip_regfile(tar);
+				continue;
+			}
+
+			if(!strcmp(pathname, "._install") || !strcmp(pathname, ".INSTALL")) {
+				/* the install script goes inside the db */
+				snprintf(expath, PATH_MAX, "%s/%s-%s/install", db->path, info->name, info->version);
+			} else {
+				/* build the new pathname relative to handle->root */
+				snprintf(expath, PATH_MAX, "%s%s", handle->root, pathname);
+			}
+
+			if(!stat(expath, &buf) && !S_ISDIR(buf.st_mode)) {
+				/* file already exists */
+				if(pm_list_is_strin(pathname, handle->noupgrade)) {
+					notouch = 1;
+				} else {
+					if(!pmo_upgrade || oldpkg == NULL) {
+						nb = pm_list_is_strin(pathname, info->backup) ? 1 : 0;
+					} else {
+						/* op == PM_UPGRADE */
+						if((md5_orig = _alpm_needbackup(pathname, oldpkg->backup)) != 0) {
+							nb = 1;
+						}
+					}
+				}
+			}
+
+			if(nb) {
+				char *temp;
+				char *md5_local, *md5_pkg;
+
+				md5_local = MDFile(expath);
+				/* extract the package's version to a temporary file and md5 it */
+				temp = strdup("/tmp/pacman_XXXXXX");
+				mkstemp(temp);
+				if(tar_extract_file(tar, temp)) {
+					alpm_logaction("could not extract %s: %s", pathname, strerror(errno));
+					errors++;
+					continue;
+				}
+				md5_pkg = MDFile(temp);
+				/* append the new md5 hash to it's respective entry in info->backup
+				 * (it will be the new orginal)
+				 */
+				for(lp = info->backup; lp; lp = lp->next) {
+					char *fn;
+
+					if(!lp->data) continue;
+					if(!strcmp((char*)lp->data, pathname)) {
+						/* 32 for the hash, 1 for the terminating NULL, and 1 for the tab delimiter */
+						MALLOC(fn, strlen(lp->data)+34);
+						sprintf(fn, "%s\t%s", (char*)lp->data, md5_pkg);
+						FREE(lp->data);
+						lp->data = fn;
+					}
+				}
+
+				_alpm_log(PM_LOG_FLOW2, " checking md5 hashes for %s", expath);
+				_alpm_log(PM_LOG_FLOW2, " current:  %s", md5_local);
+				_alpm_log(PM_LOG_FLOW2, " new:      %s", md5_pkg);
+				if(md5_orig) {
+					_alpm_log(PM_LOG_FLOW2, " original: %s", md5_orig);
+				}
+
+				if(!pmo_upgrade) {
+					/* PM_ADD */
+
+					/* if a file already exists with a different md5 hash,
+					 * then we rename it to a .pacorig extension and continue */
+					if(strcmp(md5_local, md5_pkg)) {
+						char newpath[PATH_MAX];
+						snprintf(newpath, PATH_MAX, "%s.pacorig", expath);
+						if(rename(expath, newpath)) {
+							_alpm_log(PM_LOG_ERROR, "could not rename %s: %s", expath, strerror(errno));
+							alpm_logaction("error: could not rename %s: %s", expath, strerror(errno));
+						}
+						if(_alpm_copyfile(temp, expath)) {
+							_alpm_log(PM_LOG_ERROR, "could not copy %s to %s: %s", temp, expath, strerror(errno));
+							alpm_logaction("error: could not copy %s to %s: %s", temp, expath, strerror(errno));
+							errors++;
+						} else {
+							_alpm_log(PM_LOG_WARNING, "warning: %s saved as %s", expath, newpath);
+							alpm_logaction("warning: %s saved as %s", expath, newpath);
+						}
+					}
+				} else if(md5_orig) {
+					/* PM_UPGRADE */
+					int installnew = 0;
+
+					/* the fun part */
+					if(!strcmp(md5_orig, md5_local)) {
+						if(!strcmp(md5_local, md5_pkg)) {
+							_alpm_log(PM_LOG_FLOW2, " action: installing new file");
+							installnew = 1;
+						} else {
+							_alpm_log(PM_LOG_FLOW2, " action: installing new file");
+							installnew = 1;
+						}
+					} else if(!strcmp(md5_orig, md5_pkg)) {
+						_alpm_log(PM_LOG_FLOW2, " action: leaving existing file in place");
+					} else if(!strcmp(md5_local, md5_pkg)) {
+						_alpm_log(PM_LOG_FLOW2, " action: installing new file");
+						installnew = 1;
+					} else {
+						char newpath[PATH_MAX];
+						_alpm_log(PM_LOG_FLOW2, " action: saving current file and installing new one");
+						installnew = 1;
+						snprintf(newpath, PATH_MAX, "%s.pacsave", expath);
+						if(rename(expath, newpath)) {
+							_alpm_log(PM_LOG_ERROR, "could not rename %s: %s", expath, strerror(errno));
+							alpm_logaction("error: could not rename %s: %s", expath, strerror(errno));
+						} else {
+							_alpm_log(PM_LOG_WARNING, "warning: %s saved as %s", expath, newpath);
+							alpm_logaction("warning: %s saved as %s", expath, newpath);
+						}
+					}
+
+					if(installnew) {
+						/*_alpm_log(PM_LOG_FLOW2, " %s", expath);*/
+						if(_alpm_copyfile(temp, expath)) {
+							_alpm_log(PM_LOG_ERROR, "could not copy %s to %s: %s", temp, expath, strerror(errno));
+							errors++;
+						}
+					}
+				}
+
+				FREE(md5_local);
+				FREE(md5_pkg);
+				FREE(md5_orig);
+				unlink(temp);
+				FREE(temp);
+			} else {
+				if(!notouch) {
+					_alpm_log(PM_LOG_FLOW2, "%s", pathname);
+				} else {
+					_alpm_log(PM_LOG_FLOW2, "%s is in NoUpgrade - skipping", pathname);
+					strncat(expath, ".pacnew", PATH_MAX);
+					_alpm_log(PM_LOG_WARNING, "warning: extracting %s%s as %s", handle->root, pathname, expath);
+					alpm_logaction("warning: extracting %s%s as %s", handle->root, pathname, expath);
+					tar_skip_regfile(tar);
+				}
+				if(trans->flags & PM_TRANS_FLAG_FORCE) {
+					/* if FORCE was used, then unlink() each file (whether it's there
+					 * or not) before extracting.  this prevents the old "Text file busy"
+					 * error that crops up if one tries to --force a glibc or pacman
+					 * upgrade.
+					 */
+					unlink(expath);
+				}
+				if(tar_extract_file(tar, expath)) {
+					_alpm_log(PM_LOG_ERROR, "could not extract %s: %s", pathname, strerror(errno));
+					alpm_logaction("could not extract %s: %s", pathname, strerror(errno));
+					errors++;
+				}
+				/* calculate an md5 hash if this is in info->backup */
+				for(lp = info->backup; lp; lp = lp->next) {
+					char *fn, *md5;
+					char path[PATH_MAX];
+
+					if(!lp->data) continue;
+					if(!strcmp((char*)lp->data, pathname)) {
+						snprintf(path, PATH_MAX, "%s%s", handle->root, (char*)lp->data);
+						md5 = MDFile(path);
+						/* 32 for the hash, 1 for the terminating NULL, and 1 for the tab delimiter */
+						MALLOC(fn, strlen(lp->data)+34);
+						sprintf(fn, "%s\t%s", (char*)lp->data, md5);
+						FREE(lp->data);
+						lp->data = fn;
+					}
+				}
+			}
+		}
+		tar_close(tar);
+
+		if(errors) {
+			ret = 1;
+			_alpm_log(PM_LOG_ERROR, "errors occurred while %s %s",
+				(pmo_upgrade ? "upgrading" : "installing"), info->name);
+			alpm_logaction("errors occurred while %s %s",
+				(pmo_upgrade ? "upgrading" : "installing"), info->name);
+		}
+
+		/* run the post-install script if it exists  */
+		snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version);
+		if(pmo_upgrade) {
+			_alpm_runscriptlet(handle->root, pm_install, "post_upgrade", info->version, oldpkg ? oldpkg->version : NULL);
+		} else {
+			_alpm_runscriptlet(handle->root, pm_install, "post_install", info->version, NULL);
+		}
+
+		if(pmo_upgrade && oldpkg) {
+			TRANS_CB(trans, PM_TRANS_CB_UPGRADE_DONE, info, NULL);
+			alpm_logaction("upgraded %s (%s -> %s)", info->name,
+				oldpkg->version, info->version);
+		} else {
+			TRANS_CB(trans, PM_TRANS_CB_ADD_DONE, info, NULL);
+			alpm_logaction("installed %s (%s)", info->name, info->version);
+		}
+
+		FREEPKG(oldpkg);
+	}
+
+	/* run ldconfig if it exists */
+	_alpm_log(PM_LOG_FLOW2, "running \"%ssbin/ldconfig -r %s\"", handle->root, handle->root);
+	_alpm_ldconfig(handle->root);
+
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/add.h b/lib/libalpm/add.h
new file mode 100644
index 00000000..cc578537
--- /dev/null
+++ b/lib/libalpm/add.h
@@ -0,0 +1,34 @@
+/*
+ *  add.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_ADD_H
+#define _ALPM_ADD_H
+
+#include "list.h"
+#include "db.h"
+#include "trans.h"
+
+int add_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name);
+int add_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data);
+int add_commit(pmdb_t *db, pmtrans_t *trans);
+
+#endif /* _ALPM_ADD_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c
new file mode 100644
index 00000000..23528ae4
--- /dev/null
+++ b/lib/libalpm/alpm.c
@@ -0,0 +1,577 @@
+/*
+ *  alpm.c
+ * 
+ *  Copyright (c) 2002 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <syslog.h>
+#include <limits.h> /* PATH_MAX */
+#include <stdarg.h>
+/* pacman */
+#include "log.h"
+#include "error.h"
+#include "rpmvercmp.h"
+#include "md5.h"
+#include "list.h"
+#include "package.h"
+#include "group.h"
+#include "util.h"
+#include "db.h"
+#include "cache.h"
+#include "deps.h"
+#include "backup.h"
+#include "add.h"
+#include "remove.h"
+#include "sync.h"
+#include "handle.h"
+#include "alpm.h"
+
+pmhandle_t *handle = NULL;
+
+enum __pmerrno_t pm_errno;
+
+/*
+ * Library
+ */
+
+int alpm_initialize(char *root)
+{
+	char str[PATH_MAX];
+
+	ASSERT(handle == NULL, PM_RET_ERR(PM_ERR_HANDLE_NOT_NULL, -1));
+
+	handle = handle_new();
+
+	/* lock db */
+	if(handle->access == PM_ACCESS_RW) {
+		if(_alpm_lckmk(PACLOCK) == -1) {
+			FREE(handle);
+			PM_RET_ERR(PM_ERR_HANDLE_LOCK, -1);
+		}
+	}
+
+	strncpy(str, (root) ? root : PACROOT, PATH_MAX);
+	/* add a trailing '/' if there isn't one */
+	if(str[strlen(str)-1] != '/') {
+		strcat(str, "/");
+	}
+	handle->root = strdup(str);
+
+	return(0);
+}
+
+int alpm_release()
+{
+	PMList *i;
+
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	/* unlock db */
+	if(handle->access == PM_ACCESS_RW) {
+		if(_alpm_lckrm(PACLOCK)) {
+			_alpm_log(PM_LOG_WARNING, "could not remove lock file %s", PACLOCK);
+			alpm_logaction("warning: could not remove lock file %s", PACLOCK);
+		}
+	}
+
+	/* close local database */
+	if(handle->db_local) {
+		db_close(handle->db_local);
+		handle->db_local = NULL;
+	}
+	/* and also sync ones */
+	for(i = handle->dbs_sync; i; i = i->next) {
+		if(i->data) {
+			db_close(i->data);
+			i->data = NULL;
+		}
+	}
+
+	FREEHANDLE(handle);
+
+	return(0);
+}
+
+/*
+ * Options
+ */
+
+int alpm_set_option(unsigned char parm, unsigned long data)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	return(handle_set_option(handle, parm, data));
+}
+
+int alpm_get_option(unsigned char parm, long *data)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	return(handle_get_option(handle, parm, data));
+}
+
+/*
+ * Databases
+ */
+
+int alpm_db_register(char *treename, PM_DB **db)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+	ASSERT(treename != NULL && strlen(treename) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	/* check if the db if already registered */
+	*db = db_open(handle->root, handle->dbpath, treename);
+	if(*db == NULL) {
+		/* couldn't open the db directory - try creating it */
+		if(db_create(handle->root, handle->dbpath, treename) == -1) {
+			PM_RET_ERR(PM_ERR_DB_CREATE, -1);
+		}
+		*db = db_open(handle->root, handle->dbpath, treename);
+		if(*db == NULL) {
+			/* couldn't open the db directory, try creating it */
+			PM_RET_ERR(PM_ERR_DB_OPEN, -1);
+		}
+	}
+
+	if(strcmp(treename, "local") == 0) {
+		handle->db_local = *db;
+	} else {
+		handle->dbs_sync = pm_list_add(handle->dbs_sync, *db);
+	}
+
+	return(0);
+}
+
+int alpm_db_unregister(PM_DB *db)
+{
+	PMList *i;
+	int found = 0;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	if(db == handle->db_local) {
+		db_close(handle->db_local);
+		handle->db_local = NULL;
+		found = 1;
+	} else {
+		for(i = handle->dbs_sync; i && !found; i = i->next) {
+			if(db == i->data) {
+				db_close(i->data);
+				i->data = NULL;
+				/* ORE
+				it should be _alpm_list_removed instead */
+				found = 1;
+			}
+		}
+	}
+
+	if(!found) {
+		PM_RET_ERR(PM_ERR_DB_NOT_FOUND, -1);
+	}
+
+	return(0);
+}
+
+PM_PKG *alpm_db_readpkg(PM_DB *db, char *name)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(db != NULL, return(NULL));
+	ASSERT(name != NULL && strlen(name) != 0, return(NULL));
+
+	return(db_get_pkgfromcache(db, name));
+}
+
+PM_LIST *alpm_db_getpkgcache(PM_DB *db)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(db != NULL, return(NULL));
+
+	return(db_get_pkgcache(db));
+}
+
+PM_GRP *alpm_db_readgrp(PM_DB *db, char *name)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(db != NULL, return(NULL));
+	ASSERT(name != NULL && strlen(name) != 0, return(NULL));
+
+	return(db_get_grpfromcache(db, name));
+}
+
+PM_LIST *alpm_db_getgrpcache(PM_DB *db)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(db != NULL, return(NULL));
+
+	return(db_get_grpcache(db));
+}
+
+PM_LIST *alpm_db_nextgrp(PM_LIST *cache)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(cache != NULL, return(NULL));
+
+	return(cache->next);
+}
+
+PM_GRP *alpm_db_getgrp(PM_LIST *cache)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(cache != NULL, return(NULL));
+
+	return(cache->data);
+}
+
+/*
+ * Packages
+ */
+
+void *alpm_pkg_getinfo(PM_PKG *pkg, unsigned char parm)
+{
+	void *data;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(pkg != NULL, return(NULL));
+
+	/* Update the cache package entry if needed */
+	if(pkg->origin == PKG_FROM_CACHE && pkg->data == handle->db_local) {
+		switch(parm) {
+			case PM_PKG_FILES:
+			case PM_PKG_BACKUP:
+				if(!(pkg->infolevel & INFRQ_FILES)) {
+					char target[321]; /* 256+1+64 */
+
+					snprintf(target, 321, "%s-%s", pkg->name, pkg->version);
+					db_read(pkg->data, target, INFRQ_FILES, pkg);
+				}
+			break;
+
+			case PM_PKG_SCRIPLET:
+				if(!(pkg->infolevel & INFRQ_SCRIPLET)) {
+					char target[321];
+
+					snprintf(target, 321, "%s-%s", pkg->name, pkg->version);
+					db_read(pkg->data, target, INFRQ_SCRIPLET, pkg);
+				}
+			break;
+		}
+	}
+
+	switch(parm) {
+		case PM_PKG_NAME:        data = pkg->name; break;
+		case PM_PKG_VERSION:     data = pkg->version; break;
+		case PM_PKG_DESC:        data = pkg->desc; break;
+		case PM_PKG_GROUPS:      data = pkg->groups; break;
+		case PM_PKG_URL:         data = pkg->url; break;
+		case PM_PKG_LICENSE:     data = pkg->license; break;
+		case PM_PKG_ARCH:        data = pkg->arch; break;
+		case PM_PKG_BUILDDATE:   data = pkg->builddate; break;
+		case PM_PKG_INSTALLDATE: data = pkg->installdate; break;
+		case PM_PKG_PACKAGER:    data = pkg->packager; break;
+		case PM_PKG_SIZE:        data = (void *)pkg->size; break;
+		case PM_PKG_REASON:      data = (void *)(int)pkg->reason; break;
+		case PM_PKG_REPLACES:    data = pkg->replaces; break;
+		case PM_PKG_MD5SUM:      data = pkg->md5sum; break;
+		case PM_PKG_DEPENDS:     data = pkg->depends; break;
+		case PM_PKG_REQUIREDBY:  data = pkg->requiredby; break;
+		case PM_PKG_PROVIDES:    data = pkg->provides; break;
+		case PM_PKG_CONFLICTS:   data = pkg->conflicts; break;
+		case PM_PKG_FILES:       data = pkg->files; break;
+		case PM_PKG_BACKUP:      data = pkg->backup; break;
+		case PM_PKG_SCRIPLET:    data = (void *)(int)pkg->scriptlet; break;
+		default:
+			data = NULL;
+		break;
+	}
+
+	return(data);
+}
+
+int alpm_pkg_load(char *filename, PM_PKG **pkg)
+{
+	/* Sanity checks */
+	ASSERT(filename != NULL && strlen(filename) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+	ASSERT(pkg != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	*pkg = pkg_load(filename);
+	if(*pkg == NULL) {
+		/* pm_errno is set by pkg_load */
+		return(-1);
+	}
+
+	return(0);
+}
+
+int alpm_pkg_free(PM_PKG *pkg)
+{
+	/* Sanity checks */
+	ASSERT(pkg != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	pkg_free(pkg);
+
+	return(0);
+}
+
+int alpm_pkg_vercmp(const char *ver1, const char *ver2)
+{
+	return(rpmvercmp(ver1, ver2));
+}
+
+/*
+ * Groups
+ */
+
+void *alpm_grp_getinfo(PM_GRP *grp, unsigned char parm)
+{
+	void *data;
+
+	/* Sanity checks */
+	ASSERT(grp != NULL, return(NULL));
+
+	switch(parm) {
+		case PM_GRP_NAME:     data = grp->name; break;
+		case PM_GRP_PKGNAMES: data = grp->packages; break;
+		default:
+			data = NULL;
+		break;
+	}
+
+	return(data);
+}
+
+/*
+ * Sync operations
+ */
+
+void *alpm_sync_getinfo(PM_SYNC *sync, unsigned char parm)
+{
+	void *data;
+
+	/* Sanity checks */
+	ASSERT(sync != NULL, return(NULL));
+
+	switch(parm) {
+		case PM_SYNC_TYPE:     data = (void *)(int)sync->type; break;
+		case PM_SYNC_LOCALPKG: data = sync->lpkg; break;
+		case PM_SYNC_SYNCPKG:  data = sync->spkg; break;
+		default:
+			data = NULL;
+		break;
+	}
+
+	return(data);
+}
+
+int alpm_sync_sysupgrade(PM_LIST **data)
+{
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+	ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	return(sync_sysupgrade(data));
+}
+
+/*
+ * Transactions
+ */
+
+void *alpm_trans_getinfo(unsigned char parm)
+{
+	pmtrans_t *trans;
+	void *data;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+
+	trans = handle->trans;
+
+	switch(parm) {
+		case PM_TRANS_TYPE:    data = (void *)(int)trans->type; break;
+		case PM_TRANS_FLAGS:   data = (void *)(int)trans->flags; break;
+		case PM_TRANS_TARGETS: data = trans->targets; break;
+		default:
+			data = NULL;
+		break;
+	}
+
+	return(data);
+}
+
+int alpm_trans_init(unsigned char type, unsigned char flags, alpm_trans_cb cb)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	ASSERT(handle->trans == NULL, PM_RET_ERR(PM_ERR_TRANS_NOT_NULL, -1));
+
+	handle->trans = trans_new();
+	if(handle->trans == NULL) {
+		PM_RET_ERR(PM_ERR_MEMORY, -1);
+	}
+
+	return(trans_init(handle->trans, type, flags, cb));
+}
+
+int alpm_trans_addtarget(char *target)
+{
+	pmtrans_t *trans;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+	ASSERT(target != NULL && strlen(target) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	trans = handle->trans;
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(trans->state == STATE_INITIALIZED, PM_RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
+
+	return(trans_addtarget(trans, target));
+}
+
+int alpm_trans_prepare(PMList **data)
+{
+	pmtrans_t *trans;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+	ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	trans = handle->trans;
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(trans->state == STATE_INITIALIZED, PM_RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
+
+	return(trans_prepare(handle->trans, data));
+}
+
+int alpm_trans_commit()
+{
+	pmtrans_t *trans;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	trans = handle->trans;
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(trans->state == STATE_PREPARED, PM_RET_ERR(PM_ERR_TRANS_NOT_PREPARED, -1));
+
+	/* ORE
+	ASSERT(handle->access != PM_ACCESS_RW, PM_RET_ERR(PM_ERR_BAD_PERMS, -1));*/
+
+	return(trans_commit(handle->trans));
+}
+
+int alpm_trans_release()
+{
+	pmtrans_t *trans;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	trans = handle->trans;
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(trans->state != STATE_IDLE, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+
+	FREETRANS(handle->trans);
+
+	return(0);
+}
+
+/*
+ * Log facilities
+ */
+
+int alpm_logaction(char *fmt, ...)
+{
+	char str[256];
+	va_list args;
+
+	va_start(args, fmt);
+	vsnprintf(str, 256, fmt, args);
+	va_end(args);
+
+	return(_alpm_log_action(handle->usesyslog, handle->logfd, str));
+}
+
+/*
+ * Lists wrappers
+ */
+
+PM_LIST *alpm_list_first(PM_LIST *list)
+{
+	ASSERT(list != NULL, return(NULL));
+
+	return(list);
+}
+
+PM_LIST *alpm_list_next(PM_LIST *entry)
+{
+	ASSERT(entry != NULL, return(NULL));
+
+	return(entry->next);
+}
+
+void *alpm_list_getdata(PM_LIST *entry)
+{
+	ASSERT(entry != NULL, return(NULL));
+
+	return(entry->data);
+}
+
+int alpm_list_free(PM_LIST *entry)
+{
+	if(entry) {
+		/* ORE
+		does not free all memory for packages... */
+		pm_list_free(entry);
+	}
+
+	return(0);
+}
+
+/*
+ * Misc wrappers
+ */
+
+char *alpm_get_md5sum(char *name)
+{
+	ASSERT(name != NULL, return(NULL));
+
+	return(MDFile(name));
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
new file mode 100644
index 00000000..85442196
--- /dev/null
+++ b/lib/libalpm/alpm.h
@@ -0,0 +1,330 @@
+/*
+ * alpm.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_H
+#define _ALPM_H
+
+/*
+ * Arch Linux Package Management library
+ */
+
+/* 
+ * Structures (opaque)
+ */
+
+typedef struct __pmlist_t PM_LIST;
+typedef struct __pmdb_t PM_DB;
+typedef struct __pmpkg_t PM_PKG;
+typedef struct __pmgrp_t PM_GRP;
+typedef struct __pmsync_t PM_SYNC;
+typedef struct __pmtrans_t PM_TRANS;
+/* ORE
+typedef struct __pmdepend_t PM_DEP;
+typedef struct __pmdepmissing_t PM_DEPMISS; */
+
+/*
+ * Library
+ */
+
+/* Version */
+#define ALPM_VERSION "0.1.0"
+
+int alpm_initialize(char *root);
+int alpm_release();
+
+/*
+ * Logging facilities
+ */
+
+/* Levels */
+#define PM_LOG_DEBUG    0x01
+#define PM_LOG_ERROR    0x02
+#define PM_LOG_WARNING  0x04
+#define PM_LOG_FLOW1    0x08
+#define PM_LOG_FLOW2    0x10
+#define PM_LOG_FUNCTION 0x20
+
+int alpm_logaction(char *fmt, ...);
+
+/*
+ * Options
+ */
+
+/* Parameters */
+enum {
+	PM_OPT_LOGCB = 1,
+	PM_OPT_LOGMASK,
+	PM_OPT_USESYSLOG,
+	PM_OPT_ROOT,
+	PM_OPT_DBPATH,
+	PM_OPT_LOGFILE,
+	PM_OPT_LOCALDB,
+	PM_OPT_SYNCDB,
+	PM_OPT_NOUPGRADE,
+	PM_OPT_IGNOREPKG,
+	PM_OPT_HOLDPKG
+};
+
+int alpm_set_option(unsigned char parm, unsigned long data);
+int alpm_get_option(unsigned char parm, long *data);
+
+/*
+ * Databases
+ */
+
+int alpm_db_register(char *treename, PM_DB **db);
+int alpm_db_unregister(PM_DB *db);
+
+PM_PKG *alpm_db_readpkg(PM_DB *db, char *name);
+PM_LIST *alpm_db_getpkgcache(PM_DB *db);
+
+PM_GRP *alpm_db_readgrp(PM_DB *db, char *name);
+PM_LIST *alpm_db_getgrpcache(PM_DB *db);
+
+/*
+ * Packages
+ */
+
+/* Info parameters */
+enum {
+	/* Desc entry */
+	PM_PKG_NAME = 1,
+	PM_PKG_VERSION,
+	PM_PKG_DESC,
+	PM_PKG_GROUPS,
+	PM_PKG_URL,
+	PM_PKG_LICENSE,
+	PM_PKG_ARCH,
+	PM_PKG_BUILDDATE,
+	PM_PKG_INSTALLDATE,
+	PM_PKG_PACKAGER,
+	PM_PKG_SIZE,
+	PM_PKG_REASON,
+	PM_PKG_REPLACES, /* Sync DB only */
+	PM_PKG_MD5SUM, /* Sync DB only */
+	/* Depends entry */
+	PM_PKG_DEPENDS,
+	PM_PKG_REQUIREDBY,
+	PM_PKG_CONFLICTS,
+	PM_PKG_PROVIDES,
+	/* Files entry */
+	PM_PKG_FILES,
+	PM_PKG_BACKUP,
+	/* Sciplet */
+	PM_PKG_SCRIPLET
+};
+
+/* reasons -- ie, why the package was installed */
+#define PM_PKG_REASON_EXPLICIT  0  /* explicitly requested by the user              */
+#define PM_PKG_REASON_DEPEND    1  /* installed as a dependency for another package */
+
+void *alpm_pkg_getinfo(PM_PKG *pkg, unsigned char parm);
+int alpm_pkg_load(char *filename, PM_PKG **pkg);
+int alpm_pkg_free(PM_PKG *pkg);
+int alpm_pkg_vercmp(const char *ver1, const char *ver2);
+
+/*
+ * Groups
+ */
+
+/* Info parameters */
+enum {
+	PM_GRP_NAME = 1,
+	PM_GRP_PKGNAMES
+};
+
+void *alpm_grp_getinfo(PM_GRP *grp, unsigned char parm);
+
+/*
+ * Sync
+ */
+
+/* Types */
+enum {
+	PM_SYSUPG_REPLACE = 1,
+	PM_SYSUPG_UPGRADE,
+	PM_SYSUPG_DEPEND
+};
+/* Info parameters */
+enum {
+	PM_SYNC_TYPE = 1,
+	PM_SYNC_LOCALPKG,
+	PM_SYNC_SYNCPKG
+};
+
+void *alpm_sync_getinfo(PM_SYNC *sync, unsigned char parm);
+int alpm_sync_sysupgrade(PM_LIST **data);
+
+/*
+ * Transactions
+ */
+
+/* Types */
+enum {
+	PM_TRANS_TYPE_ADD = 1,
+	PM_TRANS_TYPE_REMOVE,
+	PM_TRANS_TYPE_UPGRADE,
+	PM_TRANS_TYPE_SYNC
+};
+
+/* Flags */
+#define PM_TRANS_FLAG_NODEPS  0x01
+#define PM_TRANS_FLAG_FORCE   0x02
+#define PM_TRANS_FLAG_NOSAVE  0x04
+#define PM_TRANS_FLAG_FRESHEN 0x08
+#define PM_TRANS_FLAG_CASCADE 0x10
+#define PM_TRANS_FLAG_RECURSE 0x20
+#define PM_TRANS_FLAG_DBONLY  0x40
+
+/* Callback events */
+enum {
+	PM_TRANS_CB_DEPS_START = 1,
+	PM_TRANS_CB_DEPS_DONE,
+	PM_TRANS_CB_CONFLICTS_START,
+	PM_TRANS_CB_CONFLICTS_DONE,
+	PM_TRANS_CB_ADD_START,
+	PM_TRANS_CB_ADD_DONE,
+	PM_TRANS_CB_REMOVE_START,
+	PM_TRANS_CB_REMOVE_DONE,
+	PM_TRANS_CB_UPGRADE_START,
+	PM_TRANS_CB_UPGRADE_DONE
+};
+
+/* Callback */
+typedef void (*alpm_trans_cb)(unsigned short, void *, void *);
+
+/* Info parameters */
+enum {
+	PM_TRANS_TYPE = 1,
+	PM_TRANS_FLAGS,
+	PM_TRANS_TARGETS
+};
+
+/* Dependencies */
+enum {
+	PM_DEP_ANY = 1,
+	PM_DEP_EQ,
+	PM_DEP_GE,
+	PM_DEP_LE
+};
+enum {
+	PM_DEP_DEPEND = 1,
+	PM_DEP_REQUIRED,
+	PM_DEP_CONFLICT
+};
+
+/* ORE
+to be deprecated in favor of PM_DEP and PM_DEPMISS (opaque) */
+typedef struct __pmdepend_t {
+	unsigned short mod;
+	char name[256];
+	char version[64];
+} pmdepend_t;
+
+typedef struct __pmdepmissing_t {
+	unsigned char type;
+	char target[256];
+	pmdepend_t depend;
+} pmdepmissing_t;
+
+void *alpm_trans_getinfo(unsigned char parm);
+int alpm_trans_init(unsigned char type, unsigned char flags, alpm_trans_cb cb);
+int alpm_trans_addtarget(char *target);
+int alpm_trans_prepare(PM_LIST **data);
+int alpm_trans_commit();
+int alpm_trans_release();
+
+/*
+ * PM_LIST helpers
+ */
+PM_LIST *alpm_list_first(PM_LIST *list);
+PM_LIST *alpm_list_next(PM_LIST *entry);
+void *alpm_list_getdata(PM_LIST *entry);
+int alpm_list_free(PM_LIST *entry);
+
+/*
+ * Helpers
+ */
+ 
+char *alpm_get_md5sum(char *name);
+
+/*
+ * Errors
+ */
+
+extern enum __pmerrno_t {
+	PM_ERR_NOERROR = 1,
+	PM_ERR_MEMORY,
+	PM_ERR_SYSTEM,
+	PM_ERR_BADPERMS,
+	PM_ERR_NOT_A_FILE,
+	PM_ERR_WRONG_ARGS,
+	/* Interface */
+	PM_ERR_HANDLE_NULL,
+	PM_ERR_HANDLE_NOT_NULL,
+	PM_ERR_HANDLE_LOCK,
+	/* Databases */
+	PM_ERR_DB_OPEN,
+	PM_ERR_DB_CREATE,
+	PM_ERR_DB_NULL,
+	PM_ERR_DB_NOT_FOUND,
+	PM_ERR_DB_NOT_NULL,
+	PM_ERR_DB_WRITE,
+	/* Cache */
+	PM_ERR_CACHE_NULL,
+	/* Configuration */
+	PM_ERR_OPT_LOGFILE,
+	PM_ERR_OPT_DBPATH,
+	PM_ERR_OPT_LOCALDB,
+	PM_ERR_OPT_SYNCDB,
+	PM_ERR_OPT_USESYSLOG,
+	/* Transactions */
+	PM_ERR_TRANS_NOT_NULL,
+	PM_ERR_TRANS_NULL,
+	PM_ERR_TRANS_DUP_TARGET,
+	PM_ERR_TRANS_INITIALIZED,
+	PM_ERR_TRANS_NOT_INITIALIZED,
+	PM_ERR_TRANS_NOT_PREPARED,
+	PM_ERR_TRANS_ABORT,
+	/* Packages */
+	PM_ERR_PKG_NOT_FOUND,
+	PM_ERR_PKG_INVALID,
+	PM_ERR_PKG_OPEN,
+	PM_ERR_PKG_LOAD,
+	PM_ERR_PKG_INSTALLED,
+	PM_ERR_PKG_CANT_FRESH,
+	PM_ERR_INVALID_NAME,
+	/* Groups */
+	PM_ERR_GRP_NOT_FOUND,
+	/* Dependencies */
+	PM_ERR_UNSATISFIED_DEPS,
+	PM_ERR_CONFLICTING_DEPS,
+	PM_ERR_UNRESOLVABLE_DEPS,
+	PM_ERR_FILE_CONFLICTS,
+	/* Misc */
+	PM_ERR_USER_ABORT,
+	PM_ERR_INTERNAL_ERROR
+} pm_errno;
+
+char *alpm_strerror(int err);
+
+#endif /* _ALPM_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/backup.c b/lib/libalpm/backup.c
new file mode 100644
index 00000000..f664277e
--- /dev/null
+++ b/lib/libalpm/backup.c
@@ -0,0 +1,63 @@
+/*
+ *  backup.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+/* pacman */
+#include "backup.h"
+
+/* Look for a filename in a pkginfo_t.backup list.  If we find it,
+ * then we return the md5 hash (parsed from the same line)
+ */
+char *_alpm_needbackup(char* file, PMList *backup)
+{
+	PMList *lp;
+
+	if(file == NULL || backup == NULL) {
+		return(NULL);
+	}
+
+	/* run through the backup list and parse out the md5 hash for our file */
+	for(lp = backup; lp; lp = lp->next) {
+		char *str = strdup(lp->data);
+		char *ptr;
+		
+		/* tab delimiter */
+		ptr = strchr(str, '\t');
+		if(ptr == NULL) {
+			free(str);
+			continue;
+		}
+		*ptr = '\0';
+		ptr++;
+		/* now str points to the filename and ptr points to the md5 hash */
+		if(!strcmp(file, str)) {
+			free(str);
+			return(strdup(ptr));
+		}
+		free(str);
+	}
+
+	return(NULL);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/backup.h b/lib/libalpm/backup.h
new file mode 100644
index 00000000..af22e1e5
--- /dev/null
+++ b/lib/libalpm/backup.h
@@ -0,0 +1,30 @@
+/*
+ *  backup.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_BACKUP_H
+#define _ALPM_BACKUP_H
+
+#include "list.h"
+
+char *_alpm_needbackup(char* file, PMList *backup);
+
+#endif /* _ALPM_BACKUP_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/cache.c b/lib/libalpm/cache.c
new file mode 100644
index 00000000..c6b298eb
--- /dev/null
+++ b/lib/libalpm/cache.c
@@ -0,0 +1,203 @@
+/*
+ *  cache.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+/* pacman */
+#include "list.h"
+#include "package.h"
+#include "group.h"
+#include "db.h"
+#include "cache.h"
+
+/* Returns a new package cache from db.
+ * It frees the cache if it already exists.
+ */
+int db_load_pkgcache(pmdb_t *db)
+{
+	pmpkg_t *info;
+
+	if(db == NULL) {
+		return(-1);
+	}
+
+	db_free_pkgcache(db);
+
+	db_rewind(db);
+	while((info = db_scan(db, NULL, INFRQ_DESC|INFRQ_DEPENDS)) != NULL) {
+		info->origin = PKG_FROM_CACHE;
+		info->data = db;
+		/* add to the collective */
+		db->pkgcache = pm_list_add_sorted(db->pkgcache, info, pkg_cmp);
+	}
+
+	return(0);
+}
+
+void db_free_pkgcache(pmdb_t *db)
+{
+	if(db == NULL || db->pkgcache == NULL) {
+		return;
+	}
+
+	FREELISTPKGS(db->pkgcache);
+
+	if(db->grpcache) {
+		db_free_grpcache(db);
+	}
+}
+
+PMList *db_get_pkgcache(pmdb_t *db)
+{
+	if(db == NULL) {
+		return(NULL);
+	}
+
+	if(db->pkgcache == NULL) {
+		db_load_pkgcache(db);
+	}
+
+	return(db->pkgcache);
+}
+
+pmpkg_t *db_get_pkgfromcache(pmdb_t *db, char *target)
+{
+	PMList *i;
+
+	if(db == NULL || target == NULL || strlen(target) == 0) {
+		return(NULL);
+	}
+
+	for(i = db_get_pkgcache(db); i; i = i->next) {
+		pmpkg_t *info = i->data;
+
+		if(strcmp(info->name, target) == 0) {
+			return(info);
+		}
+	}
+
+	return(NULL);
+}
+
+/* Returns a new group cache from db.
+ * It frees the cache if it already exists.
+ */
+int db_load_grpcache(pmdb_t *db)
+{
+	PMList *lp;
+
+	if(db == NULL) {
+		return(-1);
+	}
+
+	if(db->pkgcache == NULL) {
+		db_load_pkgcache(db);
+	}
+
+	for(lp = db->pkgcache; lp; lp = lp->next) {
+		PMList *i;
+		pmpkg_t *pkg = lp->data;
+
+		for(i = pkg->groups; i; i = i->next) {
+			if(!pm_list_is_strin(i->data, db->grpcache)) {
+				pmgrp_t *grp = grp_new();
+
+				strncpy(grp->name, (char *)i->data, 256);
+				grp->packages = pm_list_add_sorted(grp->packages, pkg->name, grp_cmp);
+				db->grpcache = pm_list_add_sorted(db->grpcache, grp, grp_cmp);
+			} else {
+				PMList *j;
+
+				for(j = db->grpcache; j; j = j->next) {
+					pmgrp_t *grp = j->data;
+
+					if(strcmp(grp->name, i->data) == 0) {
+						if(!pm_list_is_strin(pkg->name, grp->packages)) {
+							grp->packages = pm_list_add_sorted(grp->packages, (char *)pkg->name, grp_cmp);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return(0);
+}
+
+void db_free_grpcache(pmdb_t *db)
+{
+	PMList *lg;
+
+	if(db == NULL || db->grpcache == NULL) {
+		return;
+	}
+
+	for(lg = db->grpcache; lg; lg = lg->next) {
+		PMList *lp;
+		pmgrp_t *grp = lg->data;
+
+		for(lp = grp->packages; lp; lp = lp->next) {
+			lp->data = NULL;
+		}
+		FREELIST(grp->packages);
+		FREEGRP(lg->data);
+	}
+	FREELIST(db->grpcache);
+}
+
+PMList *db_get_grpcache(pmdb_t *db)
+{
+	if(db == NULL) {
+		return(NULL);
+	}
+
+	if(db->grpcache == NULL) {
+		db_load_grpcache(db);
+	}
+
+	return(db->grpcache);
+}
+
+pmgrp_t *db_get_grpfromcache(pmdb_t *db, char *target)
+{
+	PMList *i;
+
+	if(db == NULL || target == NULL || strlen(target) == 0) {
+		return(NULL);
+	}
+
+	for(i = db_get_grpcache(db); i; i = i->next) {
+		pmgrp_t *info = i->data;
+
+		if(strcmp(info->name, target) == 0) {
+			return(info);
+		}
+	}
+
+	return(NULL);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/cache.h b/lib/libalpm/cache.h
new file mode 100644
index 00000000..b8897322
--- /dev/null
+++ b/lib/libalpm/cache.h
@@ -0,0 +1,42 @@
+/*
+ *  cache.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_CACHE_H
+#define _ALPM_CACHE_H
+
+#include "list.h"
+#include "package.h"
+#include "group.h"
+#include "db.h"
+
+/* packages */
+int db_load_pkgcache(pmdb_t *db);
+void db_free_pkgcache(pmdb_t *db);
+PMList *db_get_pkgcache(pmdb_t *db);
+pmpkg_t *db_get_pkgfromcache(pmdb_t *db, char *target);
+/* groups */
+int db_load_grpcache(pmdb_t *db);
+void db_free_grpcache(pmdb_t *db);
+PMList *db_get_grpcache(pmdb_t *db);
+pmgrp_t *db_get_grpfromcache(pmdb_t *db, char *target);
+
+#endif /* _ALPM_CACHE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c
new file mode 100644
index 00000000..3fad4bb6
--- /dev/null
+++ b/lib/libalpm/db.c
@@ -0,0 +1,651 @@
+/*
+ *  db.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+/* pacman */
+#include "util.h"
+#include "group.h"
+#include "cache.h"
+#include "db.h"
+
+/* Open a database and return a pmdb_t handle */
+pmdb_t *db_open(char *root, char *dbpath, char *treename)
+{
+	pmdb_t *db;
+
+	if(root == NULL || dbpath == NULL || treename == NULL) {
+		return(NULL);
+	}
+
+	MALLOC(db, sizeof(pmdb_t));
+
+	MALLOC(db->path, strlen(root)+strlen(dbpath)+strlen(treename)+2);
+	sprintf(db->path, "%s%s/%s", root, dbpath, treename);
+
+	db->dir = opendir(db->path);
+	if(db->dir == NULL) {
+		FREE(db->path);
+		FREE(db);
+		return(NULL);
+	}
+
+	strncpy(db->treename, treename, sizeof(db->treename)-1);
+
+	db->pkgcache = NULL;
+	db->grpcache = NULL;
+
+	return(db);
+}
+
+void db_close(pmdb_t *db)
+{
+	if(db == NULL) {
+		return;
+	}
+
+	if(db->dir) {
+		closedir(db->dir);
+		db->dir = NULL;
+	}
+	FREE(db->path);
+
+	db_free_pkgcache(db);
+	db_free_grpcache(db);
+
+	free(db);
+
+	return;
+}
+
+int db_create(char *root, char *dbpath, char *treename)
+{
+	char path[PATH_MAX];
+
+	if(root == NULL || dbpath == NULL || treename == NULL) {
+		return(-1);
+	}
+
+	snprintf(path, PATH_MAX, "%s%s/local", root, dbpath);
+	if(_alpm_makepath(path) != 0) {
+		return(-1);
+	}
+
+	return(0);
+}
+
+void db_rewind(pmdb_t *db)
+{
+	if(db == NULL || db->dir == NULL) {
+		return;
+	}
+
+	rewinddir(db->dir);
+}
+
+pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq)
+{
+	struct dirent *ent = NULL;
+	char name[256];
+	char *ptr = NULL;
+	int ret, found = 0;
+	pmpkg_t *pkg;
+
+	if(db == NULL) {
+		return(NULL);
+	}
+
+	if(target != NULL) {
+		/* search for a specific package (by name only) */
+		rewinddir(db->dir);
+		while(!found && (ent = readdir(db->dir)) != NULL) {
+			if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+				continue;
+			}
+			strncpy(name, ent->d_name, 255);
+			/* truncate the string at the second-to-last hyphen, */
+			/* which will give us the package name */
+			if((ptr = rindex(name, '-'))) {
+				*ptr = '\0';
+			}
+			if((ptr = rindex(name, '-'))) {
+				*ptr = '\0';
+			}
+			if(!strcmp(name, target)) {
+				found = 1;
+			}
+		}
+		if(!found) {
+			return(NULL);
+		}
+	} else {
+		/* normal iteration */
+		ent = readdir(db->dir);
+		if(ent == NULL) {
+			return(NULL);
+		}
+		if(!strcmp(ent->d_name, ".")) {
+			ent = readdir(db->dir);
+			if(ent == NULL) {
+				return(NULL);
+			}
+		}
+		if(!strcmp(ent->d_name, "..")) {
+			ent = readdir(db->dir);
+			if(ent == NULL) {
+				return(NULL);
+			}
+		}
+	}
+
+	pkg = pkg_new();
+	if(pkg == NULL) {
+		return(NULL);
+	}
+	ret = db_read(db, ent->d_name, inforeq, pkg);
+	if(ret == -1) {
+		FREEPKG(pkg);
+	}
+
+	return(ret == 0 ? pkg : NULL);
+}
+
+int db_read(pmdb_t *db, char *name, unsigned int inforeq, pmpkg_t *info)
+{
+	FILE *fp = NULL;
+	struct stat buf;
+	char path[PATH_MAX];
+	char line[512];
+
+	if(db == NULL || name == NULL || info == NULL) {
+		return(-1);
+	}
+
+	snprintf(path, PATH_MAX, "%s/%s", db->path, name);
+	if(stat(path, &buf)) {
+		/* directory doesn't exist or can't be opened */
+		return(-1);
+	}
+
+	/* DESC */
+	if(inforeq & INFRQ_DESC) {
+		snprintf(path, PATH_MAX, "%s/%s/desc", db->path, name);
+		fp = fopen(path, "r");
+		if(fp == NULL) {
+			fprintf(stderr, "error: %s: %s\n", path, strerror(errno));
+			return(-1);
+		}
+		while(!feof(fp)) {
+			if(fgets(line, 256, fp) == NULL) {
+				break;
+			}
+			_alpm_strtrim(line);
+			if(!strcmp(line, "%NAME%")) {
+				if(fgets(info->name, sizeof(info->name), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->name);
+			} else if(!strcmp(line, "%VERSION%")) {
+				if(fgets(info->version, sizeof(info->version), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->version);
+			} else if(!strcmp(line, "%DESC%")) {
+				if(fgets(info->desc, sizeof(info->desc), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->desc);
+			} else if(!strcmp(line, "%GROUPS%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					char *s = strdup(line);
+					info->groups = pm_list_add(info->groups, s);
+				}
+			} else if(!strcmp(line, "%URL%")) {
+				if(fgets(info->url, sizeof(info->url), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->url);
+			} else if(!strcmp(line, "%LICENSE%")) {
+				if(fgets(info->license, sizeof(info->license), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->license);
+			} else if(!strcmp(line, "%ARCH%")) {
+				if(fgets(info->arch, sizeof(info->arch), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->arch);
+			} else if(!strcmp(line, "%BUILDDATE%")) {
+				if(fgets(info->builddate, sizeof(info->builddate), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->builddate);
+			} else if(!strcmp(line, "%INSTALLDATE%")) {
+				if(fgets(info->installdate, sizeof(info->installdate), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->installdate);
+			} else if(!strcmp(line, "%PACKAGER%")) {
+				if(fgets(info->packager, sizeof(info->packager), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(info->packager);
+			} else if(!strcmp(line, "%REASON%")) {
+				char tmp[32];
+				if(fgets(tmp, sizeof(tmp), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(tmp);
+				info->reason = atol(tmp);
+			} else if(!strcmp(line, "%SIZE%")) {
+				char tmp[32];
+				if(fgets(tmp, sizeof(tmp), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(tmp);
+				info->size = atol(tmp);
+			} else if(!strcmp(line, "%CSIZE%")) {
+				/* NOTE: the CSIZE and SIZE fields both share the "size" field
+				 *       in the pkginfo_t struct.  This can be done b/c CSIZE
+				 *       is currently only used in sync databases, and SIZE is
+				 *       only used in local databases.
+				 */
+				char tmp[32];
+				if(fgets(tmp, sizeof(tmp), fp) == NULL) {
+					return(-1);
+				}
+				_alpm_strtrim(tmp);
+				info->size = atol(tmp);
+			} else if(!strcmp(line, "%REPLACES%")) {
+				/* the REPLACES tag is special -- it only appears in sync repositories,
+				 * not the local one. */
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->replaces = pm_list_add(info->replaces, strdup(line));
+				}
+			} else if(!strcmp(line, "%MD5SUM%")) {
+				/* MD5SUM tag only appears in sync repositories,
+				 * not the local one. */
+				if(fgets(info->md5sum, sizeof(info->md5sum), fp) == NULL) {
+					return(-1);
+				}
+			} else if(!strcmp(line, "%FORCE%")) {
+				/* FORCE tag only appears in sync repositories,
+				 * not the local one. */
+				info->force = 1;
+			}
+		}
+		fclose(fp);
+	}
+
+	/* FILES */
+	if(inforeq & INFRQ_FILES) {
+		snprintf(path, PATH_MAX, "%s/%s/files", db->path, name);
+		fp = fopen(path, "r");
+		if(fp == NULL) {
+			fprintf(stderr, "error: %s: %s\n", path, strerror(errno));
+			return(-1);
+		}
+		while(fgets(line, 256, fp)) {
+			_alpm_strtrim(line);
+			if(!strcmp(line, "%FILES%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->files = pm_list_add(info->files, strdup(line));
+				}
+			} else if(!strcmp(line, "%BACKUP%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->backup = pm_list_add(info->backup, strdup(line));
+				}
+			}
+		}
+		fclose(fp);
+	}
+
+	/* DEPENDS */
+	if(inforeq & INFRQ_DEPENDS) {
+		snprintf(path, PATH_MAX, "%s/%s/depends", db->path, name);
+		fp = fopen(path, "r");
+		if(fp == NULL) {
+			fprintf(stderr, "error: %s: %s\n", path, strerror(errno));
+			return(-1);
+		}
+		while(!feof(fp)) {
+			fgets(line, 255, fp);
+			_alpm_strtrim(line);
+			if(!strcmp(line, "%DEPENDS%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->depends = pm_list_add(info->depends, strdup(line));
+				}
+			} else if(!strcmp(line, "%REQUIREDBY%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->requiredby = pm_list_add(info->requiredby, strdup(line));
+				}
+			} else if(!strcmp(line, "%CONFLICTS%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->conflicts = pm_list_add(info->conflicts, strdup(line));
+				}
+			} else if(!strcmp(line, "%PROVIDES%")) {
+				while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
+					info->provides = pm_list_add(info->provides, strdup(line));
+				}
+			}
+		}
+		fclose(fp);
+	}
+
+	/* INSTALL */
+	if(inforeq & INFRQ_SCRIPLET) {
+		snprintf(path, PATH_MAX, "%s/%s/install", db->path, name);
+		if(!stat(path, &buf)) {
+			info->scriptlet = 1;
+		}
+	}
+
+	/* internal */
+	info->infolevel |= inforeq;
+
+	return(0);
+}
+
+int db_write(pmdb_t *db, pmpkg_t *info, unsigned int inforeq)
+{
+	char topdir[PATH_MAX];
+	FILE *fp = NULL;
+	char path[PATH_MAX];
+	mode_t oldmask;
+	PMList *lp = NULL;
+
+	if(db == NULL || info == NULL) {
+		return(-1);
+	}
+
+	snprintf(topdir, PATH_MAX, "%s/%s-%s", db->path,
+		info->name, info->version);
+	oldmask = umask(0000);
+	mkdir(topdir, 0755);
+	/* make sure we have a sane umask */
+	umask(0022);
+
+	/* DESC */
+	if(inforeq & INFRQ_DESC) {
+		snprintf(path, PATH_MAX, "%s/desc", topdir);
+		if((fp = fopen(path, "w")) == NULL) {
+			perror("db_write");
+			umask(oldmask);
+			return(-1);
+		}
+		fputs("%NAME%\n", fp);
+		fprintf(fp, "%s\n\n", info->name);
+		fputs("%VERSION%\n", fp);
+		fprintf(fp, "%s\n\n", info->version);
+		fputs("%DESC%\n", fp);
+		fprintf(fp, "%s\n\n", info->desc);
+		fputs("%GROUPS%\n", fp);
+		for(lp = info->groups; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fputs("%URL%\n", fp);
+		fprintf(fp, "%s\n\n", info->url);
+		fputs("%LICENSE%\n", fp);
+		fprintf(fp, "%s\n\n", info->license);
+		fputs("%ARCH%\n", fp);
+		fprintf(fp, "%s\n\n", info->arch);
+		fputs("%BUILDDATE%\n", fp);
+		fprintf(fp, "%s\n\n", info->builddate);
+		fputs("%INSTALLDATE%\n", fp);
+		fprintf(fp, "%s\n\n", info->installdate);
+		fputs("%PACKAGER%\n", fp);
+		fprintf(fp, "%s\n\n", info->packager);
+		fputs("%SIZE%\n", fp);
+		fprintf(fp, "%ld\n\n", info->size);
+		fputs("%REASON%\n", fp);
+		fprintf(fp, "%ld\n\n", info->size);
+		fclose(fp);
+	}
+
+	/* FILES */
+	if(inforeq & INFRQ_FILES) {
+		snprintf(path, PATH_MAX, "%s/files", topdir);
+		if((fp = fopen(path, "w")) == NULL) {
+			perror("db_write");
+			umask(oldmask);
+			return(-1);
+		}
+		fputs("%FILES%\n", fp);
+		for(lp = info->files; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fputs("%BACKUP%\n", fp);
+		for(lp = info->backup; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fclose(fp);
+	}
+
+	/* DEPENDS */
+	if(inforeq & INFRQ_DEPENDS) {
+		snprintf(path, PATH_MAX, "%s/depends", topdir);
+		if((fp = fopen(path, "w")) == NULL) {
+			perror("db_write");
+			umask(oldmask);
+			return(-1);
+		}
+		fputs("%DEPENDS%\n", fp);
+		for(lp = info->depends; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fputs("%REQUIREDBY%\n", fp);
+		for(lp = info->requiredby; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fputs("%CONFLICTS%\n", fp);
+		for(lp = info->conflicts; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fputs("%PROVIDES%\n", fp);
+		for(lp = info->provides; lp; lp = lp->next) {
+			fprintf(fp, "%s\n", (char*)lp->data);
+		}
+		fprintf(fp, "\n");
+		fclose(fp);
+	}
+
+	/* INSTALL */
+	/* nothing needed here (script is automatically extracted) */
+
+	umask(oldmask);
+
+	return(0);
+}
+
+int db_remove(pmdb_t *db, pmpkg_t *info)
+{
+	char topdir[PATH_MAX];
+	char file[PATH_MAX];
+
+	if(db == NULL || info == NULL) {
+		return(-1);
+	}
+
+	snprintf(topdir, PATH_MAX, "%s/%s-%s", db->path, info->name, info->version);
+
+	/* DESC */
+	snprintf(file, PATH_MAX, "%s/desc", topdir);
+	unlink(file);
+	/* FILES */
+	snprintf(file, PATH_MAX, "%s/files", topdir);
+	unlink(file);
+	/* DEPENDS */
+	snprintf(file, PATH_MAX, "%s/depends", topdir);
+	unlink(file);
+	/* INSTALL */
+	snprintf(file, PATH_MAX, "%s/install", topdir);
+	unlink(file);
+	/* Package directory */
+	if(rmdir(topdir) == -1) {
+		return(-1);
+	}
+
+	return(0);
+}
+
+PMList *db_find_conflicts(pmdb_t *db, PMList *targets, char *root)
+{
+	PMList *i, *j, *k;
+	char *filestr = NULL;
+	char path[PATH_MAX+1];
+	char *str = NULL;
+	struct stat buf, buf2;
+	PMList *conflicts = NULL;
+
+	if(db == NULL || targets == NULL || root == NULL) {
+		return(NULL);
+	}
+
+	/* CHECK 1: check every db package against every target package */
+	/* XXX: I've disabled the database-against-targets check for now, as the
+	 *      many many strcmp() calls slow it down heavily and most of the
+	 *      checking is redundant to the targets-against-filesystem check.
+	 *      This will be re-enabled if I can improve performance significantly.
+	 *
+	pmpkg_t *info = NULL;
+	char *dbstr   = NULL;
+	rewinddir(db->dir);
+	while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_FILES)) != NULL) {
+		for(i = info->files; i; i = i->next) {
+			if(i->data == NULL) continue;
+			dbstr = (char*)i->data;
+			for(j = targets; j; j = j->next) {
+				pmpkg_t *targ = (pmpkg_t*)j->data;
+				if(strcmp(info->name, targ->name)) {
+					for(k = targ->files; k; k = k->next) {
+						filestr = (char*)k->data;
+						if(!strcmp(dbstr, filestr)) {
+							if(rindex(k->data, '/') == filestr+strlen(filestr)-1) {
+								continue;
+							}
+							MALLOC(str, 512);
+							snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (installed)", dbstr,
+								targ->name, info->name);
+							conflicts = pm_list_add(conflicts, str);
+						}
+					}
+				}
+			}
+		}
+	}*/
+
+	/* CHECK 2: check every target against every target */
+	for(i = targets; i; i = i->next) {
+		pmpkg_t *p1 = (pmpkg_t*)i->data;
+		for(j = i; j; j = j->next) {
+			pmpkg_t *p2 = (pmpkg_t*)j->data;
+			if(strcmp(p1->name, p2->name)) {
+				for(k = p1->files; k; k = k->next) {
+					filestr = k->data;
+					if(!strcmp(filestr, "._install") || !strcmp(filestr, ".INSTALL")) {
+						continue;
+					}
+					if(rindex(filestr, '/') == filestr+strlen(filestr)-1) {
+						/* this filename has a trailing '/', so it's a directory -- skip it. */
+						continue;
+					}
+					if(pm_list_is_strin(filestr, p2->files)) {
+						MALLOC(str, 512);
+						snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (target)",
+							filestr, p1->name, p2->name);
+						conflicts = pm_list_add(conflicts, str);
+					}
+				}
+			}
+		}
+	}
+
+	/* CHECK 3: check every target against the filesystem */
+	for(i = targets; i; i = i->next) {
+		pmpkg_t *p = (pmpkg_t*)i->data;
+		pmpkg_t *dbpkg = NULL;
+		for(j = p->files; j; j = j->next) {
+			filestr = (char*)j->data;
+			snprintf(path, PATH_MAX, "%s%s", root, filestr);
+			if(!stat(path, &buf) && !S_ISDIR(buf.st_mode)) {
+				int ok = 0;
+				if(dbpkg == NULL) {
+					dbpkg = db_scan(db, p->name, INFRQ_DESC | INFRQ_FILES);
+				}
+				if(dbpkg && pm_list_is_strin(j->data, dbpkg->files)) {
+					ok = 1;
+				}
+				/* Make sure that the supposedly-conflicting file is not actually just
+				 * a symlink that points to a path that used to exist in the package.
+				 */
+				/* Check if any part of the conflicting file's path is a symlink */
+				if(dbpkg && !ok) {
+					char str[PATH_MAX];
+					for(k = dbpkg->files; k; k = k->next) {
+						snprintf(str, PATH_MAX, "%s%s", root, (char*)k->data);
+						stat(str, &buf2);
+						if(buf.st_ino == buf2.st_ino) {
+							ok = 1;
+						}
+					}
+				}
+				/* Check if the conflicting file has been moved to another package/target */
+				if(!ok) {
+					/* Look at all the targets */
+					for(k = targets; k && !ok; k = k->next) {
+						pmpkg_t *p1 = (pmpkg_t *)k->data;
+						/* As long as they're not the current package */
+						if(strcmp(p1->name, p->name)) {
+							pmpkg_t *dbpkg2 = NULL;
+							dbpkg2 = db_scan(db, p1->name, INFRQ_DESC | INFRQ_FILES);
+							/* If it used to exist in there, but doesn't anymore */
+							if(dbpkg2 && !pm_list_is_strin(filestr, p1->files) && pm_list_is_strin(filestr, dbpkg2->files)) {
+								ok = 1;
+							}
+							FREEPKG(dbpkg2);
+						}
+					}
+				}
+				if(!ok) {
+					MALLOC(str, 512);
+					snprintf(str, 512, "%s: exists in filesystem", path);
+					conflicts = pm_list_add(conflicts, str);
+				}
+			}
+		}
+		FREEPKG(dbpkg);
+	}
+
+	return(conflicts);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h
new file mode 100644
index 00000000..b4161871
--- /dev/null
+++ b/lib/libalpm/db.h
@@ -0,0 +1,60 @@
+/*
+ *  db.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_DB_H
+#define _ALPM_DB_H
+
+#include <dirent.h>
+
+#include "list.h"
+#include "package.h"
+
+/* Database entries */
+#define INFRQ_NONE     0x00
+#define INFRQ_DESC     0x01
+#define INFRQ_DEPENDS  0x02
+#define INFRQ_FILES    0x04
+#define INFRQ_SCRIPLET 0x08
+#define INFRQ_ALL      0xFF
+
+/* Database */
+typedef struct __pmdb_t {
+	char *path;
+	char treename[128];
+	DIR *dir;
+	PMList *pkgcache;
+	PMList *grpcache;
+} pmdb_t;
+
+pmdb_t *db_open(char *root, char *dbpath, char *treename);
+void db_close(pmdb_t *db);
+int db_create(char *root, char *dbpath, char *treename);
+
+void db_rewind(pmdb_t *db);
+pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq);
+int db_read(pmdb_t *db, char *name, unsigned int inforeq, pmpkg_t *info);
+int db_write(pmdb_t *db, pmpkg_t *info, unsigned int inforeq);
+int db_remove(pmdb_t *db, pmpkg_t *info);
+
+PMList *db_find_conflicts(pmdb_t *db, PMList *targets, char *root);
+
+#endif /* _ALPM_DB_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c
new file mode 100644
index 00000000..d8b79974
--- /dev/null
+++ b/lib/libalpm/deps.c
@@ -0,0 +1,685 @@
+/*
+ *  deps.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+/* pacman */
+#include "util.h"
+#include "log.h"
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "cache.h"
+#include "provide.h"
+#include "deps.h"
+#include "rpmvercmp.h"
+
+/* Re-order a list of target packages with respect to their dependencies.
+ *
+ * Example:
+ *   A depends on C
+ *   B depends on A
+ *   Target order is A,B,C,D
+ *
+ *   Should be re-ordered to C,A,B,D
+ * 
+ * This function returns the new PMList* target list.
+ *
+ */ 
+PMList *sortbydeps(PMList *targets)
+{
+	PMList *newtargs = NULL;
+	PMList *i, *j, *k;
+	int change = 1;
+	int numscans = 0;
+	int numtargs = 0;
+	int clean = 0;
+
+	if(targets == NULL) {
+		return(NULL);
+	}
+
+	/* count the number of targets */
+	numtargs = pm_list_count(targets);
+
+	while(change) {
+		change = 0;
+		if(numscans > numtargs) {
+			_alpm_log(PM_LOG_FLOW2, "warning: possible dependency cycle detected\n");
+			change = 0;
+			continue;
+		}
+		newtargs = NULL;
+		numscans++;
+		/* run thru targets, moving up packages as necessary */
+		for(i = targets; i; i = i->next) {
+			pmpkg_t *p = (pmpkg_t*)i->data;
+			for(j = p->depends; j; j = j->next) {
+				pmdepend_t dep;
+				int found = 0;
+				pmpkg_t *q = NULL;
+
+				splitdep(j->data, &dep);
+				/* look for dep.name -- if it's farther down in the list, then
+				 * move it up above p
+				 */
+				for(k = i->next; k && !found; k = k->next) {
+					q = (pmpkg_t*)k->data;
+					if(!strcmp(dep.name, q->name)) {
+						found = 1;
+					}
+				}
+				if(found) {
+					if(!pkg_isin(q, newtargs)) {
+						change = 1;
+						newtargs = pm_list_add(newtargs, q);
+					}
+				}
+			}
+			if(!pkg_isin(p, newtargs)) {
+				newtargs = pm_list_add(newtargs, p);
+			}
+		}
+		if(clean && change) {
+			/* free up targets -- it's local now */
+			for(i = targets; i; i = i->next) {
+				i->data = NULL;
+			}
+			pm_list_free(targets);
+		}
+		targets = newtargs;
+		clean = 1;
+	}
+	return(targets);
+}
+
+/* Returns a PMList* of missing_t pointers.
+ *
+ * conflicts are always name only, but dependencies can include versions
+ * with depmod operators.
+ *
+ */
+PMList *checkdeps(pmdb_t *db, unsigned short op, PMList *packages)
+{
+	pmpkg_t *info = NULL;
+	pmdepend_t depend;
+	PMList *i, *j, *k;
+	int cmp;
+	int found = 0;
+	PMList *baddeps = NULL;
+	pmdepmissing_t *miss = NULL;
+
+	if(db == NULL) {
+		return(NULL);
+	}
+
+	if(op == PM_TRANS_TYPE_UPGRADE) {
+		/* PM_TRANS_TYPE_UPGRADE handles the backwards dependencies, ie, the packages
+		 * listed in the requiredby field.
+		 */
+		for(i = packages; i; i = i->next) {
+			pmpkg_t *tp, *oldpkg;
+			if(i->data == NULL) {
+				continue;
+			}
+			tp = (pmpkg_t *)i->data;
+
+			if((oldpkg = db_scan(db, tp->name, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) {
+				continue;
+			}
+			for(j = oldpkg->requiredby; j; j = j->next) {
+				char *ver;
+				pmpkg_t *p;
+				found = 0;
+				if((p = db_scan(db, j->data, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) {
+					/* hmmm... package isn't installed.. */
+					continue;
+				}
+				if(pkg_isin(p, packages)) {
+					/* this package is also in the upgrade list, so don't worry about it */
+					continue;
+				}
+				for(k = p->depends; k && !found; k = k->next) {
+					/* find the dependency info in p->depends */
+					splitdep(k->data, &depend);
+					if(!strcmp(depend.name, oldpkg->name)) {
+						found = 1;
+					}
+				}
+				if(found == 0) {
+					PMList *lp;
+					/* look for packages that list depend.name as a "provide" */
+					PMList *provides = _alpm_db_whatprovides(db, depend.name);
+					if(provides == NULL) {
+						/* not found */
+						continue;
+					}
+					/* we found an installed package that provides depend.name */
+					for(lp = provides; lp; lp = lp->next) {
+						lp->data = NULL;
+					}
+					pm_list_free(provides);
+				}
+				found = 0;
+				if(depend.mod == PM_DEP_ANY) {
+					found = 1;
+				} else {
+					/* note that we use the version from the NEW package in the check */
+					ver = strdup(tp->version);
+					if(!index(depend.version,'-')) {
+						char *ptr;
+						for(ptr = ver; *ptr != '-'; ptr++);
+						*ptr = '\0';
+					}
+					cmp = rpmvercmp(ver, depend.version);
+					switch(depend.mod) {
+						case PM_DEP_EQ: found = (cmp == 0); break;
+						case PM_DEP_GE: found = (cmp >= 0); break;
+						case PM_DEP_LE: found = (cmp <= 0); break;
+					}
+					FREE(ver);
+				}
+				if(!found) {
+					MALLOC(miss, sizeof(pmdepmissing_t));
+					miss->type = PM_DEP_REQUIRED;
+					miss->depend.mod = depend.mod;
+					strncpy(miss->target, p->name, 256);
+					strncpy(miss->depend.name, depend.name, 256);
+					strncpy(miss->depend.version, depend.version, 64);
+					if(!pm_list_is_ptrin(baddeps, miss)) {
+						baddeps = pm_list_add(baddeps, miss);
+					}
+				}
+			}
+			pkg_free(oldpkg);
+		}
+	}
+	if(op == PM_TRANS_TYPE_ADD || op == PM_TRANS_TYPE_UPGRADE) {
+		for(i = packages; i; i = i->next) {
+			pmpkg_t *tp = i->data;
+			if(tp == NULL) {
+				continue;
+			}
+
+			/* CONFLICTS */
+			for(j = tp->conflicts; j; j = j->next) {
+				/* check targets against database */
+				for(k = db_get_pkgcache(db); k; k = k->next) {
+					pmpkg_t *dp = (pmpkg_t *)k->data;
+					if(!strcmp(j->data, dp->name)) {
+						MALLOC(miss, sizeof(pmdepmissing_t));
+						miss->type = PM_DEP_CONFLICT;
+						miss->depend.mod = PM_DEP_ANY;
+						miss->depend.version[0] = '\0';
+						strncpy(miss->target, tp->name, 256);
+						strncpy(miss->depend.name, dp->name, 256);
+						if(!pm_list_is_ptrin(baddeps, miss)) {
+							baddeps = pm_list_add(baddeps, miss);
+						}
+					}
+				}
+				/* check targets against targets */
+				for(k = packages; k; k = k->next) {
+					pmpkg_t *a = (pmpkg_t *)k->data;
+					if(!strcmp(a->name, (char *)j->data)) {
+						MALLOC(miss, sizeof(pmdepmissing_t));
+						miss->type = PM_DEP_CONFLICT;
+						miss->depend.mod = PM_DEP_ANY;
+						miss->depend.version[0] = '\0';
+						strncpy(miss->target, tp->name, 256);
+						strncpy(miss->depend.name, a->name, 256);
+						if(!pm_list_is_ptrin(baddeps, miss)) {
+							baddeps = pm_list_add(baddeps, miss);
+						}
+					}
+				}
+			}
+			/* check database against targets */
+			for(k = db_get_pkgcache(db); k; k = k->next) {
+				info = k->data;
+				for(j = info->conflicts; j; j = j->next) {
+					if(!strcmp((char *)j->data, tp->name)) {
+						MALLOC(miss, sizeof(pmdepmissing_t));
+						miss->type = PM_DEP_CONFLICT;
+						miss->depend.mod = PM_DEP_ANY;
+						miss->depend.version[0] = '\0';
+						strncpy(miss->target, tp->name, 256);
+						strncpy(miss->depend.name, info->name, 256);
+						if(!pm_list_is_ptrin(baddeps, miss)) {
+							baddeps = pm_list_add(baddeps, miss);
+						}
+					}
+				}
+			}
+
+			/* PROVIDES -- check to see if another package already provides what
+			 *             we offer
+ 			 */
+			/* XXX: disabled -- we allow multiple packages to provide the same thing.
+			 *      list packages in conflicts if they really do conflict.
+			for(j = tp->provides; j; j = j->next) {
+				PMList *provs = whatprovides(db, j->data);
+				for(k = provs; k; k = k->next) {
+					if(!strcmp(tp->name, k->data->name)) {
+						// this is the same package -- skip it
+						continue;
+					}
+					// we treat this just like a conflict
+					MALLOC(miss, sizeof(pmdepmissing_t));
+					miss->type = CONFLICT;
+					miss->depend.mod = PM_DEP_ANY;
+					miss->depend.version[0] = '\0';
+					strncpy(miss->target, tp->name, 256);
+					strncpy(miss->depend.name, k->data, 256);
+					if(!pm_list_is_in(baddeps, miss)) {
+						baddeps = pm_list_add(baddeps, miss);
+					}
+					k->data = NULL;
+				}
+				pm_list_free(provs);
+			}*/
+
+			/* DEPENDENCIES -- look for unsatisfied dependencies */
+			for(j = tp->depends; j; j = j->next) {
+				/* split into name/version pairs */
+				splitdep((char *)j->data, &depend);
+				found = 0;
+				/* check database for literal packages */
+				for(k = db_get_pkgcache(db); k && !found; k = k->next) {
+					pmpkg_t *p = (pmpkg_t *)k->data;
+					if(!strcmp(p->name, depend.name)) {
+						if(depend.mod == PM_DEP_ANY) {
+							/* accept any version */
+							found = 1;
+						} else {
+							char *ver = strdup(p->version);
+							/* check for a release in depend.version.  if it's
+							 * missing remove it from p->version as well.
+							 */
+							if(!index(depend.version,'-')) {
+								char *ptr;
+								for(ptr = ver; *ptr != '-'; ptr++);
+								*ptr = '\0';
+							}
+							cmp = rpmvercmp(ver, depend.version);
+							switch(depend.mod) {
+								case PM_DEP_EQ: found = (cmp == 0); break;
+								case PM_DEP_GE: found = (cmp >= 0); break;
+								case PM_DEP_LE: found = (cmp <= 0); break;
+							}
+							FREE(ver);
+						}
+					}
+				}
+				/* check other targets */
+				for(k = packages; k && !found; k = k->next) {
+					pmpkg_t *p = (pmpkg_t *)k->data;
+					/* see if the package names match OR if p provides depend.name */
+					if(!strcmp(p->name, depend.name) || pm_list_is_strin(depend.name, p->provides)) {
+						if(depend.mod == PM_DEP_ANY) {
+							/* accept any version */
+							found = 1;
+						} else {
+							char *ver = strdup(p->version);
+							/* check for a release in depend.version.  if it's
+							 * missing remove it from p->version as well.
+							 */
+							if(!index(depend.version,'-')) {
+								char *ptr;
+								for(ptr = ver; *ptr != '-'; ptr++);
+								*ptr = '\0';
+							}
+							cmp = rpmvercmp(ver, depend.version);
+							switch(depend.mod) {
+								case PM_DEP_EQ: found = (cmp == 0); break;
+								case PM_DEP_GE: found = (cmp >= 0); break;
+								case PM_DEP_LE: found = (cmp <= 0); break;
+							}
+							FREE(ver);
+						}
+					}
+				}
+				/* check database for provides matches */
+				if(!found){
+					PMList *lp;
+					k = _alpm_db_whatprovides(db, depend.name);
+					if(k) {
+						/* grab the first one (there should only really be one, anyway) */
+						pmpkg_t *p = db_scan(db, ((pmpkg_t *)k->data)->name, INFRQ_DESC);
+						if(p == NULL) {
+							/* wtf */
+							fprintf(stderr, "data error: %s supposedly provides %s, but it was not found in db\n",
+								((pmpkg_t *)k->data)->name, depend.name);
+							for(lp = k; lp; lp = lp->next) {
+								lp->data = NULL;
+							}
+							pm_list_free(k);
+							continue;
+						}
+						if(depend.mod == PM_DEP_ANY) {
+							/* accept any version */
+							found = 1;
+						} else {
+							char *ver = strdup(p->version);
+							/* check for a release in depend.version.  if it's
+							 * missing remove it from p->version as well.
+							 */
+							if(!index(depend.version,'-')) {
+								char *ptr;
+								for(ptr = ver; *ptr != '-'; ptr++);
+								*ptr = '\0';
+							}
+							cmp = rpmvercmp(ver, depend.version);
+							switch(depend.mod) {
+								case PM_DEP_EQ: found = (cmp == 0); break;
+								case PM_DEP_GE: found = (cmp >= 0); break;
+								case PM_DEP_LE: found = (cmp <= 0); break;
+							}
+							FREE(ver);
+						}
+					}
+					for(lp = k; lp; lp = lp->next) {
+						lp->data = NULL;
+					}
+					pm_list_free(k);
+				}
+				/* else if still not found... */
+				if(!found) {
+					MALLOC(miss, sizeof(pmdepmissing_t));
+					miss->type = PM_DEP_DEPEND;
+					miss->depend.mod = depend.mod;
+					strncpy(miss->target, tp->name, 256);
+					strncpy(miss->depend.name, depend.name, 256);
+					strncpy(miss->depend.version, depend.version, 64);
+					if(!pm_list_is_ptrin(baddeps, miss)) {
+						baddeps = pm_list_add(baddeps, miss);
+					}
+				}
+			}
+		}
+	} else if(op == PM_TRANS_TYPE_REMOVE) {
+		/* check requiredby fields */
+		for(i = packages; i; i = i->next) {
+			pmpkg_t *tp;
+			if(i->data == NULL) {
+				continue;
+			}
+			tp = (pmpkg_t*)i->data;
+			for(j = tp->requiredby; j; j = j->next) {
+				if(!pm_list_is_strin((char *)j->data, packages)) {
+					MALLOC(miss, sizeof(pmdepmissing_t));
+					miss->type = PM_DEP_REQUIRED;
+					miss->depend.mod = PM_DEP_ANY;
+					miss->depend.version[0] = '\0';
+					strncpy(miss->target, tp->name, 256);
+					strncpy(miss->depend.name, (char *)j->data, 256);
+					if(!pm_list_is_ptrin(baddeps, miss)) {
+						baddeps = pm_list_add(baddeps, miss);
+					}
+				}
+			}
+		}
+	}
+
+	return(baddeps);
+}
+
+void splitdep(char *depstr, pmdepend_t *depend)
+{
+	char *str = NULL;
+	char *ptr = NULL;
+
+	if(depstr == NULL) {
+		return;
+	}
+
+	str = strdup(depstr);
+
+	if((ptr = strstr(str, ">="))) {
+		depend->mod = PM_DEP_GE;
+	} else if((ptr = strstr(str, "<="))) {
+		depend->mod = PM_DEP_LE;
+	} else if((ptr = strstr(str, "="))) {
+		depend->mod = PM_DEP_EQ;
+	} else {
+		/* no version specified - accept any */
+		depend->mod = PM_DEP_ANY;
+		strncpy(depend->name, str, sizeof(depend->name));
+		strncpy(depend->version, "", sizeof(depend->version));
+	}
+
+	if(ptr == NULL) {
+		FREE(str);
+		return;
+	}
+	*ptr = '\0';
+	strncpy(depend->name, str, sizeof(depend->name));
+	ptr++;
+	if(depend->mod != PM_DEP_EQ) {
+		ptr++;
+	}
+	strncpy(depend->version, ptr, sizeof(depend->version));
+	FREE(str);
+
+	return;
+}
+
+/* return a new PMList target list containing all packages in the original
+ * target list, as well as all their un-needed dependencies.  By un-needed,
+ * I mean dependencies that are *only* required for packages in the target
+ * list, so they can be safely removed.  This function is recursive.
+ */
+PMList* removedeps(pmdb_t *db, PMList *targs)
+{
+	PMList *i, *j, *k;
+	PMList *newtargs = targs;
+
+	if(db == NULL) {
+		return(newtargs);
+	}
+
+	for(i = targs; i; i = i->next) {
+		pmpkg_t *pkg = (pmpkg_t*)i->data;
+		for(j = pkg->depends; j; j = j->next) {
+			pmdepend_t depend;
+			pmpkg_t *dep;
+			int needed = 0;
+			splitdep(j->data, &depend);
+			dep = db_scan(db, depend.name, INFRQ_DESC | INFRQ_DEPENDS);
+			if(pkg_isin(dep, targs)) {
+				continue;
+			}
+			/* see if it was explicitly installed */
+			if(dep->reason == PM_PKG_REASON_EXPLICIT) {
+				/* ORE
+				vprint("excluding %s -- explicitly installed\n", dep->name);*/
+				needed = 1;
+			}
+			/* see if other packages need it */
+			for(k = dep->requiredby; k && !needed; k = k->next) {
+				pmpkg_t *dummy = db_scan(db, k->data, INFRQ_DESC);
+				if(!pkg_isin(dummy, targs)) {
+					needed = 1;
+				}
+			}
+			if(!needed) {
+				/* add it to the target list */
+				pkg_free(dep);
+				dep = db_scan(db, depend.name, INFRQ_ALL);
+				newtargs = pm_list_add(newtargs, dep);
+				newtargs = removedeps(db, newtargs);
+			}
+		}
+	}
+
+	return(newtargs);
+}
+
+/* populates *list with packages that need to be installed to satisfy all
+ * dependencies (recursive) for *syncpkg->pkg
+ *
+ * make sure *list and *trail are already initialized
+ */
+int resolvedeps(pmdb_t *local, PMList *databases, pmsync_t *sync, PMList *list, PMList *trail, PMList **data)
+{
+	PMList *i, *j;
+	PMList *targ = NULL;
+	PMList *deps = NULL;
+
+	targ = pm_list_add(targ, sync->spkg);
+	deps = checkdeps(local, PM_TRANS_TYPE_ADD, targ);
+	targ->data = NULL;
+	pm_list_free(targ);
+
+	if(deps == NULL) {
+		return(0);
+	}
+
+	for(i = deps; i; i = i->next) {
+		int found = 0;
+		pmdepmissing_t *miss = i->data;
+
+		/* XXX: conflicts are now treated specially in the _add and _sync functions */
+
+		/*if(miss->type == CONFLICT) {
+			fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target);
+			fprintf(stderr, "       %s conflicts with %s\n", miss->target, miss->depend.name);
+			return(1);
+		} else*/
+
+		if(miss->type == PM_DEP_DEPEND) {
+			pmsync_t *sync = NULL;
+
+			/* find the package in one of the repositories */
+
+			/* check literals */
+			for(j = databases; !sync && j; j = j->next) {
+				PMList *k;
+				pmdb_t *dbs = j->data;
+
+				for(k = db_get_pkgcache(dbs); !sync && k; k = k->next) {
+					pmpkg_t *pkg = k->data;
+
+					if(!strcmp(miss->depend.name, pkg->name)) {
+						sync = sync_new(PM_SYSUPG_DEPEND, NULL, k->data);
+						if(sync == NULL) {
+							pm_errno = PM_ERR_MEMORY;
+							goto error;
+						}
+						/* ORE
+						sync->pkg->reason = PM_PKG_REASON_DEPEND;*/
+					}
+				}
+			}
+
+			/* check provides */
+			/* ORE
+			for(j = databases; !s && j; j = j->next) {
+				PMList *provides;
+
+				provides = _alpm_db_whatprovides(j->data, miss->depend.name);
+				if(provides) {
+					s = sync_new(PM_SYSUPG_DEPEND, NULL, !!!provides->data!!!);
+					if(s == NULL) {
+						pm_errno = PM_ERR_MEMORY;
+						FREELIST(deps);
+						return(-1);
+					}
+					sync->pkg->reason = PM_PKG_REASON_DEPEND;
+				}
+				FREELIST(provides);
+			}*/
+
+			if(sync == NULL) {
+				pmdepmissing_t *m = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t));
+				if(m == NULL) {
+					/* ORE
+					Free memory before leaving */
+					pm_errno = PM_ERR_MEMORY;
+					goto error;
+				}
+				*m = *(pmdepmissing_t *)i->data;
+				*data = pm_list_add(*data, m);
+				continue;
+			}
+
+			if(*data) {
+				/* there is at least an unresolvable dep... so we only
+				 * continue to get the whole list of unresolvable deps */
+				continue;
+			}
+
+			found = 0;
+			for(j = list; j && !found; j = j->next) {
+				pmsync_t *tmp = j->data;
+
+				if(tmp && !strcmp(tmp->spkg->name, sync->spkg->name)) {
+					found = 1;
+				}
+			}
+
+			if(found) {
+				/* this dep is already in the target list */
+				FREE(sync);
+				continue;
+			}
+
+			_alpm_log(PM_LOG_FLOW2, "resolving %s", sync->spkg->name);
+			found = 0;
+			for(j = trail; j; j = j->next) {
+				pmsync_t *tmp = j->data;
+
+				if(tmp && !strcmp(tmp->spkg->name, sync->spkg->name)) {
+					found = 1;
+				}
+			}
+
+			if(!found) {
+				trail = pm_list_add(trail, sync);
+				if(resolvedeps(local, databases, sync, list, trail, data)) {
+					goto error;
+				}
+				_alpm_log(PM_LOG_FLOW2, "adding %s-%s", sync->spkg->name, sync->spkg->version);
+				list = pm_list_add(list, sync);
+			} else {
+				/* cycle detected -- skip it */
+				_alpm_log(PM_LOG_FLOW2, "dependency cycle detected: %s", sync->spkg->name);
+				FREE(sync);
+			}
+		}
+	}
+
+	FREELIST(deps);
+
+	if(*data) {
+		pm_errno = PM_ERR_UNRESOLVABLE_DEPS;
+		return(-1);
+	}
+
+	return(0);
+
+error:
+	FREELIST(deps);
+	return(-1);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h
new file mode 100644
index 00000000..60b3a6d8
--- /dev/null
+++ b/lib/libalpm/deps.h
@@ -0,0 +1,35 @@
+/*
+ *  deps.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_DEPS_H
+#define _ALPM_DEPS_H
+
+#include "db.h"
+#include "sync.h"
+
+PMList *sortbydeps(PMList *targets);
+PMList *checkdeps(pmdb_t *db, unsigned short op, PMList *packages);
+void splitdep(char *depstr, pmdepend_t *depend);
+PMList *removedeps(pmdb_t *db, PMList *targs);
+int resolvedeps(pmdb_t *local, PMList *databases, pmsync_t *sync, PMList *list, PMList *trail, PMList **data);
+
+#endif /* _ALPM_DEPS_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c
new file mode 100644
index 00000000..266368ef
--- /dev/null
+++ b/lib/libalpm/error.c
@@ -0,0 +1,90 @@
+/*
+ *  error.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "alpm.h"
+
+char *alpm_strerror(int err)
+{
+	switch(err) {
+		/* System */
+		case PM_ERR_NOT_A_FILE:
+			return "could not find or read file";
+		/* Interface */
+		case PM_ERR_HANDLE_NULL:
+			return "library not initialized";
+		case PM_ERR_HANDLE_NOT_NULL:
+			return "library already initialized";
+		case PM_ERR_WRONG_ARGS:
+			return "wrong or NULL argument";
+		/* Databases */
+		case PM_ERR_DB_OPEN:
+			return "could not open database";
+		case PM_ERR_DB_CREATE:
+			return "could not create database";
+		case PM_ERR_DB_NULL:
+			return "database not initialized";
+		case PM_ERR_DB_NOT_NULL:
+			return "database already registered";
+		case PM_ERR_DB_NOT_FOUND:
+			return "could not find database";
+		/* Configuration */
+		case PM_ERR_OPT_LOGFILE:
+		case PM_ERR_OPT_DBPATH:
+		case PM_ERR_OPT_SYNCDB:
+		case PM_ERR_OPT_USESYSLOG:
+			return "could not set parameter";
+		/* Transactions */
+		case PM_ERR_TRANS_NULL:
+			return "transaction not initialized";
+		case PM_ERR_TRANS_NOT_NULL:
+			return "transaction already initialized";
+		case PM_ERR_TRANS_DUP_TARGET:
+			return "duplicated target";
+		case PM_ERR_TRANS_INITIALIZED:
+			return "transaction already initialized";
+		case PM_ERR_TRANS_NOT_INITIALIZED:
+			return "transaction not initialized";
+		/* Packages */
+		case PM_ERR_PKG_NOT_FOUND:
+			return "could not find or read package";
+		case PM_ERR_PKG_INVALID:
+			return "invalid or corrupted package";
+		case PM_ERR_PKG_INSTALLED:
+			return "package already installed";
+		case PM_ERR_PKG_CANT_FRESH:
+			return "package not installed or lesser version";
+		case PM_ERR_INVALID_NAME:
+			return "package name is not valid";
+		/* Dependencies */
+		case PM_ERR_UNSATISFIED_DEPS:
+			return "could not satisfy dependencies";
+		case PM_ERR_CONFLICTING_DEPS:
+			return "conflicting dependencies";
+		case PM_ERR_UNRESOLVABLE_DEPS:
+			return "could not resolve dependencies";
+		case PM_ERR_FILE_CONFLICTS:
+			return "conflicting files";
+		default:
+			return "unexpected error";
+	}
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/error.h b/lib/libalpm/error.h
new file mode 100644
index 00000000..f96f2cb4
--- /dev/null
+++ b/lib/libalpm/error.h
@@ -0,0 +1,30 @@
+/*
+ *  error.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_ERROR_H
+#define _ALPM_ERROR_H
+
+#include "alpm.h"
+
+#define PM_RET_ERR(err, ret) do { pm_errno = (err); return(ret); } while(0)
+
+#endif /* _ALPM_ERROR_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/group.c b/lib/libalpm/group.c
new file mode 100644
index 00000000..295be2f3
--- /dev/null
+++ b/lib/libalpm/group.c
@@ -0,0 +1,67 @@
+/*
+ *  group.c
+ *
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+/* pacman */
+#include "util.h"
+#include "group.h"
+
+pmgrp_t *grp_new()
+{
+	pmgrp_t* grp = NULL;
+
+	grp = (pmgrp_t *)malloc(sizeof(pmgrp_t));
+	if(grp == NULL) {
+		return(NULL);
+	}
+
+	grp->name[0] = '\0';
+	grp->packages = NULL;
+
+	return(grp);
+}
+
+void grp_free(pmgrp_t *grp)
+{
+	if(grp == NULL) {
+		return;
+	}
+
+	FREELIST(grp->packages);
+	FREE(grp);
+
+	return;
+}
+
+/* Helper function for sorting groups
+ */
+int grp_cmp(const void *g1, const void *g2)
+{
+	pmgrp_t *grp1 = (pmgrp_t *)g1;
+	pmgrp_t *grp2 = (pmgrp_t *)g2;
+
+	return(strcmp(grp1->name, grp2->name));
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/group.h b/lib/libalpm/group.h
new file mode 100644
index 00000000..a2328e0f
--- /dev/null
+++ b/lib/libalpm/group.h
@@ -0,0 +1,48 @@
+/*
+ *  group.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_GROUP_H
+#define _ALPM_GROUP_H
+
+#include "list.h"
+
+/* Groups structure */
+typedef struct __pmgrp_t {
+	char name[256];
+	PMList *packages; /* List of strings */
+} pmgrp_t;
+
+#define FREEGRP(p) do { if(p) { grp_free(p); p = NULL; } } while(0)
+
+#define FREELISTGRPS(p) do { \
+	PMList *i; \
+	for(i = p; i; i = i->next) { \
+		FREEGRP(i->data); \
+	} \
+	FREELIST(p); \
+} while(0)
+
+pmgrp_t *grp_new();
+void grp_free(pmgrp_t *grp);
+int grp_cmp(const void *g1, const void *g2);
+
+#endif /* _ALPM_GROUP_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
new file mode 100644
index 00000000..281eb96c
--- /dev/null
+++ b/lib/libalpm/handle.c
@@ -0,0 +1,229 @@
+/*
+ *  handle.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <syslog.h>
+/* pacman */
+#include "util.h"
+#include "log.h"
+#include "list.h"
+#include "error.h"
+#include "trans.h"
+#include "alpm.h"
+#include "handle.h"
+
+/* log */
+extern alpm_cb_log __pm_logcb;
+extern unsigned char __pm_logmask;
+
+pmhandle_t *handle_new()
+{
+	pmhandle_t *handle;
+
+	handle = (pmhandle_t *)malloc(sizeof(pmhandle_t));
+	if(handle == NULL) {
+		PM_RET_ERR(PM_ERR_MEMORY, NULL);
+	}
+
+	/* see if we're root or not */
+	handle->uid = geteuid();
+	if(!handle->uid && getenv("FAKEROOTKEY")) {
+		/* fakeroot doesn't count, we're non-root */
+		handle->uid = 99;
+	}
+
+	/* see if we're root or not (fakeroot does not count) */
+	if(getuid() == 0 && !getenv("FAKEROOTKEY")) {
+		handle->access = PM_ACCESS_RW;
+	} else {
+		handle->access = PM_ACCESS_RO;
+	}
+
+	handle->trans = NULL;
+
+	handle->db_local = NULL;
+	handle->dbs_sync = NULL;
+
+	handle->logfd = NULL;
+
+	handle->root = NULL;
+	handle->dbpath = NULL;
+	handle->logfile = NULL;
+	handle->noupgrade = NULL;
+	handle->ignorepkg = NULL;
+	handle->usesyslog = 0;
+
+	return(handle);
+}
+
+int handle_free(pmhandle_t *handle)
+{
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	/* close logfiles */
+	if(handle->logfd) {
+		fclose(handle->logfd);
+		handle->logfd = NULL;
+	}
+	if(handle->usesyslog) {
+		handle->usesyslog = 0;
+		closelog();
+	}
+
+	/* free memory */
+	FREETRANS(handle->trans);
+	FREE(handle->root);
+	FREE(handle->dbpath);
+	FREE(handle->logfile);
+	FREELIST(handle->dbs_sync);
+	FREELIST(handle->noupgrade);
+	FREELIST(handle->ignorepkg);
+	free(handle);
+
+	return(0);
+}
+
+int handle_set_option(pmhandle_t *handle, unsigned char val, unsigned long data)
+{
+	PMList *lp;
+	char str[PATH_MAX];
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	switch(val) {
+		case PM_OPT_DBPATH:
+			if(handle->db_local) {
+				PM_RET_ERR(PM_ERR_DB_NOT_NULL, -1);
+			}
+			for(lp = handle->dbs_sync; lp; lp = lp->next) {
+				if(lp->data) {
+					PM_RET_ERR(PM_ERR_DB_NOT_NULL, -1);
+				}
+			}
+
+			if(handle->trans && handle->trans->state != STATE_IDLE) {
+				PM_RET_ERR(PM_ERR_TRANS_INITIALIZED, -1);
+			}
+
+			strncpy(str, ((char *)data) ? (char *)data : PACDBPATH, PATH_MAX);
+			handle->dbpath = strdup(str);
+			_alpm_log(PM_LOG_FLOW2, "PM_OPT_DBPATH set to '%s'", handle->dbpath);
+		break;
+		case PM_OPT_LOGFILE:
+			if((char *)data == NULL || getuid() != 0) {
+				return(0);
+			}
+			if(handle->logfile) {
+				FREE(handle->logfile);
+			}
+			if(handle->logfd) {
+				if(fclose(handle->logfd) != 0) {
+					handle->logfd = NULL;
+					PM_RET_ERR(PM_ERR_OPT_LOGFILE, -1);
+				}
+				handle->logfd = NULL;
+			}
+			if((handle->logfd = fopen((char *)data, "a")) == NULL) {
+				_alpm_log(PM_LOG_ERROR, "can't open log file %s", (char *)data);
+				PM_RET_ERR(PM_ERR_OPT_LOGFILE, -1);
+			}
+			handle->logfile = strdup((char *)data);
+			_alpm_log(PM_LOG_FLOW2, "PM_OPT_LOGFILE set to '%s'", (char *)data);
+		break;
+		case PM_OPT_NOUPGRADE:
+			if((char *)data && strlen((char *)data) != 0) {
+				handle->noupgrade = pm_list_add(handle->noupgrade, strdup((char *)data));
+				_alpm_log(PM_LOG_FLOW2, "'%s' added to PM_OPT_NOUPGRADE", (char *)data);
+			} else {
+				FREELIST(handle->noupgrade);
+				_alpm_log(PM_LOG_FLOW2, "PM_OPT_NOUPGRADE flushed");
+			}
+		break;
+		case PM_OPT_IGNOREPKG:
+			if((char *)data && strlen((char *)data) != 0) {
+				handle->ignorepkg = pm_list_add(handle->ignorepkg, strdup((char *)data));
+				_alpm_log(PM_LOG_FLOW2, "'%s' added to PM_OPT_IGNOREPKG", (char *)data);
+			} else {
+				FREELIST(handle->ignorepkg);
+				_alpm_log(PM_LOG_FLOW2, "PM_OPT_IGNOREPKG flushed");
+			}
+		break;
+		case PM_OPT_USESYSLOG:
+			if(data != 0 && data != 1) {
+				PM_RET_ERR(PM_ERR_OPT_USESYSLOG, -1);
+			}
+			if(handle->usesyslog == data) {
+				return(0);
+			}
+			if(handle->usesyslog) {
+				closelog();
+			} else {
+				openlog("alpm", 0, LOG_USER);
+			}
+			handle->usesyslog = (unsigned short)data;
+			_alpm_log(PM_LOG_FLOW2, "PM_OPT_USESYSLOG set to '%d'", handle->usesyslog);
+		break;
+		case PM_OPT_LOGCB:
+			__pm_logcb = (alpm_cb_log)data;
+		break;
+		case PM_OPT_LOGMASK:
+			__pm_logmask = (unsigned char)data;
+			_alpm_log(PM_LOG_FLOW2, "PM_OPT_LOGMASK set to '%02x'", (unsigned char)data);
+		break;
+		default:
+			PM_RET_ERR(PM_ERR_WRONG_ARGS, -1);
+	}
+
+	return(0);
+}
+
+int handle_get_option(pmhandle_t *handle, unsigned char val, long *data)
+{
+	/* Sanity checks */
+	ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1));
+
+	switch(val) {
+		case PM_OPT_ROOT:      *data = (long)handle->root; break;
+		case PM_OPT_DBPATH:    *data = (long)handle->dbpath; break;
+		case PM_OPT_LOCALDB:   *data = (long)handle->db_local; break;
+		case PM_OPT_SYNCDB:    *data = (long)handle->dbs_sync; break;
+		case PM_OPT_LOGFILE:   *data = (long)handle->logfile; break;
+		case PM_OPT_NOUPGRADE: *data = (long)handle->noupgrade; break;
+		case PM_OPT_IGNOREPKG: *data = (long)handle->ignorepkg; break;
+		case PM_OPT_USESYSLOG: *data = handle->usesyslog; break;
+		case PM_OPT_LOGCB:     *data = (long)__pm_logcb; break;
+		case PM_OPT_LOGMASK:   *data = __pm_logmask; break;
+		default:
+			PM_RET_ERR(PM_ERR_WRONG_ARGS, -1);
+		break;
+	}
+
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
new file mode 100644
index 00000000..e8f3dbbb
--- /dev/null
+++ b/lib/libalpm/handle.h
@@ -0,0 +1,63 @@
+/*
+ *  handle.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_HANDLE_H
+#define _ALPM_HANDLE_H
+
+#include "list.h"
+#include "db.h"
+#include "trans.h"
+#include "alpm.h"
+
+#define PACROOT   "/"
+#define PACDBPATH "var/lib/pacman"
+#define PACLOCK   "/tmp/pacman.lck"
+
+typedef enum __pmaccess_t {
+	PM_ACCESS_RO,
+	PM_ACCESS_RW
+} pmaccess_t;
+
+typedef struct __pmhandle_t {
+	pmaccess_t access;
+	uid_t uid;
+	pmdb_t *db_local;
+	PMList *dbs_sync; /* List of (pmdb_t *) */
+	FILE *logfd;
+	pmtrans_t *trans;
+	/* parameters */
+	char *root;
+	char *dbpath;
+	char *logfile;
+	PMList *noupgrade; /* List of strings */
+	PMList *ignorepkg; /* List of strings */
+	unsigned char usesyslog;
+} pmhandle_t;
+
+#define FREEHANDLE(p) do { if (p) { handle_free(p); p = NULL; } } while (0)
+
+pmhandle_t *handle_new();
+int handle_free(pmhandle_t *handle);
+int handle_set_option(pmhandle_t *handle, unsigned char val, unsigned long data);
+int handle_get_option(pmhandle_t *handle, unsigned char val, long *data);
+
+#endif /* _ALPM_HANDLE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/list.c b/lib/libalpm/list.c
new file mode 100644
index 00000000..286076d9
--- /dev/null
+++ b/lib/libalpm/list.c
@@ -0,0 +1,210 @@
+/*
+ *  list.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+/* pacman */
+#include "list.h"
+
+PMList* pm_list_new()
+{
+	PMList *list = NULL;
+	
+	list = (PMList *)malloc(sizeof(PMList));
+	if(list == NULL) {
+		return(NULL);
+	}
+	list->data = NULL;
+	list->prev = NULL;
+	list->next = NULL;
+	return(list);
+}
+
+void pm_list_free(PMList *list)
+{
+	if(list == NULL) {
+		return;
+	}
+	if(list->data != NULL) {
+		free(list->data);
+		list->data = NULL;
+	}
+	if(list->next != NULL) {
+		pm_list_free(list->next);
+	}
+	free(list);
+	return;
+}
+
+PMList* pm_list_add(PMList *list, void *data)
+{
+	PMList *ptr, *lp;
+
+	ptr = list;
+	if(ptr == NULL) {
+		ptr = pm_list_new();
+	}
+
+	lp = pm_list_last(ptr);
+	if(lp == ptr && lp->data == NULL) {
+		/* nada */
+	} else {
+		lp->next = pm_list_new();
+		if(lp->next == NULL) {
+			return(NULL);
+		}
+		lp->next->prev = lp;
+		lp = lp->next;
+	}
+	lp->data = data;
+	return(ptr);
+}
+
+/* Add items to a list in sorted order. Use the given comparision func to 
+ * determine order.
+ */
+PMList* pm_list_add_sorted(PMList *list, void *data, pm_fn_cmp fn)
+{
+	PMList *add;
+	PMList *prev = NULL;
+	PMList *iter = list;
+
+	add = pm_list_new();
+	add->data = data;
+
+	/* Find insertion point. */
+	while(iter) {
+		if(fn(add->data, iter->data) <= 0) break;
+		prev = iter;
+		iter = iter->next;
+	}
+
+	/*  Insert node before insertion point. */
+	add->prev = prev;
+	add->next = iter;
+	if(iter != NULL) {
+		 /* Not at end. */
+		iter->prev = add;
+	}
+	if(prev != NULL) {
+		 /* In middle. */
+		prev->next = add;
+	} else {
+		/* Start or empty, new list head. */
+		list = add;
+	}
+
+	return(list);
+}
+
+/* Remove an item in a list. Use the given comparaison function to find the
+ * item.
+ * If found, 'ptr' is set to point to the removed element, so that the caller
+ * can free it. Otherwise, ptr is NULL.
+ * Return the new list (without the removed element).
+ */
+PMList *_alpm_list_remove(PMList *list, void *data, pm_fn_cmp fn, void **ptr)
+{
+	PMList *i = list;
+
+	while(i) {
+		if(fn(data, i->data) == 0) {
+			break;
+		}
+		i = i->next;
+	}
+
+	if(ptr) {
+		*ptr = NULL;
+	}
+
+	if(i) {
+		/* we found a matching item */
+		if(i->next) {
+			i->next->prev = i->prev;
+		}
+		if(i->prev) {
+			i->prev->next = i->next;
+		}
+		if(i == list) {
+			/* The item found is the first in the chain,
+			 * so we move the header to the next element.
+			 */
+			list = list->next;
+		}
+
+		if(ptr) {
+			*ptr = i->data;
+		}
+
+		free(i);
+	}
+
+	return(list);
+}
+
+int pm_list_count(PMList *list)
+{
+	int i;
+	PMList *lp;
+
+	for(lp = list, i = 0; lp; lp = lp->next, i++);
+
+	return(i);
+}
+
+int pm_list_is_ptrin(PMList *haystack, void *needle)
+{
+	PMList *lp;
+
+	for(lp = haystack; lp; lp = lp->next) {
+		if(lp->data == needle) {
+			return(1);
+		}
+	}
+	return(0);
+}
+
+/* Test for existence of a string in a PMList
+ */
+PMList *pm_list_is_strin(char *needle, PMList *haystack)
+{
+	PMList *lp;
+
+	for(lp = haystack; lp; lp = lp->next) {
+		if(lp->data && !strcmp(lp->data, needle)) {
+			return(lp);
+		}
+	}
+	return(NULL);
+}
+
+PMList* pm_list_last(PMList *list)
+{
+	PMList *ptr;
+
+	for(ptr = list; ptr && ptr->next; ptr = ptr->next);
+	return(ptr);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/list.h b/lib/libalpm/list.h
new file mode 100644
index 00000000..c5de46d0
--- /dev/null
+++ b/lib/libalpm/list.h
@@ -0,0 +1,50 @@
+/*
+ *  list.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_LIST_H
+#define _ALPM_LIST_H
+
+/* Chained list struct */
+typedef struct __pmlist_t {
+	void *data;
+	struct __pmlist_t *prev;
+	struct __pmlist_t *next;
+} pmlist_t;
+
+typedef struct __pmlist_t PMList;
+
+#define FREELIST(p) do { if(p) { pm_list_free(p); p = NULL; } } while(0)
+
+/* Sort comparison callback function declaration */
+typedef int (*pm_fn_cmp) (const void *, const void *);
+
+PMList *pm_list_new();
+void pm_list_free(PMList *list);
+PMList *pm_list_add(PMList *list, void *data);
+PMList *pm_list_add_sorted(PMList *list, void *data, pm_fn_cmp fn);
+PMList *_alpm_list_remove(PMList *list, void *data, pm_fn_cmp fn, void **ptr);
+int pm_list_count(PMList *list);
+int pm_list_is_ptrin(PMList *haystack, void *needle);
+PMList *pm_list_is_strin(char *needle, PMList *haystack);
+PMList *pm_list_last(PMList *list);
+
+#endif /* _ALPM_LIST_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/log.c b/lib/libalpm/log.c
new file mode 100644
index 00000000..dd4d34a0
--- /dev/null
+++ b/lib/libalpm/log.c
@@ -0,0 +1,52 @@
+/*
+ *  log.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+/* pacman */
+#include "log.h"
+
+/* Internal library log mechanism */
+
+alpm_cb_log __pm_logcb     = NULL;
+unsigned char __pm_logmask = 0;
+
+void _alpm_log(unsigned char flag, char *fmt, ...)
+{
+	char str[256];
+	va_list args;
+
+	if(__pm_logcb == NULL) {
+		return;
+	}
+
+	if(flag & __pm_logmask) {
+		va_start(args, fmt);
+		vsnprintf(str, 256, fmt, args);
+		va_end(args);
+
+		__pm_logcb(flag, str);
+	}
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/log.h b/lib/libalpm/log.h
new file mode 100644
index 00000000..852202ab
--- /dev/null
+++ b/lib/libalpm/log.h
@@ -0,0 +1,32 @@
+/*
+ *  log.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_LOG_H
+#define _ALPM_LOG_H
+
+typedef void (*alpm_cb_log)(unsigned short, char *);
+
+void _alpm_log(unsigned char flag, char *fmt, ...);
+
+int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...);
+
+#endif /* _ALPM_LOG_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/md5.c b/lib/libalpm/md5.c
new file mode 100644
index 00000000..fcb1611e
--- /dev/null
+++ b/lib/libalpm/md5.c
@@ -0,0 +1,338 @@
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(UINT4 [4], unsigned char [64]);
+static void Encode(unsigned char *, UINT4 *, unsigned int);
+static void Decode(UINT4 *, unsigned char *, unsigned int);
+static void MD5_memcpy(POINTER, POINTER, unsigned int);
+static void MD5_memset(POINTER, int, unsigned int);
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void MD5Init (context)
+MD5_CTX *context;                                        /* context */
+{
+  context->count[0] = context->count[1] = 0;
+  /* Load magic initialization constants.
+*/
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+  operation, processing another message block, and updating the
+  context.
+ */
+void MD5Update (context, input, inputLen)
+MD5_CTX *context;                                        /* context */
+unsigned char *input;                                /* input block */
+unsigned int inputLen;                     /* length of input block */
+{
+  unsigned int i, index, partLen;
+
+  /* Compute number of bytes mod 64 */
+  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+  /* Update number of bits */
+  if ((context->count[0] += ((UINT4)inputLen << 3))
+
+   < ((UINT4)inputLen << 3))
+ context->count[1]++;
+  context->count[1] += ((UINT4)inputLen >> 29);
+
+  partLen = 64 - index;
+
+  /* Transform as many times as possible.
+*/
+  if (inputLen >= partLen) {
+ MD5_memcpy
+   ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+   MD5Transform (context->state, &input[i]);
+
+ index = 0;
+  }
+  else
+ i = 0;
+
+  /* Buffer remaining input */
+  MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+  inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+  the message digest and zeroizing the context.
+ */
+void MD5Final (digest, context)
+unsigned char digest[16];                         /* message digest */
+MD5_CTX *context;                                       /* context */
+{
+  unsigned char bits[8];
+  unsigned int index, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64.
+*/
+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (index < 56) ? (56 - index) : (120 - index);
+  MD5Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD5Update (context, bits, 8);
+
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information.
+*/
+  MD5_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+
+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+  /* Round 4 */
+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information.
+
+*/
+  MD5_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+  a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+  }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+  a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (output, input, len)
+POINTER output;
+POINTER input;
+unsigned int len;
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/md5.h b/lib/libalpm/md5.h
new file mode 100644
index 00000000..e6e4ea64
--- /dev/null
+++ b/lib/libalpm/md5.h
@@ -0,0 +1,51 @@
+/* MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+
+/* MD5 context. */
+typedef struct {
+  UINT4 state[4];                                   /* state (ABCD) */
+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];                         /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
+void MD5Final(unsigned char [16], MD5_CTX *);
+
+char* MDFile(char *);
+void  MDPrint(unsigned char [16]);
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/md5driver.c b/lib/libalpm/md5driver.c
new file mode 100644
index 00000000..30b37051
--- /dev/null
+++ b/lib/libalpm/md5driver.c
@@ -0,0 +1,81 @@
+/* MD5DRIVER.C - taken and modified from MDDRIVER.C (license below)  */
+/*               for use in pacman.                                  */
+/*********************************************************************/ 
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+rights reserved.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* The following makes MD default to MD5 if it has not already been
+  defined with C compiler flags.
+ */
+#define MD MD5
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include "md5.h"
+
+/* Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 1000
+#define TEST_BLOCK_COUNT 1000
+
+#define MD_CTX MD5_CTX
+#define MDInit MD5Init
+#define MDUpdate MD5Update
+#define MDFinal MD5Final
+
+char* MDFile(char *filename)
+{
+	FILE *file;
+	MD_CTX context;
+	int len;
+	unsigned char buffer[1024], digest[16];
+
+	if((file = fopen(filename, "rb")) == NULL) {
+		printf ("%s can't be opened\n", filename);
+	} else {
+		char *ret;
+		int i;
+
+		MDInit(&context);
+		while((len = fread(buffer, 1, 1024, file))) {
+			MDUpdate(&context, buffer, len);
+		}
+		MDFinal(digest, &context);
+		fclose(file);
+		/*printf("MD5 (%s) = ", filename);
+		MDPrint(digest);
+		printf("\n");*/
+
+		ret = (char*)malloc(33);
+		ret[0] = '\0';
+		for(i = 0; i < 16; i++) {
+			sprintf(ret, "%s%02x", ret, digest[i]);
+		}
+
+		return(ret);
+	}
+	return(NULL);
+}
+
+/* Prints a message digest in hexadecimal.
+ */
+void MDPrint(unsigned char digest[16])
+{
+	unsigned int i;
+	for (i = 0; i < 16; i++)
+	printf ("%02x", digest[i]);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
new file mode 100644
index 00000000..f20b88eb
--- /dev/null
+++ b/lib/libalpm/package.c
@@ -0,0 +1,342 @@
+/*
+ *  package.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+#include <libtar.h>
+#include <zlib.h>
+/* pacman */
+#include "log.h"
+#include "util.h"
+#include "error.h"
+#include "list.h"
+#include "package.h"
+
+pmpkg_t *pkg_new()
+{
+	pmpkg_t* pkg = NULL;
+
+	MALLOC(pkg, sizeof(pmpkg_t));
+
+	pkg->name[0]        = '\0';
+	pkg->version[0]     = '\0';
+	pkg->desc[0]        = '\0';
+	pkg->url[0]         = '\0';
+	pkg->license[0]     = '\0';
+	pkg->builddate[0]   = '\0';
+	pkg->installdate[0] = '\0';
+	pkg->packager[0]    = '\0';
+	pkg->md5sum[0]      = '\0';
+	pkg->arch[0]        = '\0';
+	pkg->size           = 0;
+	pkg->scriptlet      = 0;
+	pkg->force          = 0;
+	pkg->reason         = PM_PKG_REASON_EXPLICIT;
+	pkg->requiredby     = NULL;
+	pkg->conflicts      = NULL;
+	pkg->files          = NULL;
+	pkg->backup         = NULL;
+	pkg->depends        = NULL;
+	pkg->groups         = NULL;
+	pkg->provides       = NULL;
+	pkg->replaces       = NULL;
+	/* internal */
+	pkg->origin         = 0;
+	pkg->data           = NULL;
+	pkg->infolevel      = 0;
+
+	return(pkg);
+}
+
+void pkg_free(pmpkg_t *pkg)
+{
+	if(pkg == NULL) {
+		return;
+	}
+
+	FREELIST(pkg->files);
+	FREELIST(pkg->backup);
+	FREELIST(pkg->depends);
+	FREELIST(pkg->conflicts);
+	FREELIST(pkg->requiredby);
+	FREELIST(pkg->groups);
+	FREELIST(pkg->provides);
+	FREELIST(pkg->replaces);
+	if(pkg->origin == PKG_FROM_FILE) {
+		FREE(pkg->data);
+	}
+	free(pkg);
+
+	return;
+}
+
+/* Parses the package description file for the current package
+ *
+ * Returns: 0 on success, 1 on error
+ *
+ */
+static int parse_descfile(char *descfile, pmpkg_t *info, int output)
+{
+	FILE* fp = NULL;
+	char line[PATH_MAX+1];
+	char* ptr = NULL;
+	char* key = NULL;
+	int linenum = 0;
+
+	if((fp = fopen(descfile, "r")) == NULL) {
+		_alpm_log(PM_LOG_ERROR, "could not open file %s", descfile);
+		return(-1);
+	}
+
+	while(!feof(fp)) {
+		fgets(line, PATH_MAX, fp);
+		linenum++;
+		_alpm_strtrim(line);
+		if(strlen(line) == 0 || line[0] == '#') {
+			continue;
+		}
+		if(output) {
+			printf("%s\n", line);
+		}
+		ptr = line;
+		key = strsep(&ptr, "=");
+		if(key == NULL || ptr == NULL) {
+			fprintf(stderr, "%s: syntax error in description file line %d\n",
+				info->name[0] != '\0' ? info->name : "error", linenum);
+		} else {
+			_alpm_strtrim(key);
+			key = _alpm_strtoupper(key);
+			_alpm_strtrim(ptr);
+			if(!strcmp(key, "PKGNAME")) {
+				strncpy(info->name, ptr, sizeof(info->name));
+			} else if(!strcmp(key, "PKGVER")) {
+				strncpy(info->version, ptr, sizeof(info->version));
+			} else if(!strcmp(key, "PKGDESC")) {
+				strncpy(info->desc, ptr, sizeof(info->desc));
+			} else if(!strcmp(key, "GROUP")) {
+				info->groups = pm_list_add(info->groups, strdup(ptr));
+			} else if(!strcmp(key, "URL")) {
+				strncpy(info->url, ptr, sizeof(info->url));
+			} else if(!strcmp(key, "LICENSE")) {
+				strncpy(info->license, ptr, sizeof(info->license));
+			} else if(!strcmp(key, "BUILDDATE")) {
+				strncpy(info->builddate, ptr, sizeof(info->builddate));
+			} else if(!strcmp(key, "INSTALLDATE")) {
+				strncpy(info->installdate, ptr, sizeof(info->installdate));
+			} else if(!strcmp(key, "PACKAGER")) {
+				strncpy(info->packager, ptr, sizeof(info->packager));
+			} else if(!strcmp(key, "ARCH")) {
+				strncpy(info->arch, ptr, sizeof(info->arch));
+			} else if(!strcmp(key, "SIZE")) {
+				char tmp[32];
+				strncpy(tmp, ptr, sizeof(tmp));
+				info->size = atol(tmp);
+			} else if(!strcmp(key, "DEPEND")) {
+				info->depends = pm_list_add(info->depends, strdup(ptr));
+			} else if(!strcmp(key, "CONFLICT")) {
+				info->conflicts = pm_list_add(info->conflicts, strdup(ptr));
+			} else if(!strcmp(key, "REPLACES")) {
+				info->replaces = pm_list_add(info->replaces, strdup(ptr));
+			} else if(!strcmp(key, "PROVIDES")) {
+				info->provides = pm_list_add(info->provides, strdup(ptr));
+			} else if(!strcmp(key, "BACKUP")) {
+				info->backup = pm_list_add(info->backup, strdup(ptr));
+			} else {
+				fprintf(stderr, "%s: syntax error in description file line %d\n",
+					info->name[0] != '\0' ? info->name : "error", linenum);
+			}
+		}
+		line[0] = '\0';
+	}
+	fclose(fp);
+	unlink(descfile);
+
+	return(0);
+}
+
+pmpkg_t *pkg_load(char *pkgfile)
+{
+	char *expath;
+	int i;
+	int config = 0;
+	int filelist = 0;
+	int scriptcheck = 0;
+	TAR *tar;
+	pmpkg_t *info = NULL;
+	tartype_t gztype = {
+		(openfunc_t)_alpm_gzopen_frontend,
+		(closefunc_t)gzclose,
+		(readfunc_t)gzread,
+		(writefunc_t)gzwrite
+	};
+
+	if(pkgfile == NULL) {
+		PM_RET_ERR(PM_ERR_WRONG_ARGS, NULL);
+	}
+
+	if(tar_open(&tar, pkgfile, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
+		PM_RET_ERR(PM_ERR_NOT_A_FILE, NULL);
+	}
+
+	info = pkg_new();
+	if(info == NULL) {
+		tar_close(tar);
+		PM_RET_ERR(PM_ERR_MEMORY, NULL);
+	}
+
+	for(i = 0; !th_read(tar); i++) {
+		if(config && filelist && scriptcheck) {
+			/* we have everything we need */
+			break;
+		}
+		if(!strcmp(th_get_pathname(tar), ".PKGINFO")) {
+			char *descfile;
+
+			/* extract this file into /tmp. it has info for us */
+			descfile = strdup("/tmp/pacman_XXXXXX");
+			mkstemp(descfile);
+			tar_extract_file(tar, descfile);
+			/* parse the info file */
+			if(parse_descfile(descfile, info, 0) == -1) {
+				goto error;
+			}
+			if(!strlen(info->name)) {
+				_alpm_log(PM_LOG_ERROR, "missing package name in %s", pkgfile);
+				goto error;
+			}
+			if(!strlen(info->version)) {
+				_alpm_log(PM_LOG_ERROR, "missing package version in %s", pkgfile);
+				goto error;
+			}
+			config = 1;
+			FREE(descfile);
+			continue;
+		} else if(!strcmp(th_get_pathname(tar), "._install") || !strcmp(th_get_pathname(tar), ".INSTALL")) {
+			info->scriptlet = 1;
+			scriptcheck = 1;
+		} else if(!strcmp(th_get_pathname(tar), ".FILELIST")) {
+			/* Build info->files from the filelist */
+			FILE *fp;
+			char *fn;
+			char *str;
+			
+			MALLOC(str, PATH_MAX);
+			fn = strdup("/tmp/pacman_XXXXXX");
+			mkstemp(fn);
+			tar_extract_file(tar, fn);
+			fp = fopen(fn, "r");
+			while(!feof(fp)) {
+				if(fgets(str, PATH_MAX, fp) == NULL) {
+					continue;
+				}
+				_alpm_strtrim(str);
+				info->files = pm_list_add(info->files, strdup(str));
+			}
+			FREE(str);
+			fclose(fp);
+			if(unlink(fn)) {
+				_alpm_log(PM_LOG_WARNING, "could not remove tempfile %s\n", fn);
+			}
+			FREE(fn);
+			filelist = 1;
+			continue;
+		} else {
+			scriptcheck = 1;
+			if(!filelist) {
+				/* no .FILELIST present in this package..  build the filelist the */
+				/* old-fashioned way, one at a time */
+				expath = strdup(th_get_pathname(tar));
+				info->files = pm_list_add(info->files, expath);
+			}
+		}
+
+		if(TH_ISREG(tar) && tar_skip_regfile(tar)) {
+			_alpm_log(PM_LOG_ERROR, "bad package file in %s", pkgfile);
+			goto error;
+		}
+		expath = NULL;
+	}
+	tar_close(tar);
+
+	if(!config) {
+		_alpm_log(PM_LOG_ERROR, "missing package info file in %s", pkgfile);
+		goto error;
+	}
+
+	/* internal */
+	info->origin = PKG_FROM_FILE;
+	info->data = strdup(pkgfile);
+	info->infolevel = 0xFF;
+
+	return(info);
+
+error:
+	printf("toto\n");
+
+	FREEPKG(info);
+	tar_close(tar);
+
+	return(NULL);
+}
+
+/* Helper function for sorting packages
+ */
+int pkg_cmp(const void *p1, const void *p2)
+{
+	pmpkg_t *pkg1 = (pmpkg_t *)p1;
+	pmpkg_t *pkg2 = (pmpkg_t *)p2;
+
+	return(strcmp(pkg1->name, pkg2->name));
+}
+
+/* Test for existence of a package in a PMList*
+ * of pmpkg_t*
+ *
+ * returns:  0 for no match
+ *           1 for identical match
+ *          -1 for name-only match (version mismatch)
+ */
+int pkg_isin(pmpkg_t *needle, PMList *haystack)
+{
+	PMList *lp;
+
+	if(needle == NULL || haystack == NULL) {
+		return(0);
+	}
+
+	for(lp = haystack; lp; lp = lp->next) {
+		pmpkg_t *info = lp->data;
+
+		if(info && !strcmp(info->name, needle->name)) {
+			if(!strcmp(info->version, needle->version)) {
+				return(1);
+			}
+			return(-1);
+		}
+	}
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h
new file mode 100644
index 00000000..890bbccc
--- /dev/null
+++ b/lib/libalpm/package.h
@@ -0,0 +1,78 @@
+/*
+ *  package.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_PACKAGE_H
+#define _ALPM_PACKAGE_H
+
+#include "list.h"
+
+#define PKG_FROM_CACHE 1
+#define PKG_FROM_FILE  2
+
+typedef struct __pmpkg_t {
+	char name[256];
+	char version[64];
+	char desc[512];
+	char url[256];
+	char license[128];
+	char builddate[32];
+	char installdate[32];
+	char packager[64];
+	char md5sum[33];
+	char arch[32];
+	unsigned long size;
+	unsigned char scriptlet;
+	unsigned char force;
+	unsigned char reason;
+	PMList *replaces;
+	PMList *groups;
+	PMList *files;
+	PMList *backup;
+	PMList *depends;
+	PMList *requiredby;
+	PMList *conflicts;
+	PMList *provides;
+	/* internal */
+	unsigned char origin;
+	void *data;
+	unsigned char infolevel;
+} pmpkg_t;
+
+#define FREEPKG(p) do { if(p) { pkg_free(p); p = NULL; } } while(0)
+
+#define FREELISTPKGS(p) do {\
+	if(p) { \
+		PMList *i;\
+		for(i = p; i; i = i->next) {\
+			FREEPKG(i->data); \
+		}\
+		FREELIST(p);\
+	} \
+} while(0)
+
+pmpkg_t* pkg_new();
+void pkg_free(pmpkg_t *pkg);
+pmpkg_t *pkg_load(char *pkgfile);
+int pkg_cmp(const void *p1, const void *p2);
+int pkg_isin(pmpkg_t *needle, PMList *haystack);
+
+#endif /* _ALPM_PACKAGE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/provide.c b/lib/libalpm/provide.c
new file mode 100644
index 00000000..a472c7af
--- /dev/null
+++ b/lib/libalpm/provide.c
@@ -0,0 +1,53 @@
+/*
+ *  provide.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+/* pacman */
+#include "cache.h"
+#include "list.h"
+#include "db.h"
+#include "alpm.h"
+
+/* return a PMList of packages in "db" that provide "package"
+ */
+PMList *_alpm_db_whatprovides(pmdb_t *db, char *package)
+{
+	PMList *pkgs = NULL;
+	PMList *lp;
+
+	if(db == NULL || package == NULL || strlen(package) == 0) {
+		return(NULL);
+	}
+
+	for(lp = db_get_pkgcache(db); lp; lp = lp->next) {
+		pmpkg_t *info = lp->data;
+
+		if(pm_list_is_strin(package, info->provides)) {
+			pkgs = pm_list_add(pkgs, info);
+		}
+	}
+
+	return(pkgs);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/provide.h b/lib/libalpm/provide.h
new file mode 100644
index 00000000..c8c84367
--- /dev/null
+++ b/lib/libalpm/provide.h
@@ -0,0 +1,33 @@
+/*
+ *  provide.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_PROVIDE_H
+#define _ALPM_PROVIDE_H
+
+#include "config.h"
+
+#include "list.h"
+#include "db.h"
+
+PMList *_alpm_db_whatprovides(pmdb_t *db, char *package);
+
+#endif /* _ALPM_PROVIDE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c
new file mode 100644
index 00000000..c53ffa4d
--- /dev/null
+++ b/lib/libalpm/remove.c
@@ -0,0 +1,259 @@
+/*
+ *  remove.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <zlib.h>
+#include <libtar.h>
+/* pacman */
+#include "util.h"
+#include "error.h"
+#include "rpmvercmp.h"
+#include "md5.h"
+#include "log.h"
+#include "backup.h"
+#include "package.h"
+#include "db.h"
+#include "cache.h"
+#include "deps.h"
+#include "provide.h"
+#include "remove.h"
+#include "handle.h"
+#include "alpm.h"
+
+extern pmhandle_t *handle;
+
+int remove_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name)
+{
+	pmpkg_t *info;
+
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1));
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(name != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	if((info = db_scan(db, name, INFRQ_ALL)) == NULL) {
+		_alpm_log(PM_LOG_ERROR, "could not find %s in database", name);
+		PM_RET_ERR(PM_ERR_PKG_NOT_FOUND, -1);
+	}
+	trans->packages = pm_list_add(trans->packages, info);
+
+	return(0);
+}
+
+int remove_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data)
+{
+	pmpkg_t *info;
+	PMList *lp;
+
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1));
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	if(!(trans->flags & (PM_TRANS_FLAG_NODEPS)) && (trans->type != PM_TRANS_TYPE_UPGRADE)) {
+		TRANS_CB(trans, PM_TRANS_CB_DEPS_START, NULL, NULL);
+
+		if((lp = checkdeps(db, trans->type, trans->packages)) != NULL) {
+			if(trans->flags & PM_TRANS_FLAG_CASCADE) {
+				while(lp) {
+					PMList *j;
+					for(j = lp; j; j = j->next) {
+						pmdepmissing_t* miss = (pmdepmissing_t*)j->data;
+						info = db_scan(db, miss->depend.name, INFRQ_ALL);
+						if(!pkg_isin(info, trans->packages)) {
+							trans->packages = pm_list_add(trans->packages, info);
+						}
+					}
+					FREELIST(lp);
+					lp = checkdeps(db, trans->type, trans->packages);
+				}
+			} else {
+				*data = lp;
+				PM_RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1);
+			}
+		}
+
+		if(trans->flags & PM_TRANS_FLAG_RECURSE) {
+			_alpm_log(PM_LOG_FLOW1, "finding removable dependencies...");
+			trans->packages = removedeps(db, trans->packages);
+		}
+
+		TRANS_CB(trans, PM_TRANS_CB_DEPS_DONE, NULL, NULL);
+	}
+
+	return(0);
+}
+
+int remove_commit(pmdb_t *db, pmtrans_t *trans)
+{
+	pmpkg_t *info;
+	struct stat buf;
+	PMList *targ, *lp;
+	char line[PATH_MAX+1];
+
+	ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1));
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+
+	for(targ = trans->packages; targ; targ = targ->next) {
+		char pm_install[PATH_MAX];
+		info = (pmpkg_t*)targ->data;
+
+		if(trans->type != PM_TRANS_TYPE_UPGRADE) {
+			TRANS_CB(trans, PM_TRANS_CB_REMOVE_START, info, NULL);
+
+			/* run the pre-remove scriptlet if it exists  */
+			snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version);
+			_alpm_runscriptlet(handle->root, pm_install, "pre_remove", info->version, NULL);
+		}
+
+		if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) {
+			/* iterate through the list backwards, unlinking files */
+			for(lp = pm_list_last(info->files); lp; lp = lp->prev) {
+				char *newpath = NULL;
+				int nb = 0;
+				if(_alpm_needbackup(lp->data, info->backup)) {
+					nb = 1;
+				}
+				if(!nb && trans->type == PM_TRANS_TYPE_UPGRADE) {
+					/* check noupgrade */
+					if(pm_list_is_strin(lp->data, handle->noupgrade)) {
+						nb = 1;
+					}
+				}
+				snprintf(line, PATH_MAX, "%s%s", handle->root, (char*)lp->data);
+				if(lstat(line, &buf)) {
+					_alpm_log(PM_LOG_ERROR, "file %s does not exist", line);
+					continue;
+				}
+				if(S_ISDIR(buf.st_mode)) {
+					_alpm_log(PM_LOG_DEBUG, "removing directory %s", line);
+					if(rmdir(line)) {
+						/* this is okay, other packages are probably using it. */
+					}
+				} else {
+					/* if the file is flagged, back it up to .pacsave */
+					if(nb) {
+						if(trans->type == PM_TRANS_TYPE_UPGRADE) {
+							/* we're upgrading so just leave the file as is.  pacman_add() will handle it */
+						} else {
+							if(!(trans->flags & PM_TRANS_FLAG_NOSAVE)) {
+								newpath = (char*)realloc(newpath, strlen(line)+strlen(".pacsave")+1);
+								sprintf(newpath, "%s.pacsave", line);
+								rename(line, newpath);
+								_alpm_log(PM_LOG_WARNING, "%s saved as %s", line, newpath);
+								alpm_logaction("%s saved as %s", line, newpath);
+							} else {
+								_alpm_log(PM_LOG_DEBUG, "unlinking %s", line);
+								if(unlink(line)) {
+									_alpm_log(PM_LOG_ERROR, "cannot remove file %s", line);
+								}
+							}
+						}
+					} else {
+						_alpm_log(PM_LOG_DEBUG, "unlinking %s", line);
+						if(unlink(line)) {
+							_alpm_log(PM_LOG_ERROR, "cannot remove file %s", line);
+						}
+					}
+				}
+			}
+		}
+
+		if(trans->type != PM_TRANS_TYPE_UPGRADE) {
+			char pm_install[PATH_MAX];
+
+			/* run the post-remove script if it exists  */
+			snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version);
+			_alpm_runscriptlet(handle->root, pm_install, "post_remove", info->version, NULL);
+		}
+
+		/* remove the package from the database */
+		if(db_remove(db, info) == -1) {
+			_alpm_log(PM_LOG_ERROR, "failed to remove database entry %s/%s-%s", db->path, info->name, info->version);
+		}
+
+		/* update dependency packages' REQUIREDBY fields */
+		for(lp = info->depends; lp; lp = lp->next) {
+			PMList *last, *j;
+			pmpkg_t *depinfo = NULL;
+			pmdepend_t depend;
+
+			splitdep((char*)lp->data, &depend);
+
+			depinfo = db_scan(db, depend.name, INFRQ_DESC|INFRQ_DEPENDS);
+			if(depinfo == NULL) {
+				/* look for a provides package */
+				PMList *provides = _alpm_db_whatprovides(db, depend.name);
+				if(provides) {
+					/* TODO: should check _all_ packages listed in provides, not just
+					 *       the first one.
+					 */
+					/* use the first one */
+					depinfo = db_scan(db, provides->data, INFRQ_DEPENDS);
+					FREELIST(provides);
+					if(depinfo == NULL) {
+						/* wtf */
+						continue;
+					}
+				} else {
+					continue;
+				}
+			}
+			/* splice out this entry from requiredby */
+			last = pm_list_last(depinfo->requiredby);
+			/* ORE - use list_remove here? */
+			for(j = depinfo->requiredby; j; j = j->next) {
+				if(!strcmp((char*)j->data, info->name)) {
+					if(j == depinfo->requiredby) {
+						depinfo->requiredby = j->next;
+					}
+					if(j->prev)	j->prev->next = j->next;
+					if(j->next)	j->next->prev = j->prev;
+					/* free the spliced node */
+					j->prev = j->next = NULL;
+					FREELIST(j);
+					break;
+				}
+			}
+			db_write(db, depinfo, INFRQ_DEPENDS);
+			FREEPKG(depinfo);
+		}
+
+		if(trans->type != PM_TRANS_TYPE_UPGRADE) {
+			TRANS_CB(trans, PM_TRANS_CB_REMOVE_DONE, info, NULL);
+			alpm_logaction("removed %s (%s)", info->name, info->version);
+		}
+	}
+
+	/* run ldconfig if it exists */
+	_alpm_log(PM_LOG_FLOW2, "running \"%ssbin/ldconfig -r %s\"", handle->root, handle->root);
+	_alpm_ldconfig(handle->root);
+
+	/* cache needs to be rebuilt */
+	db_free_pkgcache(db);
+
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/remove.h b/lib/libalpm/remove.h
new file mode 100644
index 00000000..2d5cec1b
--- /dev/null
+++ b/lib/libalpm/remove.h
@@ -0,0 +1,34 @@
+/*
+ *  remove.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_REMOVE_H
+#define _ALPM_REMOVE_H
+
+#include "list.h"
+#include "db.h"
+#include "trans.h"
+
+int remove_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name);
+int remove_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data);
+int remove_commit(pmdb_t *db, pmtrans_t *trans);
+
+#endif /* _ALPM_REMOVE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/rpmvercmp.c b/lib/libalpm/rpmvercmp.c
new file mode 100644
index 00000000..bbabc2b4
--- /dev/null
+++ b/lib/libalpm/rpmvercmp.c
@@ -0,0 +1,237 @@
+/*
+ *  rpmvercmp.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+/* pacman */
+#include "rpmvercmp.h"
+
+/* this function was taken from rpm 4.0.4 and rewritten */
+int rpmvercmp(const char *a, const char *b)
+{
+	char *str1, *str2;
+	char *one, *two;
+	char *rel1 = NULL, *rel2 = NULL;
+	char oldch1, oldch2;
+	int is1num, is2num;
+	int rc;
+
+	if(!strcmp(a,b)) {
+		return(0);
+	}
+
+	str1 = strdup(a);
+	str2 = strdup(b);
+
+	/* lose the release number */
+	for(one = str1; *one && *one != '-'; one++);
+	if(one) {
+		*one = '\0';
+		rel1 = ++one;
+	}
+	for(two = str2; *two && *two != '-'; two++);
+	if(two) {
+		*two = '\0';
+		rel2 = ++two;
+	}
+
+	one = str1;
+	two = str2;
+
+	while(*one || *two) {
+		while(*one && !isalnum(*one)) one++;
+		while(*two && !isalnum(*two)) two++;
+
+		str1 = one;
+		str2 = two;
+
+		/* find the next segment for each string */
+		if(isdigit(*str1)) {
+			is1num = 1;
+			while(*str1 && isdigit(*str1)) str1++;
+		} else {
+			is1num = 0;
+			while(*str1 && isalpha(*str1)) str1++;
+		}
+		if(isdigit(*str2)) {
+			is2num = 1;
+			while(*str2 && isdigit(*str2)) str2++;
+		} else {
+			is2num = 0;
+			while(*str2 && isalpha(*str2)) str2++;
+		}
+
+		oldch1 = *str1;
+		*str1 = '\0';
+		oldch2 = *str2;
+		*str2 = '\0';
+
+		/* see if we ran out of segments on one string */
+		if(one == str1 && two != str2) {
+			return(is2num ? -1 : 1);
+		}
+		if(one != str1 && two == str2) {
+			return(is1num ? 1 : -1);
+		}
+
+		/* see if we have a type mismatch (ie, one is alpha and one is digits) */
+		if(is1num && !is2num) return(1);
+		if(!is1num && is2num) return(-1);
+
+		if(is1num) while(*one == '0') one++;
+		if(is2num) while(*two == '0') two++;
+
+		rc = strverscmp(one, two);
+		if(rc) return(rc);
+
+		*str1 = oldch1;
+		*str2 = oldch2;
+		one = str1;
+		two = str2;
+	}
+
+	if((!*one) && (!*two)) {
+		/* compare release numbers */
+		if(rel1 && rel2) return(rpmvercmp(rel1, rel2));
+		return(0);
+	}
+
+	return(*one ? 1 : -1);
+}
+
+#ifndef HAVE_STRVERSCMP
+
+/* GNU's strverscmp() function, taken from glibc 2.3.2 sources
+ */
+
+/* Compare strings while treating digits characters numerically.
+   Copyright (C) 1997, 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 1997.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* states: S_N: normal, S_I: comparing integral part, S_F: comparing
+           fractionnal parts, S_Z: idem but with leading Zeroes only */
+#define  S_N    0x0
+#define  S_I    0x4
+#define  S_F    0x8
+#define  S_Z    0xC
+
+/* result_type: CMP: return diff; LEN: compare using len_diff/diff */
+#define  CMP    2
+#define  LEN    3
+
+
+/* Compare S1 and S2 as strings holding indices/version numbers,
+   returning less than, equal to or greater than zero if S1 is less than,
+   equal to or greater than S2 (for more info, see the texinfo doc).
+*/
+
+int strverscmp (s1, s2)
+     const char *s1;
+     const char *s2;
+{
+  const unsigned char *p1 = (const unsigned char *) s1;
+  const unsigned char *p2 = (const unsigned char *) s2;
+  unsigned char c1, c2;
+  int state;
+  int diff;
+
+  /* Symbol(s)    0       [1-9]   others  (padding)
+     Transition   (10) 0  (01) d  (00) x  (11) -   */
+  static const unsigned int next_state[] =
+  {
+      /* state    x    d    0    - */
+      /* S_N */  S_N, S_I, S_Z, S_N,
+      /* S_I */  S_N, S_I, S_I, S_I,
+      /* S_F */  S_N, S_F, S_F, S_F,
+      /* S_Z */  S_N, S_F, S_Z, S_Z
+  };
+
+  static const int result_type[] =
+  {
+      /* state   x/x  x/d  x/0  x/-  d/x  d/d  d/0  d/-
+                 0/x  0/d  0/0  0/-  -/x  -/d  -/0  -/- */
+
+      /* S_N */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
+                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
+      /* S_I */  CMP, -1,  -1,  CMP, +1,  LEN, LEN, CMP,
+                 +1,  LEN, LEN, CMP, CMP, CMP, CMP, CMP,
+      /* S_F */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
+                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
+      /* S_Z */  CMP, +1,  +1,  CMP, -1,  CMP, CMP, CMP,
+                 -1,  CMP, CMP, CMP
+  };
+
+  if (p1 == p2)
+    return 0;
+
+  c1 = *p1++;
+  c2 = *p2++;
+  /* Hint: '0' is a digit too.  */
+  state = S_N | ((c1 == '0') + (isdigit (c1) != 0));
+
+  while ((diff = c1 - c2) == 0 && c1 != '\0')
+    {
+      state = next_state[state];
+      c1 = *p1++;
+      c2 = *p2++;
+      state |= (c1 == '0') + (isdigit (c1) != 0);
+    }
+
+  state = result_type[state << 2 | (((c2 == '0') + (isdigit (c2) != 0)))];
+
+  switch (state)
+  {
+    case CMP:
+      return diff;
+
+    case LEN:
+      while (isdigit (*p1++))
+	if (!isdigit (*p2++))
+	  return 1;
+
+      return isdigit (*p2) ? -1 : diff;
+
+    default:
+      return state;
+  }
+}
+
+#endif
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/rpmvercmp.h b/lib/libalpm/rpmvercmp.h
new file mode 100644
index 00000000..c42c0fc7
--- /dev/null
+++ b/lib/libalpm/rpmvercmp.h
@@ -0,0 +1,32 @@
+/*
+ *  rpmvercmp.h
+ * 
+ *  Copyright (c) 2002 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _PM_RPMVERCMP_H
+#define _PM_RPMVERCMP_H
+
+int rpmvercmp(const char *a, const char *b);
+
+#ifndef HAVE_STRVERSCMP
+int strverscmp(const char *s1, const char *s2);
+#endif
+
+#endif
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c
new file mode 100644
index 00000000..8a049c74
--- /dev/null
+++ b/lib/libalpm/sync.c
@@ -0,0 +1,248 @@
+/*
+ *  sync.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+/* pacman */
+#include "log.h"
+#include "util.h"
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "cache.h"
+#include "deps.h"
+#include "trans.h"
+#include "sync.h"
+#include "rpmvercmp.h"
+#include "handle.h"
+
+extern pmhandle_t *handle;
+
+pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg)
+{
+	pmsync_t *sync;
+
+	if((sync = (pmsync_t *)malloc(sizeof(pmsync_t))) == NULL) {
+		return(NULL);
+	}
+
+	sync->type = type;
+	sync->lpkg = lpkg;
+	sync->spkg = spkg;
+	
+	return(sync);
+}
+
+int sync_sysupgrade(PMList **data)
+{
+	PMList *i, *j, *k;
+	PMList *targets = NULL;
+
+	*data = NULL;
+
+	/* check for "recommended" package replacements */
+	for(i = handle->dbs_sync; i; i = i->next) {
+		PMList *j;
+
+		for(j = db_get_pkgcache(i->data); j; j = j->next) {
+			pmpkg_t *spkg = j->data;
+
+			for(k = spkg->replaces; k; k = k->next) {
+				PMList *m;
+
+				for(m = db_get_pkgcache(handle->db_local); m; m = m->next) {
+					pmpkg_t *lpkg = m->data;
+
+					if(!strcmp(k->data, lpkg->name)) {
+						if(pm_list_is_strin(lpkg->name, handle->ignorepkg)) {
+							_alpm_log(PM_LOG_WARNING, "%s-%s: ignoring package upgrade (to be replaced by %s-%s)",
+								lpkg->name, lpkg->version, spkg->name, spkg->version);
+						} else {
+							pmsync_t *sync = sync_new(PM_SYSUPG_REPLACE, lpkg, spkg);
+
+							if(sync == NULL) {
+								pm_errno = PM_ERR_MEMORY;
+								goto error;
+							}
+
+							targets = pm_list_add(targets, sync);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/* match installed packages with the sync dbs and compare versions */
+	for(i = db_get_pkgcache(handle->db_local); i; i = i->next) {
+		int cmp;
+		pmpkg_t *local = i->data;
+		pmpkg_t *spkg = NULL;
+		pmsync_t *sync;
+
+		for(j = handle->dbs_sync; !spkg && j; j = j->next) {
+
+			for(k = db_get_pkgcache(j->data); !spkg && k; k = k->next) {
+				pmpkg_t *sp = k->data;
+
+				if(!strcmp(local->name, sp->name)) {
+					spkg = sp;
+				}
+			}
+		}
+		if(spkg == NULL) {
+			/*fprintf(stderr, "%s: not found in sync db.  skipping.", local->name);*/
+			continue;
+		}
+
+		/* compare versions and see if we need to upgrade */
+		cmp = rpmvercmp(local->version, spkg->version);
+		if(cmp > 0 && !spkg->force) {
+			/* local version is newer */
+			_alpm_log(PM_LOG_FLOW1, "%s-%s: local version is newer",
+				local->name, local->version);
+			continue;
+		} else if(cmp == 0) {
+			/* versions are identical */
+			continue;
+		} else if(pm_list_is_strin(i->data, handle->ignorepkg)) {
+			/* package should be ignored (IgnorePkg) */
+			_alpm_log(PM_LOG_FLOW1, "%s-%s: ignoring package upgrade (%s)",
+				local->name, local->version, spkg->version);
+			continue;
+		}
+
+		sync = sync_new(PM_SYSUPG_UPGRADE, local, spkg);
+		if(sync == NULL) {
+			pm_errno = PM_ERR_MEMORY;
+			goto error;
+		}
+
+		targets = pm_list_add(targets, sync);
+	}
+
+	*data = targets;
+
+	return(0);
+
+error:
+	FREELIST(targets);
+	return(-1);
+}
+
+int sync_resolvedeps(PMList **syncs)
+{
+	return(0);
+}
+
+int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data)
+{
+	PMList *i;
+	PMList *trail = NULL;
+
+	/* Resolve targets dependencies */
+	for(i = trans->targets; i; i = i->next) {
+		if(resolvedeps(handle->db_local, handle->dbs_sync, i->data, trans->targets, trail, data) == -1) {
+			/* pm_errno is set by resolvedeps */
+			goto error;
+		}
+	}
+
+	/* ORE
+	check for inter-conflicts and whatnot */
+
+	/* ORE
+	any packages in rmtargs need to be removed from final.
+	rather than ripping out nodes from final, we just copy over
+	our "good" nodes to a new list and reassign. */
+
+	/* ORE
+	Check dependencies of packages in rmtargs and make sure
+	we won't be breaking anything by removing them.
+	If a broken dep is detected, make sure it's not from a
+	package that's in our final (upgrade) list. */
+
+	return(0);
+
+error:
+	return(-1);
+}
+
+int sync_commit(pmdb_t *db, pmtrans_t *trans)
+{
+	PMList *i, *files = NULL;
+	PMList *final = NULL;
+	PMList *data;
+	pmtrans_t *tr;
+
+	/* remove any conflicting packages (WITHOUT dep checks) */
+
+	/* remove to-be-replaced packages */
+
+	/* install targets */
+	tr = trans_new(PM_TRANS_TYPE_UPGRADE, 0);
+	for(i = files; i; i = i->next) {
+		trans_addtarget(tr, i->data);
+	}
+
+	trans_prepare(tr, &data);
+
+	trans_commit(tr);
+
+	trans_free(tr);
+
+	/* propagate replaced packages' requiredby fields to their new owners */
+	for(i = final; i; i = i->next) {
+		/*syncpkg_t *sync = (syncpkg_t*)i->data;
+		if(sync->replaces) {
+			pkginfo_t *new = db_scan(db, sync->pkg->name, INFRQ_DEPENDS);
+			for(j = sync->replaces; j; j = j->next) {
+				pkginfo_t *old = (pkginfo_t*)j->data;
+				// merge lists
+				for(k = old->requiredby; k; k = k->next) {
+					if(!is_in(k->data, new->requiredby)) {
+						// replace old's name with new's name in the requiredby's dependency list
+						PMList *m;
+						pkginfo_t *depender = db_scan(db, k->data, INFRQ_DEPENDS);
+						for(m = depender->depends; m; m = m->next) {
+							if(!strcmp(m->data, old->name)) {
+								FREE(m->data);
+								m->data = strdup(new->name);
+							}
+						}
+						db_write(db, depender, INFRQ_DEPENDS);
+
+						// add the new requiredby
+						new->requiredby = list_add(new->requiredby, strdup(k->data));
+					}
+				}
+			}
+			db_write(db, new, INFRQ_DEPENDS);
+			FREEPKG(new);
+		}*/
+	}
+
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/sync.h b/lib/libalpm/sync.h
new file mode 100644
index 00000000..00bd7c57
--- /dev/null
+++ b/lib/libalpm/sync.h
@@ -0,0 +1,47 @@
+/*
+ *  sync.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_SYNC_H
+#define _ALPM_SYNC_H
+
+#include "db.h"
+#include "package.h"
+#include "trans.h"
+#include "alpm.h"
+
+typedef struct __pmsync_t {
+	unsigned char type;
+	pmpkg_t *lpkg;
+	pmpkg_t *spkg;
+} pmsync_t;
+
+pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg);
+
+/*int sync_findpkg(char *name, PMList *dbs, pmsyncpkg_t **sync);
+pmsyncpkg_t *find_pkginsync(char *needle, PMList *haystack);
+PMList *rm_pkginsync(char *needle, PMList *haystack);*/
+
+int sync_sysupgrade(PMList **data);
+int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data);
+int sync_commit(pmdb_t *db, pmtrans_t *trans);
+
+#endif /* _ALPM_SYNC_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c
new file mode 100644
index 00000000..1aaca0e4
--- /dev/null
+++ b/lib/libalpm/trans.c
@@ -0,0 +1,187 @@
+/*
+ *  trans.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+/* pacman */
+#include "error.h"
+#include "package.h"
+#include "util.h"
+#include "list.h"
+#include "handle.h"
+#include "add.h"
+#include "remove.h"
+#include "sync.h"
+#include "alpm.h"
+
+extern pmhandle_t *handle;
+
+pmtrans_t *trans_new()
+{
+	pmtrans_t *trans;
+
+	if((trans = (pmtrans_t *)malloc(sizeof(pmtrans_t))) == NULL) {
+		return(NULL);
+	}
+
+	trans->targets = NULL;
+	trans->packages = NULL;
+	trans->type = 0;
+	trans->flags = 0;
+	trans->cb = NULL;
+	trans->state = STATE_IDLE;
+
+	return(trans);
+}
+
+void trans_free(pmtrans_t *trans)
+{
+	if(trans == NULL) {
+		return;
+	}
+
+	FREELIST(trans->targets);
+	FREELISTPKGS(trans->packages);
+
+	free(trans);
+}
+
+int trans_init(pmtrans_t *trans, unsigned char type, unsigned char flags, alpm_trans_cb cb)
+{
+	/* Sanity checks */
+	if(trans == NULL) {
+		PM_RET_ERR(PM_ERR_TRANS_NULL, -1);
+	}
+
+	/* ORE
+	perform sanity checks on type and flags:
+	for instance, we can't set UPGRADE and FRESHEN at the same time */
+
+	trans->type = type;
+	trans->flags = flags;
+	trans->cb = cb;
+	trans->state = STATE_INITIALIZED;
+
+	return(0);
+}
+
+int trans_addtarget(pmtrans_t *trans, char *target)
+{
+	/* Sanity checks */
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1));
+	ASSERT(target != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	if(pm_list_is_strin(target, trans->targets)) {
+		PM_RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1);
+	}
+
+	switch(trans->type) {
+		case PM_TRANS_TYPE_ADD:
+		case PM_TRANS_TYPE_UPGRADE:
+			if(add_loadtarget(handle->db_local, trans, target) == -1) {
+				/* pm_errno is set by add_loadtarget() */
+				return(-1);
+			}
+		break;
+		case PM_TRANS_TYPE_REMOVE:
+			if(remove_loadtarget(handle->db_local, trans, target) == -1) {
+				/* pm_errno is set by remove_loadtarget() */
+				return(-1);
+			}
+		break;
+	}
+	trans->targets = pm_list_add(trans->targets, strdup(target));
+	trans->state = STATE_INITIALIZED;
+
+	return(0);
+}
+
+int trans_prepare(pmtrans_t *trans, PMList **data)
+{
+	*data = NULL;
+
+	/* Sanity checks */
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	ASSERT(trans->packages != NULL, return(0));
+
+	switch(trans->type) {
+		case PM_TRANS_TYPE_ADD:
+		case PM_TRANS_TYPE_UPGRADE:
+			if(add_prepare(handle->db_local, trans, data) == -1) {
+				/* pm_errno is set by add_prepare() */
+				return(-1);
+			}
+		break;
+		case PM_TRANS_TYPE_REMOVE:
+			if(remove_prepare(handle->db_local, trans, data) == -1) {
+				/* pm_errno is set by remove_prepare() */
+				return(-1);
+			}
+		break;
+		case PM_TRANS_TYPE_SYNC:
+			if(sync_prepare(handle->db_local, trans, data) == -1) {
+				/* pm_errno is set by sync_prepare() */
+				return(-1);
+			}
+		break;
+	}
+
+	trans->state = STATE_PREPARED;
+
+	return(0);
+}
+
+int trans_commit(pmtrans_t *trans)
+{
+	/* Sanity checks */
+	ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1));
+
+	/* If there's nothing to do, return without complaining */
+	ASSERT(trans->packages != NULL, return(0));
+
+	switch(trans->type) {
+		case PM_TRANS_TYPE_ADD:
+		case PM_TRANS_TYPE_UPGRADE:
+			if(add_commit(handle->db_local, trans) == -1) {
+				return(-1);
+			}
+		break;
+		case PM_TRANS_TYPE_REMOVE:
+			if(remove_commit(handle->db_local, trans) == -1) {
+				return(-1);
+			}
+		break;
+		case PM_TRANS_TYPE_SYNC:
+			if(sync_commit(handle->db_local, trans) == -1) {
+				return(-1);
+			}
+		break;
+	}
+
+	trans->state = STATE_COMMITED;
+
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/trans.h b/lib/libalpm/trans.h
new file mode 100644
index 00000000..98732fbf
--- /dev/null
+++ b/lib/libalpm/trans.h
@@ -0,0 +1,54 @@
+/*
+ *  trans.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_TRANS_H
+#define _ALPM_TRANS_H
+
+enum {
+	STATE_IDLE = 0,
+	STATE_INITIALIZED,
+	STATE_PREPARED,
+	STATE_COMMITED
+};
+
+#include "alpm.h"
+
+typedef struct __pmtrans_t {
+	unsigned char type;
+	unsigned char flags;
+	unsigned char state;
+	PMList *targets; /* PMList of (char *) */
+	PMList *packages; /* PMList of (pmpkginfo_t *) */
+	alpm_trans_cb cb;
+} pmtrans_t;
+
+#define FREETRANS(p) do { if (p) { trans_free(p); p = NULL; } } while (0)
+#define TRANS_CB(t, e, d1, d2) do { if((t) && (t)->cb) { (t)->cb(e, d1, d2); } } while(0)
+
+pmtrans_t *trans_new();
+void trans_free(pmtrans_t *trans);
+int trans_init(pmtrans_t *trans, unsigned char type, unsigned char flags, alpm_trans_cb cb);
+int trans_addtarget(pmtrans_t *trans, char *target);
+int trans_prepare(pmtrans_t *trans, PMList **data);
+int trans_commit(pmtrans_t *trans);
+
+#endif /* _ALPM_TRANS_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
new file mode 100644
index 00000000..9ba3e1ee
--- /dev/null
+++ b/lib/libalpm/util.c
@@ -0,0 +1,401 @@
+/*
+ *  util.c
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <time.h>
+#include <syslog.h>
+#include <zlib.h>
+#include <libtar.h>
+/* pacman */
+#include "log.h"
+#include "util.h"
+#include "alpm.h"
+
+/* borrowed and modified from Per Liden's pkgutils (http://crux.nu) */
+long _alpm_gzopen_frontend(char *pathname, int oflags, int mode)
+{
+	char* gzoflags;
+	int fd;
+	gzFile gzf;
+
+	switch (oflags & O_ACCMODE) {
+		case O_WRONLY:
+			gzoflags = "w";
+			break;
+		case O_RDONLY:
+			gzoflags = "r";
+			break;
+		case O_RDWR:
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+	
+	if((fd = open(pathname, oflags, mode)) == -1) {
+		return -1;
+	}
+	if((oflags & O_CREAT) && fchmod(fd, mode)) {
+		return -1;
+	}
+	if(!(gzf = gzdopen(fd, gzoflags))) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	return (long)gzf;
+}
+
+/* does the same thing as 'mkdir -p' */
+int _alpm_makepath(char *path)
+{
+	char *orig, *str, *ptr;
+	char full[PATH_MAX] = "";
+	mode_t oldmask;
+
+	oldmask = umask(0000);
+
+	orig = strdup(path);
+	str = orig;
+	while((ptr = strsep(&str, "/"))) {
+		if(strlen(ptr)) {
+			struct stat buf;
+
+			strcat(full, "/");
+			strcat(full, ptr);
+			if(stat(full, &buf)) {
+			  if(mkdir(full, 0755)) {
+					free(orig);
+					umask(oldmask);
+					return(1);
+				}
+			}
+		}
+	}
+	free(orig);
+	umask(oldmask);
+	return(0);
+}
+
+int _alpm_copyfile(char *src, char *dest)
+{
+	FILE *in, *out;
+	size_t len;
+	char buf[4097];
+
+	in = fopen(src, "r");
+	if(in == NULL) {
+		return(1);
+	}
+	out = fopen(dest, "w");
+	if(out == NULL) {
+		return(1);
+	}
+
+	while((len = fread(buf, 1, 4096, in))) {
+		fwrite(buf, 1, len, out);
+	}
+
+	fclose(in);
+	fclose(out);
+	return(0);
+}
+
+/* Convert a string to uppercase
+ */
+char *_alpm_strtoupper(char *str)
+{
+	char *ptr = str;
+
+	while(*ptr) {
+		(*ptr) = toupper(*ptr);
+		ptr++;
+	}
+	return str;
+}
+
+/* Trim whitespace and newlines from a string
+ */
+char *_alpm_strtrim(char *str)
+{
+	char *pch = str;
+	while(isspace(*pch)) {
+		pch++;
+	}
+	if(pch != str) {
+		memmove(str, pch, (strlen(pch) + 1));
+	}
+	
+	pch = (char*)(str + (strlen(str) - 1));
+	while(isspace(*pch)) {
+		pch--;
+	}
+	*++pch = '\0';
+
+	return str;
+}
+
+/* A cheap grep for text files, returns 1 if a substring
+ * was found in the text file fn, 0 if it wasn't
+ */
+int _alpm_grep(const char *fn, const char *needle)
+{
+	FILE *fp;
+
+	if((fp = fopen(fn, "r")) == NULL) {
+		return(0);
+	}
+	while(!feof(fp)) {
+		char line[1024];
+		fgets(line, 1024, fp);
+		if(feof(fp)) {
+			continue;
+		}
+		if(strstr(line, needle)) {
+			fclose(fp);
+			return(1);
+		}
+	}
+	fclose(fp);
+	return(0);
+}
+
+/* Create a lock file
+ */
+int _alpm_lckmk(char *file)
+{
+	int fd, count = 0;
+
+	while((fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000)) == -1 && errno == EACCES) { 
+		if(++count < 1) {
+			sleep(1);
+		}	else {
+			return(-1);
+		}
+	}
+	return(fd > 0 ? 0 : -1);
+
+	return(0);
+}
+
+/* Remove a lock file
+ */
+int _alpm_lckrm(char *file)
+{
+	return(unlink(file) == -1);
+}
+
+int _alpm_unpack(char *archive, const char *prefix, const char *fn)
+{
+	TAR *tar = NULL;
+	char expath[PATH_MAX];
+	tartype_t gztype = {
+		(openfunc_t) _alpm_gzopen_frontend,
+		(closefunc_t)gzclose,
+		(readfunc_t) gzread,
+		(writefunc_t)gzwrite
+	};
+
+	/* open the .tar.gz package */
+	if(tar_open(&tar, archive, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
+		perror(archive);
+		return(1);
+	}
+	while(!th_read(tar)) {
+		if(fn && strcmp(fn, th_get_pathname(tar))) {
+			if(TH_ISREG(tar) && tar_skip_regfile(tar)) {
+				char errorstr[255];
+				snprintf(errorstr, 255, "bad tar archive: %s", archive);
+				perror(errorstr);
+				tar_close(tar);
+				return(1);
+			}
+			continue;
+		}
+		snprintf(expath, PATH_MAX, "%s/%s", prefix, th_get_pathname(tar));
+		if(tar_extract_file(tar, expath)) {
+			fprintf(stderr, "could not extract %s: %s\n", th_get_pathname(tar), strerror(errno));
+		}
+		if(fn) break;
+	}
+	tar_close(tar);
+
+	return(0);
+}
+
+/* does the same thing as 'rm -rf' */
+int _alpm_rmrf(char *path)
+{
+	int errflag = 0;
+	struct dirent *dp;
+	DIR *dirp;
+	char name[PATH_MAX];
+	extern int errno;
+
+	if(!unlink(path)) {
+		return(0);
+	} else {
+		if(errno == ENOENT) {
+			return(0);
+		} else if(errno == EPERM) {
+			/* fallthrough */
+		} else if(errno == EISDIR) {
+			/* fallthrough */
+		} else if(errno == ENOTDIR) {
+			return(1);
+		} else {
+			/* not a directory */
+			return(1);
+		}
+
+		if((dirp = opendir(path)) == (DIR *)-1) {
+			return(1);
+		}
+		for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+			if(dp->d_ino) {
+				sprintf(name, "%s/%s", path, dp->d_name);
+				if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) {
+					errflag += _alpm_rmrf(name);
+				}
+			}
+		}
+		closedir(dirp);
+		if(rmdir(path)) {
+			errflag++;
+		}
+		return(errflag);
+	}
+	return(0);
+}
+
+int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...)
+{
+	char msg[1024];
+	va_list args;
+
+	va_start(args, fmt);
+	vsnprintf(msg, 1024, fmt, args);
+	va_end(args);
+
+	if(usesyslog) {
+		syslog(LOG_WARNING, "%s", msg);
+	}
+
+	if(f) {
+		time_t t;
+		struct tm *tm;
+
+		t = time(NULL);
+		tm = localtime(&t);
+
+		fprintf(f, "[%02d/%02d/%02d %02d:%02d] %s\n", tm->tm_mon+1, tm->tm_mday,
+		        tm->tm_year-100, tm->tm_hour, tm->tm_min, msg);
+	}
+
+	return(0);
+}
+
+int _alpm_ldconfig(char *root)
+{
+	char line[PATH_MAX];
+	struct stat buf;
+
+	snprintf(line, PATH_MAX, "%setc/ld.so.conf", root);
+	if(!stat(line, &buf)) {
+		snprintf(line, PATH_MAX, "%ssbin/ldconfig", root);
+		if(!stat(line, &buf)) {
+			char cmd[PATH_MAX];
+			snprintf(cmd, PATH_MAX, "%s -r %s", line, root);
+			system(cmd);
+		}
+	}
+
+	return(0);
+}
+
+int _alpm_runscriptlet(char *root, char *installfn, char *script, char *ver, char *oldver)
+{
+	char scriptfn[PATH_MAX];
+	char cmdline[PATH_MAX];
+	char tmpdir[PATH_MAX] = "";
+	char *scriptpath;
+	struct stat buf;
+
+	return(0);
+
+	if(stat(installfn, &buf)) {
+		/* not found */
+		return(0);
+	}
+
+	if(!strcmp(script, "pre_upgrade") || !strcmp(script, "pre_install")) {
+		snprintf(tmpdir, PATH_MAX, "%stmp/", root);
+		if(stat(tmpdir, &buf)) {
+			_alpm_makepath(tmpdir);
+		}
+		snprintf(tmpdir, PATH_MAX, "%stmp/pacman-XXXXXX", root);
+		if(mkdtemp(tmpdir) == NULL) {
+			perror("error creating temp directory");
+			return(1);
+		}
+		_alpm_unpack(installfn, tmpdir, ".INSTALL");
+		snprintf(scriptfn, PATH_MAX, "%s/.INSTALL", tmpdir);
+		/* chop off the root so we can find the tmpdir in the chroot */
+		scriptpath = scriptfn + strlen(root) - 1;
+		return(0);
+	} else {
+		strncpy(scriptfn, installfn, PATH_MAX-1);
+		/* chop off the root so we can find the tmpdir in the chroot */
+		scriptpath = scriptfn + strlen(root) - 1;
+	}
+
+	if(!_alpm_grep(scriptfn, script)) {
+		/* script not found in scriptlet file */
+		return(0);
+	}
+
+	/* ORE
+	pm_cblog(PM_LOG_FLOW2, "Executing %s script...\n", script);*/
+	if(oldver) {
+		snprintf(cmdline, PATH_MAX, "echo \"umask 0022; source %s %s %s %s\" | chroot %s /bin/sh",
+				scriptpath, script, ver, oldver, root);
+	} else {
+		snprintf(cmdline, PATH_MAX, "echo \"umask 0022; source %s %s %s\" | chroot %s /bin/sh",
+				scriptpath, script, ver, root);
+	}
+	/* ORE
+	pm_cblog(PM_LOG_FLOW2, "%s\n", cmdline);*/
+	system(cmdline);
+	
+	if(strlen(tmpdir) && _alpm_rmrf(tmpdir)) {
+		fprintf(stderr, "warning: could not remove tmpdir %s\n", tmpdir);
+	}
+	return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h
new file mode 100644
index 00000000..3776ebbc
--- /dev/null
+++ b/lib/libalpm/util.h
@@ -0,0 +1,57 @@
+/*
+ *  util.h
+ * 
+ *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+#ifndef _ALPM_UTIL_H
+#define _ALPM_UTIL_H
+
+#include <stdio.h>
+
+#define MALLOC(p, b) { \
+	if((b) > 0) { \
+		p = malloc(b); \
+		if (!(p)) { \
+			fprintf(stderr, "malloc failure: could not allocate %d bytes\n", (b)); \
+			exit(1); \
+		} \
+	} else { \
+		p = NULL; \
+	} \
+}
+#define FREE(p) do { if (p) { free(p); p = NULL; } } while(0)
+
+#define ASSERT(cond, action) do { if(!(cond)) { action; } } while(0)
+
+long _alpm_gzopen_frontend(char *pathname, int oflags, int mode);
+int _alpm_makepath(char *path);
+int _alpm_copyfile(char *src, char *dest);
+char *_alpm_strtoupper(char *str);
+char *_alpm_strtrim(char *str);
+int _alpm_grep(const char *fn, const char *needle);
+int _alpm_lckmk(char *file);
+int _alpm_lckrm(char *file);
+int _alpm_unpack(char *archive, const char *prefix, const char *fn);
+int _alpm_rmrf(char *path);
+int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...);
+int _alpm_ldconfig(char *root);
+int _alpm_runscriptlet(char *util, char *installfn, char *script, char *ver, char *oldver);
+
+#endif /* _ALPM_UTIL_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libftp/Makefile b/lib/libftp/Makefile
new file mode 100644
index 00000000..c68bf2b5
--- /dev/null
+++ b/lib/libftp/Makefile
@@ -0,0 +1,68 @@
+#
+# This makefile contains modifications submitted by Richard Braakman
+# (dark@xs4all.nl) for the shared library generation.
+#
+
+# By default, ftplib uses PASV.  If you need it to use  PORT
+# instead, uncomment the next line
+DEFINES = -DFTPLIB_DEFMODE=FTPLIB_PORT
+
+SONAME = 3
+SOVERSION = $(SONAME).1
+
+TARGETS = qftp libftp.a libftp.so
+OBJECTS = qftp.o ftplib.o
+SOURCES = qftp.c ftplib.c
+
+CFLAGS = -Wall $(DEBUG) -I. $(INCLUDES) $(DEFINES)
+LDFLAGS = -L.
+DEPFLAGS =
+
+all : $(TARGETS)
+
+clean :
+	rm -f $(OBJECTS) core *.bak
+	rm -rf unshared 
+
+clobber : clean
+	rm -f $(TARGETS) .depend
+	rm -f libftp.so.*
+
+install : all
+	install qftp /usr/local/bin
+	install -m 644 libftp.so.$(SOVERSION) /usr/local/lib
+	install -m 644 ftplib.h /usr/local/include
+	(cd /usr/local/lib && \
+	 ln -sf libftp.so.$(SOVERSION) libftp.so.$(SONAME) && \
+	 ln -sf libftp.so.$(SONAME) libftp.so)
+	-(cd /usr/local/bin && \
+	  for f in ftpdir ftpget ftplist ftprm ftpsend; \
+	  do ln -s qftp $$f; done)
+
+depend :
+	$(CC) $(CFLAGS) -M $(SOURCES) > .depend
+
+# build without -fPIC
+unshared/ftplib.o: ftplib.c ftplib.h
+	-mkdir unshared
+	$(CC) -c $(CFLAGS) -D_REENTRANT $< -o $@
+
+ftplib.o: ftplib.c ftplib.h
+	$(CC) -c $(CFLAGS) -fPIC -D_REENTRANT $< -o $@
+
+libftp.a: unshared/ftplib.o
+	ar -rcs $@ $<
+
+libftp.so.$(SOVERSION): ftplib.o
+	$(CC) -shared -Wl,-soname,libftp.so.$(SONAME) -lc -o $@ $<
+
+libftp.so: libftp.so.$(SOVERSION)
+	ln -sf $< libftp.so.$(SONAME)
+	ln -sf $< $@
+
+qftp : qftp.o libftp.so ftplib.h
+	$(CC) $(LDFLAGS) -o $@ $< -lftp
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/lib/libftp/ftplib.c b/lib/libftp/ftplib.c
new file mode 100644
index 00000000..b69744eb
--- /dev/null
+++ b/lib/libftp/ftplib.c
@@ -0,0 +1,1569 @@
+/***************************************************************************/
+/*                                                                         */
+/* ftplib.c - callable ftp access routines                                 */
+/* Copyright (C) 1996-2000 Thomas Pfau, pfau@cnj.digex.net                 */
+/* 73 Catherine Street, South Bound Brook, NJ, 08880                       */
+/*                                                                         */
+/* This library is free software; you can redistribute it and/or           */
+/* modify it under the terms of the GNU Library General Public             */
+/* License as published by the Free Software Foundation; either            */
+/* version 2 of the License, or (at your option) any later version.        */
+/*                                                                         */
+/* This library is distributed in the hope that it will be useful,         */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of          */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
+/* Library General Public License for more details.                        */
+/*                                                                         */
+/* You should have received a copy of the GNU Library General Public       */
+/* License along with this progam; if not, write to the                    */
+/* Free Software Foundation, Inc., 59 Temple Place - Suite 330,            */
+/* Boston, MA 02111-1307, USA.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+#if defined(__unix__) || defined(__VMS)
+#include <unistd.h>
+#endif
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#if defined(__unix__)
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#elif defined(VMS)
+#include <types.h>
+#include <socket.h>
+#include <in.h>
+#include <netdb.h>
+#include <inet.h>
+#elif defined(_WIN32)
+#include <winsock.h>
+#endif
+
+#define BUILDING_LIBRARY
+#include "ftplib.h"
+
+#if defined(_WIN32)
+#define SETSOCKOPT_OPTVAL_TYPE (const char *)
+#else
+#define SETSOCKOPT_OPTVAL_TYPE (void *)
+#endif
+
+#define FTPLIB_BUFSIZ 8192
+#define ACCEPT_TIMEOUT 30
+
+#define FTPLIB_CONTROL 0
+#define FTPLIB_READ 1
+#define FTPLIB_WRITE 2
+
+#if !defined FTPLIB_DEFMODE
+#define FTPLIB_DEFMODE FTPLIB_PASSIVE
+#endif
+
+struct NetBuf {
+	char *cput,*cget;
+	int handle;
+	int cavail,cleft;
+	char *buf;
+	int dir;
+	netbuf *ctrl;
+	netbuf *data;    
+	int cmode;
+	struct timeval idletime;
+	FtpCallback idlecb;
+	void *idlearg;
+	int xfered;
+	int cbbytes;
+	int xfered1;
+	char response[256];
+};
+
+static char *version =
+"ftplib Release 3.1-1 9/16/00, copyright 1996-2000 Thomas Pfau";
+
+GLOBALDEF int ftplib_debug = 0;
+
+#if defined(__unix__) || defined(VMS)
+#define net_read read
+#define net_write write
+#define net_close close
+#elif defined(_WIN32)
+#define net_read(x,y,z) recv(x,y,z,0)
+#define net_write(x,y,z) send(x,y,z,0)
+#define net_close closesocket
+#endif
+	
+#if defined(NEED_MEMCCPY)
+	/*
+	 * VAX C does not supply a memccpy routine so I provide my own
+	 */
+void *memccpy(void *dest, const void *src, int c, size_t n)
+{
+	int i=0;
+	const unsigned char *ip=src;
+	unsigned char *op=dest;
+
+	while (i < n)
+	{
+		if ((*op++ = *ip++) == c)
+			break;
+		i++;
+	}
+	if (i == n)
+		return NULL;
+	return op;
+}
+#endif
+#if defined(NEED_STRDUP)
+/*
+ * strdup - return a malloc'ed copy of a string
+ */
+char *strdup(const char *src)
+{
+	int l = strlen(src) + 1;
+	char *dst = malloc(l);
+	if (dst)
+		strcpy(dst,src);
+	return dst;
+}
+#endif
+
+/*
+ * socket_wait - wait for socket to receive or flush data
+ *
+ * return 1 if no user callback, otherwise, return value returned by
+ * user callback
+ */
+static int socket_wait(netbuf *ctl)
+{
+	fd_set fd,*rfd = NULL,*wfd = NULL;
+	struct timeval tv;
+	int rv = 0;
+	if ((ctl->dir == FTPLIB_CONTROL) || (ctl->idlecb == NULL))
+		return 1;
+	if (ctl->dir == FTPLIB_WRITE)
+		wfd = &fd;
+	else
+		rfd = &fd;
+	FD_ZERO(&fd);
+	do
+	{
+		FD_SET(ctl->handle,&fd);
+		tv = ctl->idletime;
+		rv = select(ctl->handle+1, rfd, wfd, NULL, &tv);
+		if (rv == -1)
+		{
+			rv = 0;
+			strncpy(ctl->ctrl->response, strerror(errno),
+					sizeof(ctl->ctrl->response));
+			break;
+		}
+		else if (rv > 0)
+		{
+			rv = 1;
+			break;
+		}
+	}
+	while ((rv = ctl->idlecb(ctl, ctl->xfered, ctl->idlearg)));
+	return rv;
+}
+
+/*
+ * read a line of text
+ *
+ * return -1 on error or bytecount
+ */
+static int readline(char *buf,int max,netbuf *ctl)
+{
+	int x,retval = 0;
+	char *end,*bp=buf;
+	int eof = 0;
+
+	if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ))
+		return -1;
+	if (max == 0)
+		return 0;
+	do
+	{
+		if (ctl->cavail > 0)
+		{
+			x = (max >= ctl->cavail) ? ctl->cavail : max-1;
+			end = memccpy(bp,ctl->cget,'\n',x);
+			if (end != NULL)
+				x = end - bp;
+			retval += x;
+			bp += x;
+			*bp = '\0';
+			max -= x;
+			ctl->cget += x;
+			ctl->cavail -= x;
+			if (end != NULL)
+			{
+				bp -= 2;
+				if (strcmp(bp,"\r\n") == 0)
+				{
+					*bp++ = '\n';
+					*bp++ = '\0';
+					--retval;
+				}
+				break;
+			}
+		}
+		if (max == 1)
+		{
+			*buf = '\0';
+			break;
+		}
+		if (ctl->cput == ctl->cget)
+		{
+			ctl->cput = ctl->cget = ctl->buf;
+			ctl->cavail = 0;
+			ctl->cleft = FTPLIB_BUFSIZ;
+		}
+		if (eof)
+		{
+			if (retval == 0)
+				retval = -1;
+			break;
+		}
+		if (!socket_wait(ctl))
+			return retval;
+		if ((x = net_read(ctl->handle,ctl->cput,ctl->cleft)) == -1)
+		{
+			perror("read");
+			retval = -1;
+			break;
+		}
+		if (x == 0)
+			eof = 1;
+		ctl->cleft -= x;
+		ctl->cavail += x;
+		ctl->cput += x;
+	}
+	while (1);
+	return retval;
+}
+
+/*
+ * write lines of text
+ *
+ * return -1 on error or bytecount
+ */
+static int writeline(char *buf, int len, netbuf *nData)
+{
+	int x, nb=0, w;
+	char *ubp = buf, *nbp;
+	char lc=0;
+
+	if (nData->dir != FTPLIB_WRITE)
+		return -1;
+	nbp = nData->buf;
+	for (x=0; x < len; x++)
+	{
+		if ((*ubp == '\n') && (lc != '\r'))
+		{
+			if (nb == FTPLIB_BUFSIZ)
+			{
+				if (!socket_wait(nData))
+					return x;
+				w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ);
+				if (w != FTPLIB_BUFSIZ)
+				{
+					printf("net_write(1) returned %d, errno = %d\n", w, errno);
+					return(-1);
+				}
+				nb = 0;
+			}
+			nbp[nb++] = '\r';
+		}
+		if (nb == FTPLIB_BUFSIZ)
+		{
+			if (!socket_wait(nData))
+				return x;
+			w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ);
+			if (w != FTPLIB_BUFSIZ)
+			{
+				printf("net_write(2) returned %d, errno = %d\n", w, errno);
+				return(-1);
+			}
+			nb = 0;
+		}
+		nbp[nb++] = lc = *ubp++;
+	}
+	if (nb)
+	{
+		if (!socket_wait(nData))
+			return x;
+		w = net_write(nData->handle, nbp, nb);
+		if (w != nb)
+		{
+			printf("net_write(3) returned %d, errno = %d\n", w, errno);
+			return(-1);
+		}
+	}
+	return len;
+}
+
+/*
+ * read a response from the server
+ *
+ * return 0 if first char doesn't match
+ * return 1 if first char matches
+ */
+static int readresp(char c, netbuf *nControl)
+{
+	char match[5];
+	if (readline(nControl->response,256,nControl) == -1)
+	{
+		perror("Control socket read failed");
+		return 0;
+	}
+	if (ftplib_debug > 1)
+		fprintf(stderr,"%s",nControl->response);
+	if (nControl->response[3] == '-')
+	{
+		strncpy(match,nControl->response,3);
+		match[3] = ' ';
+		match[4] = '\0';
+		do
+		{
+			if (readline(nControl->response,256,nControl) == -1)
+			{
+				perror("Control socket read failed");
+				return 0;
+			}
+			if (ftplib_debug > 1)
+				fprintf(stderr,"%s",nControl->response);
+		}
+		while (strncmp(nControl->response,match,4));
+	}
+	if (nControl->response[0] == c)
+		return 1;
+	return 0;
+}
+
+/*
+ * FtpInit for stupid operating systems that require it (Windows NT)
+ */
+GLOBALDEF void FtpInit(void)
+{
+#if defined(_WIN32)
+	WORD wVersionRequested;
+	WSADATA wsadata;
+	int err;
+	wVersionRequested = MAKEWORD(1,1);
+	if ((err = WSAStartup(wVersionRequested,&wsadata)) != 0)
+		fprintf(stderr,"Network failed to start: %d\n",err);
+#endif
+}
+
+/*
+ * FtpLastResponse - return a pointer to the last response received
+ */
+GLOBALDEF char *FtpLastResponse(netbuf *nControl)
+{
+	if ((nControl) && (nControl->dir == FTPLIB_CONTROL))
+		return nControl->response;
+	return NULL;
+}
+
+/*
+ * FtpConnect - connect to remote server
+ *
+ * return 1 if connected, 0 if not
+ */
+GLOBALDEF int FtpConnect(const char *host, netbuf **nControl)
+{
+	int sControl;
+	struct sockaddr_in sin;
+	struct hostent *phe;
+	struct servent *pse;
+	int on=1;
+	netbuf *ctrl;
+	char *lhost;
+	char *pnum;
+
+	memset(&sin,0,sizeof(sin));
+	sin.sin_family = AF_INET;
+	lhost = strdup(host);
+	pnum = strchr(lhost,':');
+	if (pnum == NULL)
+	{
+#if defined(VMS)
+		sin.sin_port = htons(21);
+#else
+		if ((pse = getservbyname("ftp","tcp")) == NULL)
+		{
+			perror("getservbyname");
+			return 0;
+		}
+		sin.sin_port = pse->s_port;
+#endif
+	}
+	else
+	{
+		*pnum++ = '\0';
+		if (isdigit(*pnum))
+			sin.sin_port = htons(atoi(pnum));
+		else
+		{
+			pse = getservbyname(pnum,"tcp");
+			if(pse == NULL) {
+				perror("getservbyname");
+				return 0;
+			}
+			sin.sin_port = pse->s_port;
+		}
+	}
+	if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1)
+	{
+		if ((phe = gethostbyname(lhost)) == NULL)
+		{
+			perror("gethostbyname");
+			return 0;
+		}
+		memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
+	}
+	free(lhost);
+	sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sControl == -1)
+	{
+		perror("socket");
+		return 0;
+	}
+	if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR,
+				SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
+	{
+		perror("setsockopt");
+		net_close(sControl);
+		return 0;
+	}
+	if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1)
+	{
+		perror("connect");
+		net_close(sControl);
+		return 0;
+	}
+	ctrl = calloc(1,sizeof(netbuf));
+	if (ctrl == NULL)
+	{
+		perror("calloc");
+		net_close(sControl);
+		return 0;
+	}
+	ctrl->buf = malloc(FTPLIB_BUFSIZ);
+	if (ctrl->buf == NULL)
+	{
+		perror("calloc");
+		net_close(sControl);
+		free(ctrl);
+		return 0;
+	}
+	ctrl->handle = sControl;
+	ctrl->dir = FTPLIB_CONTROL;
+	ctrl->ctrl = NULL;
+	ctrl->cmode = FTPLIB_DEFMODE;
+	ctrl->idlecb = NULL;
+	ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0;
+	ctrl->idlearg = NULL;
+	ctrl->xfered = 0;
+	ctrl->xfered1 = 0;
+	ctrl->cbbytes = 0;
+	if (readresp('2', ctrl) == 0)
+	{
+		net_close(sControl);
+		free(ctrl->buf);
+		free(ctrl);
+		return 0;
+	}
+	*nControl = ctrl;
+	return 1;
+}
+
+/*
+ * FtpOptions - change connection options
+ *
+ * returns 1 if successful, 0 on error
+ */
+GLOBALDEF int FtpOptions(int opt, long val, netbuf *nControl)
+{
+	int v,rv=0;
+	switch (opt)
+	{
+		case FTPLIB_CONNMODE:
+			v = (int) val;
+			if ((v == FTPLIB_PASSIVE) || (v == FTPLIB_PORT))
+			{
+				nControl->cmode = v;
+				rv = 1;
+			}
+			break;
+		case FTPLIB_CALLBACK:
+			nControl->idlecb = (FtpCallback) val;
+			rv = 1;
+			break;
+		case FTPLIB_IDLETIME:
+			v = (int) val;
+			rv = 1;
+			nControl->idletime.tv_sec = v / 1000;
+			nControl->idletime.tv_usec = (v % 1000) * 1000;
+			break;
+		case FTPLIB_CALLBACKARG:
+			rv = 1;
+			nControl->idlearg = (void *) val;
+			break;
+		case FTPLIB_CALLBACKBYTES:
+			rv = 1;
+			nControl->cbbytes = (int) val;
+			break;
+	}
+	return rv;
+}
+
+/*
+ * FtpSendCmd - send a command and wait for expected response
+ *
+ * return 1 if proper response received, 0 otherwise
+ */
+static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl)
+{
+	char buf[256];
+	if (nControl->dir != FTPLIB_CONTROL)
+		return 0;
+	if (ftplib_debug > 2)
+		fprintf(stderr,"%s\n",cmd);
+	if ((strlen(cmd) + 3) > sizeof(buf))
+		return 0;
+	sprintf(buf,"%s\r\n",cmd);
+	if (net_write(nControl->handle,buf,strlen(buf)) <= 0)
+	{
+		perror("write");
+		return 0;
+	}
+	return readresp(expresp, nControl);
+}
+
+/*
+ * FtpLogin - log in to remote server
+ *
+ * return 1 if logged in, 0 otherwise
+ */
+GLOBALDEF int FtpLogin(const char *user, const char *pass, netbuf *nControl)
+{
+	char tempbuf[64];
+
+	if (((strlen(user) + 7) > sizeof(tempbuf)) ||
+			((strlen(pass) + 7) > sizeof(tempbuf)))
+		return 0;
+	sprintf(tempbuf,"USER %s",user);
+	if (!FtpSendCmd(tempbuf,'3',nControl))
+	{
+		if (nControl->response[0] == '2')
+			return 1;
+		return 0;
+	}
+	sprintf(tempbuf,"PASS %s",pass);
+	return FtpSendCmd(tempbuf,'2',nControl);
+}
+
+/*
+ * FtpOpenPort - set up data connection
+ *
+ * return 1 if successful, 0 otherwise
+ */
+static int FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir)
+{
+	int sData;
+	union {
+		struct sockaddr sa;
+		struct sockaddr_in in;
+	} sin;
+	struct linger lng = { 0, 0 };
+	unsigned int l;
+	int on=1;
+	netbuf *ctrl;
+	char *cp;
+	unsigned int v[6];
+	char buf[256];
+
+	if (nControl->dir != FTPLIB_CONTROL)
+		return -1;
+	if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE))
+	{
+		sprintf(nControl->response, "Invalid direction %d\n", dir);
+		return -1;
+	}
+	if ((mode != FTPLIB_ASCII) && (mode != FTPLIB_IMAGE))
+	{
+		sprintf(nControl->response, "Invalid mode %c\n", mode);
+		return -1;
+	}
+	l = sizeof(sin);
+	if (nControl->cmode == FTPLIB_PASSIVE)
+	{
+		memset(&sin, 0, l);
+		sin.in.sin_family = AF_INET;
+		if (!FtpSendCmd("PASV",'2',nControl))
+			return -1;
+		cp = strchr(nControl->response,'(');
+		if (cp == NULL)
+			return -1;
+		cp++;
+		sscanf(cp,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]);
+		sin.sa.sa_data[2] = v[2];
+		sin.sa.sa_data[3] = v[3];
+		sin.sa.sa_data[4] = v[4];
+		sin.sa.sa_data[5] = v[5];
+		sin.sa.sa_data[0] = v[0];
+		sin.sa.sa_data[1] = v[1];
+	}
+	else
+	{
+		if (getsockname(nControl->handle, &sin.sa, &l) < 0)
+		{
+			perror("getsockname");
+			return 0;
+		}
+	}
+	sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+	if (sData == -1)
+	{
+		perror("socket");
+		return -1;
+	}
+	if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR,
+				SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1)
+	{
+		perror("setsockopt");
+		net_close(sData);
+		return -1;
+	}
+	if (setsockopt(sData,SOL_SOCKET,SO_LINGER,
+				SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1)
+	{
+		perror("setsockopt");
+		net_close(sData);
+		return -1;
+	}
+	if (nControl->cmode == FTPLIB_PASSIVE)
+	{
+		if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1)
+		{
+			perror("connect");
+			net_close(sData);
+			return -1;
+		}
+	}
+	else
+	{
+		sin.in.sin_port = 0;
+		if (bind(sData, &sin.sa, sizeof(sin)) == -1)
+		{
+			perror("bind");
+			net_close(sData);
+			return 0;
+		}
+		if (listen(sData, 1) < 0)
+		{
+			perror("listen");
+			net_close(sData);
+			return 0;
+		}
+		if (getsockname(sData, &sin.sa, &l) < 0)
+			return 0;
+		sprintf(buf, "PORT %d,%d,%d,%d,%d,%d",
+				(unsigned char) sin.sa.sa_data[2],
+				(unsigned char) sin.sa.sa_data[3],
+				(unsigned char) sin.sa.sa_data[4],
+				(unsigned char) sin.sa.sa_data[5],
+				(unsigned char) sin.sa.sa_data[0],
+				(unsigned char) sin.sa.sa_data[1]);
+		if (!FtpSendCmd(buf,'2',nControl))
+		{
+			net_close(sData);
+			return 0;
+		}
+	}
+	ctrl = calloc(1,sizeof(netbuf));
+	if (ctrl == NULL)
+	{
+		perror("calloc");
+		net_close(sData);
+		return -1;
+	}
+	if ((mode == 'A') && ((ctrl->buf = malloc(FTPLIB_BUFSIZ)) == NULL))
+	{
+		perror("calloc");
+		net_close(sData);
+		free(ctrl);
+		return -1;
+	}
+	ctrl->handle = sData;
+	ctrl->dir = dir;
+	ctrl->idletime = nControl->idletime;
+	ctrl->idlearg = nControl->idlearg;
+	ctrl->xfered = 0;
+	ctrl->xfered1 = 0;
+	ctrl->cbbytes = nControl->cbbytes;
+	if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec || ctrl->cbbytes)
+		ctrl->idlecb = nControl->idlecb;
+	else
+		ctrl->idlecb = NULL;
+	*nData = ctrl;
+	return 1;
+}
+
+/*
+ * FtpAcceptConnection - accept connection from server
+ *
+ * return 1 if successful, 0 otherwise
+ */
+static int FtpAcceptConnection(netbuf *nData, netbuf *nControl)
+{
+	int sData;
+	struct sockaddr addr;
+	unsigned int l;
+	int i;
+	struct timeval tv;
+	fd_set mask;
+	int rv;
+
+	FD_ZERO(&mask);
+	FD_SET(nControl->handle, &mask);
+	FD_SET(nData->handle, &mask);
+	tv.tv_usec = 0;
+	tv.tv_sec = ACCEPT_TIMEOUT;
+	i = nControl->handle;
+	if (i < nData->handle)
+		i = nData->handle;
+	i = select(i+1, &mask, NULL, NULL, &tv);
+	if (i == -1)
+	{
+		strncpy(nControl->response, strerror(errno),
+				sizeof(nControl->response));
+		net_close(nData->handle);
+		nData->handle = 0;
+		rv = 0;
+	}
+	else if (i == 0)
+	{
+		strcpy(nControl->response, "timed out waiting for connection");
+		net_close(nData->handle);
+		nData->handle = 0;
+		rv = 0;
+	}
+	else
+	{
+		if (FD_ISSET(nData->handle, &mask))
+		{
+			l = sizeof(addr);
+			sData = accept(nData->handle, &addr, &l);
+			i = errno;
+			net_close(nData->handle);
+			if (sData > 0)
+			{
+				rv = 1;
+				nData->handle = sData;
+			}
+			else
+			{
+				strncpy(nControl->response, strerror(i),
+						sizeof(nControl->response));
+				nData->handle = 0;
+				rv = 0;
+			}
+		}
+		else if (FD_ISSET(nControl->handle, &mask))
+		{
+			net_close(nData->handle);
+			nData->handle = 0;
+			readresp('2', nControl);
+			rv = 0;
+		}
+	}
+	return rv;	
+}
+
+/*
+ * FtpAccess - return a handle for a data stream
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl,
+		netbuf **nData)
+{
+	char buf[256];
+	int dir;
+	if ((path == NULL) &&
+			((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ)))
+	{
+		sprintf(nControl->response,
+				"Missing path argument for file transfer\n");
+		return 0;
+	}
+	sprintf(buf, "TYPE %c", mode);
+	if (!FtpSendCmd(buf, '2', nControl))
+		return 0;
+	switch (typ)
+	{
+		case FTPLIB_DIR:
+			strcpy(buf,"NLST");
+			dir = FTPLIB_READ;
+			break;
+		case FTPLIB_DIR_VERBOSE:
+			strcpy(buf,"LIST");
+			dir = FTPLIB_READ;
+			break;
+		case FTPLIB_FILE_READ:
+			strcpy(buf,"RETR");
+			dir = FTPLIB_READ;
+			break;
+		case FTPLIB_FILE_WRITE:
+			strcpy(buf,"STOR");
+			dir = FTPLIB_WRITE;
+			break;
+		default:
+			sprintf(nControl->response, "Invalid open type %d\n", typ);
+			return 0;
+	}
+	if (path != NULL)
+	{
+		int i = strlen(buf);
+		buf[i++] = ' ';
+		if ((strlen(path) + i) >= sizeof(buf))
+			return 0;
+		strcpy(&buf[i],path);
+	}
+	if (FtpOpenPort(nControl, nData, mode, dir) == -1)
+		return 0;
+	if (!FtpSendCmd(buf, '1', nControl))
+	{
+		FtpClose(*nData);
+		*nData = NULL;
+		return 0;
+	}
+	(*nData)->ctrl = nControl;
+	nControl->data = *nData;
+	if (nControl->cmode == FTPLIB_PORT)
+	{
+		if (!FtpAcceptConnection(*nData,nControl))
+		{
+			FtpClose(*nData);
+			*nData = NULL;
+			nControl->data = NULL;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ * FtpRead - read from a data connection
+ */
+GLOBALDEF int FtpRead(void *buf, int max, netbuf *nData)
+{
+	int i;
+	if (nData->dir != FTPLIB_READ)
+		return 0;
+	if (nData->buf)
+		i = readline(buf, max, nData);
+	else
+	{
+		i = socket_wait(nData);
+		if (i != 1)
+			return 0;
+		i = net_read(nData->handle, buf, max);
+	}
+	if (i == -1)
+		return 0;
+	nData->xfered += i;
+	if (nData->idlecb && nData->cbbytes)
+	{
+		nData->xfered1 += i;
+		if (nData->xfered1 > nData->cbbytes)
+		{
+			if (nData->idlecb(nData, nData->xfered, nData->idlearg) == 0)
+				return 0;
+			nData->xfered1 = 0;
+		}
+	}
+	return i;
+}
+
+/*
+ * FtpWrite - write to a data connection
+ */
+GLOBALDEF int FtpWrite(void *buf, int len, netbuf *nData)
+{
+	int i;
+	if (nData->dir != FTPLIB_WRITE)
+		return 0;
+	if (nData->buf)
+		i = writeline(buf, len, nData);
+	else
+	{
+		socket_wait(nData);
+		i = net_write(nData->handle, buf, len);
+	}
+	if (i == -1)
+		return 0;
+	nData->xfered += i;
+	if (nData->idlecb && nData->cbbytes)
+	{
+		nData->xfered1 += i;
+		if (nData->xfered1 > nData->cbbytes)
+		{
+			nData->idlecb(nData, nData->xfered, nData->idlearg);
+			nData->xfered1 = 0;
+		}
+	}
+	return i;
+}
+
+/*
+ * FtpClose - close a data connection
+ */
+GLOBALDEF int FtpClose(netbuf *nData)
+{
+	netbuf *ctrl;
+	switch (nData->dir)
+	{
+		case FTPLIB_WRITE:
+			/* potential problem - if buffer flush fails, how to notify user? */
+			if (nData->buf != NULL)
+				writeline(NULL, 0, nData);
+		case FTPLIB_READ:
+			if (nData->buf)
+				free(nData->buf);
+			shutdown(nData->handle,2);
+			net_close(nData->handle);
+			ctrl = nData->ctrl;
+			free(nData);
+			if (ctrl)
+			{
+				ctrl->data = NULL;
+				return(readresp('2', ctrl));
+			}
+			return 1;
+		case FTPLIB_CONTROL:
+			if (nData->data)
+			{
+				nData->ctrl = NULL;
+				FtpClose(nData);
+			}
+			net_close(nData->handle);
+			free(nData);
+			return 0;
+	}
+	return 1;
+}
+
+/*
+ * FtpSite - send a SITE command
+ *
+ * return 1 if command successful, 0 otherwise
+ */
+GLOBALDEF int FtpSite(const char *cmd, netbuf *nControl)
+{
+	char buf[256];
+
+	if ((strlen(cmd) + 7) > sizeof(buf))
+		return 0;
+	sprintf(buf,"SITE %s",cmd);
+	if (!FtpSendCmd(buf,'2',nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpSysType - send a SYST command
+ *
+ * Fills in the user buffer with the remote system type.  If more
+ * information from the response is required, the user can parse
+ * it out of the response buffer returned by FtpLastResponse().
+ *
+ * return 1 if command successful, 0 otherwise
+ */
+GLOBALDEF int FtpSysType(char *buf, int max, netbuf *nControl)
+{
+	int l = max;
+	char *b = buf;
+	char *s;
+	if (!FtpSendCmd("SYST",'2',nControl))
+		return 0;
+	s = &nControl->response[4];
+	while ((--l) && (*s != ' '))
+		*b++ = *s++;
+	*b++ = '\0';
+	return 1;
+}
+
+/*
+ * FtpMkdir - create a directory at server
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpMkdir(const char *path, netbuf *nControl)
+{
+	char buf[256];
+
+	if ((strlen(path) + 6) > sizeof(buf))
+		return 0;
+	sprintf(buf,"MKD %s",path);
+	if (!FtpSendCmd(buf,'2', nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpChdir - change path at remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpChdir(const char *path, netbuf *nControl)
+{
+	char buf[256];
+
+	if ((strlen(path) + 6) > sizeof(buf))
+		return 0;
+	sprintf(buf,"CWD %s",path);
+	if (!FtpSendCmd(buf,'2',nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpCDUp - move to parent directory at remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpCDUp(netbuf *nControl)
+{
+	if (!FtpSendCmd("CDUP",'2',nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpRmdir - remove directory at remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpRmdir(const char *path, netbuf *nControl)
+{
+	char buf[256];
+
+	if ((strlen(path) + 6) > sizeof(buf))
+		return 0;
+	sprintf(buf,"RMD %s",path);
+	if (!FtpSendCmd(buf,'2',nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpPwd - get working directory at remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpPwd(char *path, int max, netbuf *nControl)
+{
+	int l = max;
+	char *b = path;
+	char *s;
+	if (!FtpSendCmd("PWD",'2',nControl))
+		return 0;
+	s = strchr(nControl->response, '"');
+	if (s == NULL)
+		return 0;
+	s++;
+	while ((--l) && (*s) && (*s != '"'))
+		*b++ = *s++;
+	*b++ = '\0';
+	return 1;
+}
+
+/*
+ * FtpXfer - issue a command and transfer data
+ *
+ * return 1 if successful, 0 otherwise
+ */
+static int FtpXfer(const char *localfile, const char *path,
+		netbuf *nControl, int typ, int mode)
+{
+	int l,c;
+	char *dbuf;
+	FILE *local = NULL;
+	netbuf *nData;
+	int rv=1;
+
+	if (localfile != NULL)
+	{
+		char ac[4] = "a";
+		if (typ == FTPLIB_FILE_WRITE)
+			ac[0] = 'r';
+		if (mode == FTPLIB_IMAGE)
+			ac[1] = 'b';
+		local = fopen(localfile, ac);
+		if (local == NULL)
+		{
+			strncpy(nControl->response, strerror(errno),
+					sizeof(nControl->response));
+			return 0;
+		}
+	}
+	if (local == NULL)
+		local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout;
+	if (!FtpAccess(path, typ, mode, nControl, &nData))
+		return 0;
+	dbuf = malloc(FTPLIB_BUFSIZ);
+	if (typ == FTPLIB_FILE_WRITE)
+	{
+		while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0)
+			if ((c = FtpWrite(dbuf, l, nData)) < l)
+			{
+				printf("short write: passed %d, wrote %d\n", l, c);
+				rv = 0;
+				break;
+			}
+	}
+	else
+	{
+		while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0)
+			if (fwrite(dbuf, 1, l, local) < l)
+			{
+				perror("\nlocalfile write");
+				rv = 0;
+				break;
+			}
+	}
+	free(dbuf);
+	fflush(local);
+	if (localfile != NULL)
+		fclose(local);
+	FtpClose(nData);
+	return rv;
+}
+
+/*
+ * FtpNlst - issue an NLST command and write response to output
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpNlst(const char *outputfile, const char *path,
+		netbuf *nControl)
+{
+	return FtpXfer(outputfile, path, nControl, FTPLIB_DIR, FTPLIB_ASCII);
+}
+
+/*
+ * FtpDir - issue a LIST command and write response to output
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpDir(const char *outputfile, const char *path, netbuf *nControl)
+{
+	return FtpXfer(outputfile, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII);
+}
+
+/*
+ * FtpSize - determine the size of a remote file
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpSize(const char *path, int *size, char mode, netbuf *nControl)
+{
+	char cmd[256];
+	int resp,sz,rv=1;
+
+	if ((strlen(path) + 7) > sizeof(cmd))
+		return 0;
+	sprintf(cmd, "TYPE %c", mode);
+	if (!FtpSendCmd(cmd, '2', nControl))
+		return 0;
+	sprintf(cmd,"SIZE %s",path);
+	if (!FtpSendCmd(cmd,'2',nControl))
+		rv = 0;
+	else
+	{
+		if (sscanf(nControl->response, "%d %d", &resp, &sz) == 2)
+			*size = sz;
+		else
+			rv = 0;
+	}   
+	return rv;
+}
+
+/*
+ * FtpRestart - issue a REST command
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpRestart(int offset, netbuf *nControl)
+{
+	char cmd[256];
+	int rv=1;
+
+	sprintf(cmd,"REST %d",offset);
+	if (!FtpSendCmd(cmd,'3',nControl))
+		rv = 0;
+	return rv;
+}
+
+/*
+ * FtpModDate - determine the modification date of a remote file
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl)
+{
+	char buf[256];
+	int rv = 1;
+
+	if ((strlen(path) + 7) > sizeof(buf))
+		return 0;
+	sprintf(buf,"MDTM %s",path);
+	if (!FtpSendCmd(buf,'2',nControl))
+		rv = 0;
+	else
+		strncpy(dt, &nControl->response[4], max);
+	return rv;
+}
+
+/*
+ * FtpGet - issue a GET command and write received data to output
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpGet(const char *outputfile, const char *path,
+		char mode, netbuf *nControl)
+{
+	return FtpXfer(outputfile, path, nControl, FTPLIB_FILE_READ, mode);
+}
+
+/*
+ * FtpPut - issue a PUT command and send data from input
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpPut(const char *inputfile, const char *path, char mode,
+		netbuf *nControl)
+{
+	return FtpXfer(inputfile, path, nControl, FTPLIB_FILE_WRITE, mode);
+}
+
+/*
+ * FtpRename - rename a file at remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpRename(const char *src, const char *dst, netbuf *nControl)
+{
+	char cmd[256];
+
+	if (((strlen(src) + 7) > sizeof(cmd)) ||
+			((strlen(dst) + 7) > sizeof(cmd)))
+		return 0;
+	sprintf(cmd,"RNFR %s",src);
+	if (!FtpSendCmd(cmd,'3',nControl))
+		return 0;
+	sprintf(cmd,"RNTO %s",dst);
+	if (!FtpSendCmd(cmd,'2',nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpDelete - delete a file at remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF int FtpDelete(const char *fnm, netbuf *nControl)
+{
+	char cmd[256];
+
+	if ((strlen(fnm) + 7) > sizeof(cmd))
+		return 0;
+	sprintf(cmd,"DELE %s",fnm);
+	if (!FtpSendCmd(cmd,'2', nControl))
+		return 0;
+	return 1;
+}
+
+/*
+ * FtpQuit - disconnect from remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALDEF void FtpQuit(netbuf *nControl)
+{
+	if (nControl->dir != FTPLIB_CONTROL)
+		return;
+	FtpSendCmd("QUIT",'2',nControl);
+	net_close(nControl->handle);
+	free(nControl->buf);
+	free(nControl);
+}
+
+/*
+ * HttpConnect - connect to remote server
+ *
+ * return 1 if connected, 0 if not
+ */
+GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl)
+{
+	int sControl;
+	struct sockaddr_in sin;
+	struct hostent *phe;
+	struct servent *pse;
+	netbuf *ctrl;
+	char *lhost;
+	char *pnum;
+
+	memset(&sin,0,sizeof(sin));
+	sin.sin_family = AF_INET;
+	lhost = strdup(host);
+	pnum = strchr(lhost,':');
+	if (pnum == NULL)
+	{
+#if defined(VMS)
+		sin.sin_port = htons(80);
+#else
+		/* we pass a port variable instead (for use with proxies)
+		 *
+		if ((pse = getservbyname("http","tcp")) == NULL)
+		{
+			perror("getservbyname");
+			return 0;
+		}
+		sin.sin_port = pse->s_port;
+		*/
+		sin.sin_port = htons(port);
+#endif
+	}
+	else
+	{
+		*pnum++ = '\0';
+		if (isdigit(*pnum))
+			sin.sin_port = htons(atoi(pnum));
+		else
+		{
+			pse = getservbyname(pnum,"tcp");
+			sin.sin_port = pse->s_port;
+		}
+	}
+	if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1)
+	{
+		if ((phe = gethostbyname(lhost)) == NULL)
+		{
+			perror("gethostbyname");
+			return 0;
+		}
+		memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
+	}
+	free(lhost);
+	sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sControl == -1)
+	{
+		perror("socket");
+		return 0;
+	}
+	if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1)
+	{
+		perror("connect");
+		net_close(sControl);
+		return 0;
+	}
+	ctrl = calloc(1,sizeof(netbuf));
+	if (ctrl == NULL)
+	{
+		perror("calloc");
+		net_close(sControl);
+		return 0;
+	}
+	ctrl->buf = NULL;
+	ctrl->handle = sControl;
+	ctrl->dir = FTPLIB_CONTROL;
+	ctrl->ctrl = NULL;
+	ctrl->cmode = FTPLIB_DEFMODE;
+	ctrl->idlecb = NULL;
+	ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0;
+	ctrl->idlearg = NULL;
+	ctrl->xfered = 0;
+	ctrl->xfered1 = 0;
+	ctrl->cbbytes = 0;
+	*nControl = ctrl;
+	return 1;
+}
+
+/*
+ * HttpSendCmd - send a command
+ *
+ * return 1 if proper response received, 0 otherwise
+ */
+static int HttpSendCmd(const char *cmd, char expresp, netbuf *nControl)
+{
+	int ret = 0;
+	char *buf = nControl->response;
+	//if (nControl->dir != FTPLIB_CONTROL)
+		//return 0;
+	if (ftplib_debug > 2)
+		fprintf(stderr,"%s\n",cmd);
+	if (net_write(nControl->handle,cmd,strlen(cmd)) <= 0)
+	{
+		perror("write");
+		return 0;
+	}
+	while (ret < 256) {
+		if (socket_wait(nControl) != 1)
+			return 0;
+		if (net_read(nControl->handle,buf,1) != 1) {
+			break;
+		}
+		ret++;
+		if (*buf == '\r') continue;
+		if (*buf == '\n') break;
+		buf++;
+	}
+	*buf = 0;
+	if (nControl->response[9] == expresp)
+		return 1;
+	return 0;
+}
+
+/*
+ * HttpXfer - issue a command and transfer data
+ *
+ * return 1 if successful, 0 otherwise
+ */
+static int HttpXfer(const char *localfile, const char *path, int *size,
+		netbuf *nControl, int typ, int mode)
+{
+	int l,c;
+	char *dbuf;
+	FILE *local = NULL;
+	int rv=1;
+	int bytes = 0;
+
+	if (localfile != NULL)
+	{
+		char ac[4] = "a";
+		if (typ == FTPLIB_FILE_WRITE)
+			ac[0] = 'r';
+		if (mode == FTPLIB_IMAGE)
+			ac[1] = 'b';
+		local = fopen(localfile, ac);
+		if (local == NULL)
+		{
+			strncpy(nControl->response, strerror(errno),
+					sizeof(nControl->response));
+			return 0;
+		}
+	}
+	if (local == NULL)
+		local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout;
+	dbuf = malloc(FTPLIB_BUFSIZ);
+	if (typ == FTPLIB_FILE_WRITE)
+	{
+		while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0)
+			if ((c = FtpWrite(dbuf, l, nControl)) < l)
+			{
+				printf("short write: passed %d, wrote %d\n", l, c);
+				rv = 0;
+				break;
+			}
+	}
+	else
+	{
+		nControl->dir = FTPLIB_READ;
+		while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nControl)) > 0) {
+			if (fwrite(dbuf, 1, l, local) < l)
+			{
+				perror("\nlocalfile write");
+				rv = 0;
+				break;
+			}
+			bytes += l;
+			if(size && bytes >= *size) {
+				break;
+			}
+		}
+	}
+	free(dbuf);
+	fflush(local);
+	if (localfile != NULL)
+		fclose(local);
+	free(nControl->data);
+	return rv;
+}
+
+/*
+ * HttpGet - issue a GET command and write received data to output
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALREF int HttpGet(const char *host, const char *outputfile, const char *path,
+		int *size, netbuf *nControl, unsigned int offset)
+{
+	char buf[256];
+
+	if(offset > 0)
+		sprintf(buf, "GET %s HTTP/1.1\r\nHost: %s\r\nRange: bytes=%d-\r\n\r\n", path, host, offset);
+	else
+		sprintf(buf, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", path, host);
+	if(!HttpSendCmd(buf,'2',nControl))
+	{
+		if (nControl->response[9] == '3')
+			printf("redirection not supported\n");
+		return 0;
+	}
+
+	while (1)
+	{
+		int ret = 0;
+		char *buf = nControl->response;
+		while (ret < 256) {
+			if (socket_wait(nControl) != 1)
+				return 0;
+			if (net_read(nControl->handle,buf,1) != 1)
+				break;
+			ret++;
+			if (*buf == '\r') continue;
+			if (*buf == '\n') break;
+			buf++;
+		}
+		*buf = 0;
+		if (strstr(nControl->response,"Content-Length"))
+		{
+			sscanf(nControl->response,"Content-Length: %d",size);
+			if(offset > 0)
+				*size += offset;
+		}
+		if (strlen(nControl->response) == 0)
+			break;
+	}
+
+	return HttpXfer(outputfile, path, size, nControl, FTPLIB_FILE_READ, FTPLIB_IMAGE);
+}
+
+/*
+ * HttpQuit - disconnect from remote
+ *
+ * return 1 if successful, 0 otherwise
+ */
+GLOBALREF void HttpQuit(netbuf *nControl)
+{
+	if(nControl) {
+		net_close(nControl->handle);
+		free(nControl);
+	}
+}
diff --git a/lib/libftp/ftplib.h b/lib/libftp/ftplib.h
new file mode 100644
index 00000000..49e0ccbf
--- /dev/null
+++ b/lib/libftp/ftplib.h
@@ -0,0 +1,131 @@
+/***************************************************************************/
+/*                                                                         */
+/* ftplib.h - header file for callable ftp access routines                 */
+/* Copyright (C) 1996, 1997 Thomas Pfau, pfau@cnj.digex.net                */
+/* 73 Catherine Street, South Bound Brook, NJ, 08880                       */
+/*                                                                         */
+/* This library is free software; you can redistribute it and/or           */
+/* modify it under the terms of the GNU Library General Public             */
+/* License as published by the Free Software Foundation; either            */
+/* version 2 of the License, or (at your option) any later version.        */
+/*                                                                         */
+/* This library is distributed in the hope that it will be useful,         */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of          */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
+/* Library General Public License for more details.                        */
+/*                                                                         */
+/* You should have received a copy of the GNU Library General Public       */
+/* License along with this progam; if not, write to the                    */
+/* Free Software Foundation, Inc., 59 Temple Place - Suite 330,            */
+/* Boston, MA 02111-1307, USA.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+#if !defined(__FTPLIB_H)
+#define __FTPLIB_H
+
+#if defined(__unix__) || defined(VMS)
+#define GLOBALDEF
+#define GLOBALREF extern
+#elif defined(_WIN32)
+#if defined BUILDING_LIBRARY
+#define GLOBALDEF __declspec(dllexport)
+#define GLOBALREF __declspec(dllexport)
+#else
+#define GLOBALREF __declspec(dllimport)
+#endif
+#endif
+
+/* FtpAccess() type codes */
+#define FTPLIB_DIR 1
+#define FTPLIB_DIR_VERBOSE 2
+#define FTPLIB_FILE_READ 3
+#define FTPLIB_FILE_WRITE 4
+
+/* FtpAccess() mode codes */
+#define FTPLIB_ASCII 'A'
+#define FTPLIB_IMAGE 'I'
+#define FTPLIB_TEXT FTPLIB_ASCII
+#define FTPLIB_BINARY FTPLIB_IMAGE
+
+/* connection modes */
+#define FTPLIB_PASSIVE 1
+#define FTPLIB_PORT 2
+
+/* connection option names */
+#define FTPLIB_CONNMODE 1
+#define FTPLIB_CALLBACK 2
+#define FTPLIB_IDLETIME 3
+#define FTPLIB_CALLBACKARG 4
+#define FTPLIB_CALLBACKBYTES 5
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct NetBuf netbuf;
+typedef int (*FtpCallback)(netbuf *nControl, int xfered, void *arg);
+
+/* v1 compatibility stuff */
+#if !defined(_FTPLIB_NO_COMPAT)
+netbuf *DefaultNetbuf;
+
+#define ftplib_lastresp FtpLastResponse(DefaultNetbuf)
+#define ftpInit FtpInit
+#define ftpOpen(x) FtpConnect(x, &DefaultNetbuf)
+#define ftpLogin(x,y) FtpLogin(x, y, DefaultNetbuf)
+#define ftpSite(x) FtpSite(x, DefaultNetbuf)
+#define ftpMkdir(x) FtpMkdir(x, DefaultNetbuf)
+#define ftpChdir(x) FtpChdir(x, DefaultNetbuf)
+#define ftpRmdir(x) FtpRmdir(x, DefaultNetbuf)
+#define ftpNlst(x, y) FtpNlst(x, y, DefaultNetbuf)
+#define ftpDir(x, y) FtpDir(x, y, DefaultNetbuf)
+#define ftpGet(x, y, z) FtpGet(x, y, z, DefaultNetbuf)
+#define ftpPut(x, y, z) FtpPut(x, y, z, DefaultNetbuf)
+#define ftpRename(x, y) FtpRename(x, y, DefaultNetbuf)
+#define ftpDelete(x) FtpDelete(x, DefaultNetbuf)
+#define ftpQuit() FtpQuit(DefaultNetbuf)
+#endif /* (_FTPLIB_NO_COMPAT) */
+/* end v1 compatibility stuff */
+
+GLOBALREF int ftplib_debug;
+GLOBALREF void FtpInit(void);
+GLOBALREF char *FtpLastResponse(netbuf *nControl);
+GLOBALREF int FtpConnect(const char *host, netbuf **nControl);
+GLOBALREF int FtpOptions(int opt, long val, netbuf *nControl);
+GLOBALREF int FtpLogin(const char *user, const char *pass, netbuf *nControl);
+GLOBALREF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl,
+    netbuf **nData);
+GLOBALREF int FtpRead(void *buf, int max, netbuf *nData);
+GLOBALREF int FtpWrite(void *buf, int len, netbuf *nData);
+GLOBALREF int FtpClose(netbuf *nData);
+GLOBALREF int FtpSite(const char *cmd, netbuf *nControl);
+GLOBALREF int FtpSysType(char *buf, int max, netbuf *nControl);
+GLOBALREF int FtpMkdir(const char *path, netbuf *nControl);
+GLOBALREF int FtpChdir(const char *path, netbuf *nControl);
+GLOBALREF int FtpCDUp(netbuf *nControl);
+GLOBALREF int FtpRmdir(const char *path, netbuf *nControl);
+GLOBALREF int FtpPwd(char *path, int max, netbuf *nControl);
+GLOBALREF int FtpNlst(const char *output, const char *path, netbuf *nControl);
+GLOBALREF int FtpDir(const char *output, const char *path, netbuf *nControl);
+GLOBALREF int FtpSize(const char *path, int *size, char mode, netbuf *nControl);
+GLOBALREF int FtpRestart(int offset, netbuf *nControl);
+GLOBALREF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl);
+GLOBALREF int FtpGet(const char *output, const char *path, char mode,
+	netbuf *nControl);
+GLOBALREF int FtpPut(const char *input, const char *path, char mode,
+	netbuf *nControl);
+GLOBALREF int FtpRename(const char *src, const char *dst, netbuf *nControl);
+GLOBALREF int FtpDelete(const char *fnm, netbuf *nControl);
+GLOBALREF void FtpQuit(netbuf *nControl);
+
+GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl);
+GLOBALREF int HttpGet(const char *host, const char *output, const char *path,
+	int *size, netbuf *nControl, unsigned int offset);
+GLOBALREF void HttpQuit(netbuf *nControl);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __FTPLIB_H */
-- 
cgit v1.2.3-70-g09d2