summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJudd Vinet <judd@archlinux.org>2005-03-15 02:51:43 +0100
committerJudd Vinet <judd@archlinux.org>2005-03-15 02:51:43 +0100
commitd04baabafa2ebbad92741d1f87e6ff32999f894a (patch)
tree5a2280176812b80c28ca77bfa8e0655c16f4db7e /lib
downloadpacman-d04baabafa2ebbad92741d1f87e6ff32999f894a.tar.gz
pacman-d04baabafa2ebbad92741d1f87e6ff32999f894a.tar.xz
Initial revision
Diffstat (limited to 'lib')
-rw-r--r--lib/libalpm/Makefile39
-rw-r--r--lib/libalpm/add.c580
-rw-r--r--lib/libalpm/add.h34
-rw-r--r--lib/libalpm/alpm.c577
-rw-r--r--lib/libalpm/alpm.h330
-rw-r--r--lib/libalpm/backup.c63
-rw-r--r--lib/libalpm/backup.h30
-rw-r--r--lib/libalpm/cache.c203
-rw-r--r--lib/libalpm/cache.h42
-rw-r--r--lib/libalpm/db.c651
-rw-r--r--lib/libalpm/db.h60
-rw-r--r--lib/libalpm/deps.c685
-rw-r--r--lib/libalpm/deps.h35
-rw-r--r--lib/libalpm/error.c90
-rw-r--r--lib/libalpm/error.h30
-rw-r--r--lib/libalpm/group.c67
-rw-r--r--lib/libalpm/group.h48
-rw-r--r--lib/libalpm/handle.c229
-rw-r--r--lib/libalpm/handle.h63
-rw-r--r--lib/libalpm/list.c210
-rw-r--r--lib/libalpm/list.h50
-rw-r--r--lib/libalpm/log.c52
-rw-r--r--lib/libalpm/log.h32
-rw-r--r--lib/libalpm/md5.c338
-rw-r--r--lib/libalpm/md5.h51
-rw-r--r--lib/libalpm/md5driver.c81
-rw-r--r--lib/libalpm/package.c342
-rw-r--r--lib/libalpm/package.h78
-rw-r--r--lib/libalpm/provide.c53
-rw-r--r--lib/libalpm/provide.h33
-rw-r--r--lib/libalpm/remove.c259
-rw-r--r--lib/libalpm/remove.h34
-rw-r--r--lib/libalpm/rpmvercmp.c237
-rw-r--r--lib/libalpm/rpmvercmp.h32
-rw-r--r--lib/libalpm/sync.c248
-rw-r--r--lib/libalpm/sync.h47
-rw-r--r--lib/libalpm/trans.c187
-rw-r--r--lib/libalpm/trans.h54
-rw-r--r--lib/libalpm/util.c401
-rw-r--r--lib/libalpm/util.h57
-rw-r--r--lib/libftp/Makefile68
-rw-r--r--lib/libftp/ftplib.c1569
-rw-r--r--lib/libftp/ftplib.h131
43 files changed, 8500 insertions, 0 deletions
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 */