From 62a783a6bd7649ccd162e32278098080bf450c7b Mon Sep 17 00:00:00 2001 From: Judd Vinet Date: Fri, 9 Aug 2002 18:03:48 +0000 Subject: Imported from pacman-2.0.tar.gz --- src/pacman.c | 1660 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1660 insertions(+) create mode 100644 src/pacman.c (limited to 'src/pacman.c') diff --git a/src/pacman.c b/src/pacman.c new file mode 100644 index 00000000..7c70cacd --- /dev/null +++ b/src/pacman.c @@ -0,0 +1,1660 @@ +/* + * pacman + * + * Copyright (c) 2002 by Judd Vinet + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "list.h" +#include "md5.h" +#include "package.h" +#include "db.h" +#include "util.h" +#include "pacsync.h" +#include "pacman.h" + +extern tartype_t gztype; + +/* other prototypes */ +int rpmvercmp(const char *a, const char *b); +char* MDFile(char *); + +/* + * GLOBALS + * + */ + +/* pacman options */ +char* pmo_root = NULL; +unsigned short pmo_op = PM_MAIN; +unsigned short pmo_verbose = 0; +unsigned short pmo_version = 0; +unsigned short pmo_help = 0; +unsigned short pmo_force = 0; +unsigned short pmo_nodeps = 0; +unsigned short pmo_upgrade = 0; +unsigned short pmo_nosave = 0; +unsigned short pmo_vertest = 0; +unsigned short pmo_q_isfile = 0; +unsigned short pmo_q_info = 0; +unsigned short pmo_q_list = 0; +unsigned short pmo_q_owns = 0; +unsigned short pmo_s_upgrade = 0; +unsigned short pmo_s_sync = 0; +unsigned short pmo_s_search = 0; +unsigned short pmo_s_clean = 0; + +/* configuration options */ +char pmc_syncserver[512] = ""; +char pmc_syncpath[512] = ""; +char pmc_syncname[512] = ""; + +char *lckfile = "/tmp/pacman.lck"; + +/* list of installed packages */ +PMList *pm_packages = NULL; + +/* list of targets specified on command line */ +PMList *pm_targets = NULL; + +int main(int argc, char *argv[]) +{ + int ret = 0; + char *ptr = NULL; + char *dbpath = NULL; + pacdb_t *db_local = NULL; + PMList *lp; + + /* default root */ + MALLOC(pmo_root, PATH_MAX); + strcpy(pmo_root, "/"); + + if(argc < 2) { + usage(PM_MAIN, (char*)basename(argv[0])); + return(0); + } + + ret = parseargs(PM_ADD, argc, argv); + if(ret) { + return(ret); + } + + /* check for permission */ + if(pmo_op != PM_MAIN && pmo_op != PM_QUERY && pmo_op != PM_DEPTEST) { + if(pmo_op == PM_SYNC && pmo_s_search) { + /* special case: PM_SYNC can be used w/ pmo_s_search by any user */ + } else { + uid_t uid = geteuid(); + if(uid != 0) { + fprintf(stderr, "error: you cannot perform this operation unless you are root.\n"); + return(1); + } + } + } + + vprint("Installation Root: %s\n", pmo_root); + if(pm_targets) { + vprint("Targets:\n"); + for(lp = pm_targets; lp; lp = lp->next) { + vprint(" %s\n", lp->data); + } + } + + /* lock */ + if(lckmk(lckfile, 1, 1) == -1) { + fprintf(stderr, "error: unable to lock pacman database.\n"); + fprintf(stderr, " if you're sure pacman is not already running, you\n"); + fprintf(stderr, " can remove %s\n", lckfile); + return(127); + } + + /* set signal handlers */ + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + + /* check for db existence */ + /* add a trailing '/' if there isn't one */ + if(pmo_root[strlen(pmo_root)-1] != '/') { + MALLOC(ptr, strlen(pmo_root)+1); + strcpy(ptr, pmo_root); + strcat(ptr, "/"); + FREE(pmo_root); + pmo_root = ptr; + } + /* db location */ + MALLOC(dbpath, PATH_MAX); + snprintf(dbpath, PATH_MAX-1, "%s%s", pmo_root, PKGDIR); + vprint("Top-level DB Path: %s\n", dbpath); + + db_local = db_open(dbpath, "local"); + if(db_local == NULL) { + /* couldn't open the db directory - try creating it */ + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s/local", dbpath); + vprint("initializing database...\n", path); + ret = makepath(path); + + if(ret) { + fprintf(stderr, "error: could not create database.\n"); + cleanup(1); + } + if((db_local = db_open(dbpath, "local")) == NULL) { + fprintf(stderr, "error: could not open database.\n"); + cleanup(1); + } + } + /* load pm_packages cache */ + pm_packages = db_loadpkgs(db_local, pm_packages); + + /* start the requested operation */ + switch(pmo_op) { + case PM_ADD: ret = pacman_add(db_local, pm_targets); break; + case PM_REMOVE: ret = pacman_remove(db_local, pm_targets); break; + case PM_UPGRADE: ret = pacman_upgrade(db_local, pm_targets); break; + case PM_QUERY: ret = pacman_query(db_local, pm_targets); break; + case PM_SYNC: ret = pacman_sync(db_local, pm_targets); break; + case PM_DEPTEST: ret = pacman_deptest(db_local, pm_targets); break; + case PM_MAIN: ret = 0; break; + default: fprintf(stderr, "error: no operation specified (use -h for help)\n\n"); + ret = 1; + } + db_close(db_local); + FREE(pmo_root); + FREE(dbpath); + cleanup(ret); + /* not reached */ + return(0); +} + +int pacman_deptest(pacdb_t *db, PMList *targets) +{ + PMList *list = NULL; + PMList *lp, *deps; + pkginfo_t *dummy; + + if(pmo_vertest) { + if(targets && targets->data && targets->next && targets->next->data) { + int ret = rpmvercmp(targets->data, targets->next->data); + printf("%d\n", ret); + return(ret); + } + return(0); + } + + dummy = newpkg(); + sprintf(dummy->name, "_dummy_"); + sprintf(dummy->version, "1.0-1"); + for(lp = targets; lp; lp = lp->next) { + if(lp->data == NULL) continue; + dummy->depends = list_add(dummy->depends, lp->data); + } + list = list_add(list, dummy); + deps = checkdeps(db, PM_ADD, list); + freepkg(dummy); + list->data = NULL; + list_free(list); + + if(deps) { + for(lp = deps; lp; lp = lp->next) { + depmissing_t *miss = (depmissing_t*)lp->data; + if(miss->type == CONFLICT) { + printf("conflict: %s\n", miss->depend.name); + } else if(miss->type == DEPEND || miss->type == REQUIRED) { + printf("requires: %s", miss->depend.name); + switch(miss->depend.mod) { + case DEP_EQ: printf("=%s", miss->depend.version); break; + case DEP_GE: printf(">=%s", miss->depend.version); break; + case DEP_LE: printf("<=%s", miss->depend.version); break; + } + printf("\n"); + } + FREE(miss); + lp->data = NULL; + } + FREE(deps); + return(127); + } + return(0); +} + +int pacman_sync(pacdb_t *db, PMList *targets) +{ + char url[1024]; + char *dbpath = NULL; + int allgood = 1, confirm = 0; + pacdb_t *db_sync = NULL; + PMList *pkgcache = NULL; + PMList *i, *j; + PMList *final = NULL; + PMList *trail = NULL; + PMList *deps = NULL; + int cols; + + /* parse the system-wide config file */ + snprintf(url, 511, "/%s", PACCONF); + if(parseconfig(url)) { + return(1); + } + if(!strlen(pmc_syncserver)) { + fprintf(stderr, "error: no Sync_Server specified in %s\n", url); + return(1); + } + if(!strlen(pmc_syncname)) { + fprintf(stderr, "error: no Sync_Tree_Name specified in %s\n", url); + return(1); + } + if(!strlen(pmc_syncpath)) { + fprintf(stderr, "error: no Sync_Tree_Path specified in %s\n", url); + return(1); + } + if(pmc_syncpath[0] != '/') { + sprintf(pmc_syncpath, "/%s", pmc_syncpath); + } + + if(pmo_s_clean) { + mode_t oldmask; + + printf("removing packages from cache... "); + if(rmrf("/var/cache/pacman/pkg")) { + fprintf(stderr, "error: could not remove cache directory: %s\n", strerror(errno)); + return(1); + } + + oldmask = umask(0000); + if(mkdir("/var/cache/pacman", 0755) && mkdir("/var/cache/pacman/pkg", 0755)) { + fprintf(stderr, "error: could not create new cache directory: %s\n", strerror(errno)); + return(1); + } + umask(oldmask); + + printf("done.\n"); + return(0); + } + + if(pmo_s_sync && !pmo_s_search) { + /* grab a fresh package list */ + printf(":: Synchronizing remote package tree... \n"); + sync_synctree(); + printf("\n"); + } + + /* open the sync db */ + MALLOC(dbpath, PATH_MAX); + snprintf(dbpath, PATH_MAX-1, "%s%s", pmo_root, PKGDIR); + db_sync = db_open(dbpath, pmc_syncname); + if(db_sync == NULL) { + fprintf(stderr, "error: could not open the sync database.\n"); + fprintf(stderr, " have you used --refresh yet?\n"); + return(1); + } + /* cache packages */ + pkgcache = db_loadpkgs(db_sync, pkgcache); + + if(db_sync == NULL) { + fprintf(stderr, "error: could not open sync database (%s/%s)\n", dbpath, pmc_syncname); + return(1); + } + + final = list_new(); + trail = list_new(); + + if(pmo_s_search) { + /* search sync db */ + for(i = targets; i; i = i->next) { + char *targ = strdup(i->data); + strtoupper(targ); + for(j = pkgcache; j; j = j->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + char *haystack; + /* check name */ + haystack = strdup(pkg->name); + strtoupper(haystack); + if(strstr(haystack, targ)) { + printf("%s %s\n", pkg->name, pkg->version); + } else { + /* check description */ + FREE(haystack); + haystack = strdup(pkg->desc); + strtoupper(haystack); + if(strstr(haystack, targ)) { + printf("%s %s\n", pkg->name, pkg->version); + } + } + FREE(haystack); + } + FREE(targ); + } + } else if(pmo_s_upgrade) { + int newer = 0; + for(i = pm_packages; i && allgood; i = i->next) { + int cmp, found = 0; + pkginfo_t *local = (pkginfo_t*)i->data; + pkginfo_t *sync = NULL; + + for(j = pkgcache; !found && j; j = j->next) { + sync = (pkginfo_t*)j->data; + if(local == NULL || local->name == NULL) { + vprint("local is NULL!\n"); + } + if(sync == NULL || sync->name == NULL) { + vprint("sync is NULL!\n"); + } + if(!strcmp(local->name, sync->name)) { + found = 1; + } + } + if(!found) { + /*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, sync->version); + if(cmp > 0) { + /* local version is newer */ + fprintf(stderr, ":: %s-%s: local version is newer\n", + local->name, local->version); + newer = 1; + continue; + } else if(cmp == 0) { + /* versions are identical */ + continue; + } else { + /* re-fetch the package record with dependency info */ + sync = db_scan(db_sync, sync->name, INFRQ_DESC | INFRQ_DEPENDS); + /* add to the targets list */ + if(!is_pkgin(sync, final)) { + allgood = !resolvedeps(db, db_sync, sync, final, trail); + /* check again, as resolvedeps could have added our target for us */ + if(!is_pkgin(sync, final)) { + final = list_add(final, sync); + } + } + } + } + if(newer) { + fprintf(stderr, ":: Above packages will be skipped. To manually upgrade use 'pacman -S '\n"); + } + } else { + /* process targets */ + for(i = targets; i && allgood; i = i->next) { + if(i->data) { + int cmp; + pkginfo_t *local; + pkginfo_t *sync; + + local = db_scan(db, (char*)i->data, INFRQ_DESC); + sync = db_scan(db_sync, (char*)i->data, INFRQ_DESC | INFRQ_DEPENDS); + if(sync == NULL) { + fprintf(stderr, ":: %s: not found in sync db\n", (char*)i->data); + continue; + } + if(local) { + /* this is an upgrade, compare versions and determine if it is necessary */ + cmp = rpmvercmp(local->version, sync->version); + if(cmp > 0) { + /* local version is newer - get confirmation first */ + if(!yesno(":: %s-%s: local version is newer. Upgrade anyway? [Y/n] ", local->name, local->version)) { + continue; + } + } else if(cmp == 0) { + /* versions are identical */ + if(!yesno(":: %s-%s: is up to date. Upgrade anyway? [Y/n] ", local->name, local->version)) { + continue; + } + } + } + /* add to targets list */ + if(!is_pkgin(sync, final)) { + allgood = !resolvedeps(db, db_sync, sync, final, trail); + /* check again, as resolvedeps could have added our target for us */ + if(!is_pkgin(sync, final)) { + final = list_add(final, sync); + } + } + } + } + } + + if(allgood) { + /* check for inter-conflicts and whatnot */ + deps = checkdeps(db, PM_UPGRADE, final); + if(deps) { + fprintf(stderr, "error: unresolvable conflicts/dependencies:\n"); + for(i = deps; i; i = i->next) { + depmissing_t *miss = (depmissing_t*)i->data; + if(miss->type == CONFLICT) { + fprintf(stderr, " %s: conflicts with %s\n", miss->target, miss->depend.name); + } else if(miss->type == DEPEND || miss->type == REQUIRED) { + fprintf(stderr, " %s: requires %s", miss->target, miss->depend.name); + switch(miss->depend.mod) { + case DEP_EQ: fprintf(stderr, "=%s", miss->depend.version); break; + case DEP_GE: fprintf(stderr, ">=%s", miss->depend.version); break; + case DEP_LE: fprintf(stderr, "<=%s", miss->depend.version); break; + } + if(miss->type == DEPEND) { + fprintf(stderr, " but it is not in the sync db\n"); + } else { + fprintf(stderr, "\n"); + } + } + FREE(miss); + i->data = NULL; + } + list_free(deps); + /* abort mission */ + allgood = 0; + } + + /* list targets */ + if(final && final->data) { + fprintf(stderr, "\nTargets: "); + } + cols = 9; + for(i = final; allgood && i; i = i->next) { + pkginfo_t *p = (pkginfo_t*)i->data; + if(p) { + char t[PATH_MAX]; + int s; + snprintf(t, PATH_MAX, "%s-%s ", p->name, p->version); + s = strlen(t); + if(s+cols > 78) { + cols = 9; + fprintf(stderr, "\n%9s", " "); + } + fprintf(stderr, "%s", t); + cols += s; + } + } + printf("\n"); + + /* get confirmation */ + confirm = 0; + if(allgood && final && final->data) { + confirm = yesno("\nDo you want to install/upgrade these packages? [Y/n] "); + } + } + + if(allgood && confirm) { + PMList *files = NULL; + char ldir[PATH_MAX]; + int varcache = 1; + + /* download targets */ + for(i = final; i; i = i->next) { + pkginfo_t *p = (pkginfo_t*)i->data; + if(p) { + struct stat buf; + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%svar/cache/pacman/pkg/%s-%s.pkg.tar.gz", + pmo_root, p->name, p->version); + if(stat(path, &buf)) { + /* file is not in the cache dir, so add it to the list */ + snprintf(path, PATH_MAX, "%s-%s.pkg.tar.gz", p->name, p->version); + files = list_add(files, strdup(path)); + } + } + } + snprintf(ldir, PATH_MAX, "%svar/cache/pacman/pkg", pmo_root); + + if(files) { + struct stat buf; + + printf("\n:: Downloading packages...\n"); fflush(stdout); + if(stat(ldir, &buf)) { + mode_t oldmask; + char parent[PATH_MAX]; + + /* no cache directory.... try creating it */ + snprintf(parent, PATH_MAX, "%svar/cache/pacman", pmo_root); + fprintf(stderr, "warning: no %s cache exists. creating...\n", ldir); + oldmask = umask(0000); + if(mkdir(parent, 0755) || mkdir(ldir, 0755)) { + /* couldn't mkdir the cache directory, so fall back to /tmp and unlink + * the package afterwards. + */ + fprintf(stderr, "warning: couldn't create package cache, using /tmp instead\n"); + snprintf(ldir, PATH_MAX, "/tmp"); + varcache = 0; + } + umask(oldmask); + } + if(downloadfiles(pmc_syncserver, pmc_syncpath, ldir, files)) { + fprintf(stderr, "error: ftp transfer failed.\n"); + allgood = 0; + } + list_free(files); + files = NULL; + } + + /* install targets */ + for(i = final; allgood && i; i = i->next) { + char *str; + pkginfo_t *p = (pkginfo_t*)i->data; + if(p) { + MALLOC(str, PATH_MAX); + snprintf(str, PATH_MAX, "%s/%s-%s.pkg.tar.gz", ldir, p->name, p->version); + files = list_add(files, str); + } + } + if(allgood) { + pacman_upgrade(db, files); + } + + if(!varcache) { + /* delete packages */ + for(i = files; i; i = i->next) { + unlink(i->data); + } + } + } + + /* cleanup */ + for(i = pkgcache; i; i = i->next) { + if(i->data) freepkg((pkginfo_t*)i->data); + i->data = NULL; + } + for(i = final; i; i = i->next) { + if(i->data) freepkg((pkginfo_t*)i->data); + i->data = NULL; + } + for(i = trail; i; i = i->next) { + /* this list used the same pointers as final, so they're already freed */ + i->data = NULL; + } + list_free(pkgcache); + list_free(final); + list_free(trail); + db_close(db_sync); + return(!allgood); +} + +int pacman_add(pacdb_t *db, PMList *targets) +{ + int i, ret = 0, errors = 0; + TAR *tar = NULL; + char expath[PATH_MAX]; + char pm_install[PATH_MAX]; + pkginfo_t *info = NULL; + struct stat buf; + PMList *targ, *file, *lp, *j; + PMList *alltargs = NULL; + PMList *filenames = NULL; + unsigned short real_pmo_upgrade; + + if(targets == NULL) { + return(0); + } + + vprint("Loading all target data...\n"); + for(targ = targets; targ; targ = targ->next) { + /* Populate the package struct */ + vprint(" %s\n", (char*)targ->data); + info = load_pkg((char*)targ->data, 0); + if(info == NULL) { + return(1); + } + alltargs = list_add(alltargs, info); + filenames = list_add(filenames, strdup(targ->data)); + } + + if(!pmo_nodeps) { + vprint("Checking dependencies...\n"); + lp = checkdeps(db, (pmo_upgrade ? PM_UPGRADE : PM_ADD), alltargs); + if(lp) { + fprintf(stderr, "error: unsatisfied dependencies:\n"); + for(j = lp; j; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + printf(" %s: ", miss->target); + if(miss->type == DEPEND || miss->type == REQUIRED) { + printf("requires %s", miss->depend.name); + switch(miss->depend.mod) { + case DEP_EQ: printf("=%s", miss->depend.version); break; + case DEP_GE: printf(">=%s", miss->depend.version); break; + case DEP_LE: printf("<=%s", miss->depend.version); break; + } + printf("\n"); + } else if(miss->type == CONFLICT) { + printf("conflicts with %s\n", miss->depend.name); + } + } + list_free(lp); + return(1); + } + list_free(lp); + } + + if(!pmo_force) { + printf("checking for conflicts... "); + fflush(stdout); + lp = db_find_conflicts(db, alltargs); + if(lp) { + printf("\nerror: the following file conflicts were found:\n"); + for(j = lp; j; j = j->next) { + printf(" %s\n", (char*)j->data); + } + printf("\n"); + list_free(lp); + return(1); + } + printf("done.\n"); + list_free(lp); + } + + /* this can get modified in the next for loop, so we reset it on each iteration */ + real_pmo_upgrade = pmo_upgrade; + + for(targ = alltargs, file = filenames; targ && file; targ = targ->next, file = file->next) { + pkginfo_t* oldpkg = NULL; + info = (pkginfo_t*)targ->data; + errors = 0; + + pmo_upgrade = real_pmo_upgrade; + /* check for an already installed package */ + if(!pmo_upgrade && is_pkgin(info, pm_packages)) { + fprintf(stderr, "error: %s is already installed. (try --upgrade)\n", info->name); + continue; + } + + /* see if this is an upgrade. if so, remove the old package first */ + if(pmo_upgrade) { + if(is_pkgin(info, pm_packages)) { + PMList* tmp = list_new(); + int retcode; + + printf("upgrading %s... ", info->name); + /* we'll need the full record for backup checks later */ + oldpkg = db_scan(db, info->name, INFRQ_ALL); + + list_add(tmp, strdup(info->name)); + vprint("removing old package first...\n"); + retcode = pacman_remove(db, tmp); + list_free(tmp); + /* reload package cache */ + pm_packages = db_loadpkgs(db, pm_packages); + if(retcode == 1) { + fprintf(stderr, "\nupgrade aborted.\n"); + return(1); + } + } else { + /* no previous package version is installed, so this is actually just an + * install + */ + pmo_upgrade = 0; + } + } + if(!pmo_upgrade) { + printf("installing %s... ", info->name); + } + fflush(stdout); + + /* open the .tar.gz package */ + if(tar_open(&tar, (char*)file->data, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + perror("could not open package"); + return(1); + } + vprint("extracting files...\n"); + for(i = 0; !th_read(tar); i++) { + int nb = 0; + char *md5_orig = NULL; + char pathname[PATH_MAX]; + strncpy(pathname, th_get_pathname(tar), PATH_MAX); + + if(!strcmp(pathname, ".PKGINFO")) { + tar_skip_regfile(tar); + continue; + } + + if(!strcmp(pathname, "._install")) { + /* the install script goes inside the db */ + snprintf(expath, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, + PKGDIR, db->treename, info->name, info->version); + } else { + /* build the new pathname relative to pmo_root */ + snprintf(expath, PATH_MAX, "%s%s", pmo_root, pathname); + } + + if(!stat(expath, &buf) && !S_ISDIR(buf.st_mode)) { + /* file already exists */ + if(!pmo_upgrade) { + nb = is_in(pathname, info->backup); + } else { + /* op == PM_UPGRADE */ + md5_orig = needbackup(pathname, oldpkg->backup); + if(md5_orig) { + 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)) { + fprintf(stderr, "could not extract %s: %s\n", 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; + } + } + + vprint("checking md5 hashes for %s\n", expath); + vprint(" current: %s\n", md5_local); + vprint(" new: %s\n", md5_pkg); + if(md5_orig) { + vprint(" original: %s\n", 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)) { + fprintf(stderr, "error: could not rename %s: %s\n", expath, strerror(errno)); + } + if(copyfile(temp, expath)) { + fprintf(stderr, "error: could not copy %s to %s: %s\n", temp, expath, strerror(errno)); + errors++; + } else { + fprintf(stderr, "warning: %s saved as %s\n", 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)) { + vprint(" action: installing new file\n"); + installnew = 1; + } else { + vprint(" action: installing new file\n"); + installnew = 1; + } + } else if(!strcmp(md5_orig, md5_pkg)) { + vprint(" action: leaving existing file in place\n"); + } else if(!strcmp(md5_local, md5_pkg)) { + vprint(" action: installing new file\n"); + installnew = 1; + } else { + char newpath[PATH_MAX]; + vprint(" action: saving current file and installing new one\n"); + installnew = 1; + snprintf(newpath, PATH_MAX, "%s.pacsave", expath); + if(rename(expath, newpath)) { + fprintf(stderr, "error: could not rename %s: %s\n", expath, strerror(errno)); + } else { + fprintf(stderr, "warning: %s saved as %s\n", expath, newpath); + } + } + + if(installnew) { + /*vprint(" %s\n", expath);*/ + if(copyfile(temp, expath)) { + fprintf(stderr, "error: could not copy %s to %s: %s\n", temp, expath, strerror(errno)); + errors++; + } + } + } + + FREE(md5_local); + FREE(md5_pkg); + FREE(md5_orig); + unlink(temp); + FREE(temp); + } else { + /*vprint(" %s\n", expath);*/ + if(tar_extract_file(tar, expath)) { + fprintf(stderr, "could not extract %s: %s\n", 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", pmo_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; + fprintf(stderr, "error installing %s - skipping db update for this package\n", info->name); + } else { + time_t t = time(NULL); + + /* if this is an upgrade then propagate the old package's requiredby list over to + * the new package */ + if(pmo_upgrade && oldpkg) { + list_free(info->requiredby); + info->requiredby = NULL; + for(lp = oldpkg->requiredby; lp; lp = lp->next) { + char *s = strdup(lp->data); + info->requiredby = list_add(info->requiredby, s); + } + } + + vprint("Updating database..."); + /* make an install date (in UTC) */ + strncpy(info->installdate, asctime(gmtime(&t)), sizeof(info->installdate)); + if(db_write(db, info)) { + fprintf(stderr, "error updating database for %s!\n", info->name); + return(1); + } + vprint("done.\n"); + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + pkginfo_t *depinfo = NULL; + depend_t depend; + + if(splitdep(lp->data, &depend)) { + continue; + } + depinfo = db_scan(db, depend.name, INFRQ_ALL); + if(depinfo == NULL) { + continue; + } + depinfo->requiredby = list_add(depinfo->requiredby, strdup(info->name)); + db_write(db, depinfo); + freepkg(depinfo); + } + printf("done.\n"); fflush(stdout); + + /* run the post-install script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, PKGDIR, db->treename, info->name, info->version); + if(!stat(pm_install, &buf)) { + char cmdline[PATH_MAX+1]; + snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", PKGDIR, db->treename, info->name, info->version); + vprint("Executing post-install script...\n"); + snprintf(cmdline, PATH_MAX, "chroot %s /bin/sh %s post_%s %s %s", pmo_root, pm_install, + (pmo_upgrade ? "upgrade" : "install"), info->version, (pmo_upgrade ? oldpkg->version : "")); + system(cmdline); + } + } + + freepkg(oldpkg); + } + + /* clean up */ + for(lp = alltargs; lp; lp = lp->next) { + freepkg((pkginfo_t*)lp->data); + lp->data = NULL; + } + list_free(alltargs); + list_free(filenames); + + /* run ldconfig if it exists */ + snprintf(expath, PATH_MAX, "%setc/ld.so.conf", pmo_root); + if(!stat(expath, &buf)) { + snprintf(expath, PATH_MAX, "%ssbin/ldconfig", pmo_root); + if(!stat(expath, &buf)) { + char cmd[PATH_MAX]; + snprintf(cmd, PATH_MAX, "%s -r %s", expath, pmo_root); + vprint("running \"%s\"\n", cmd); + system(cmd); + } + } + + return(ret); +} + +int pacman_remove(pacdb_t *db, PMList *targets) +{ + char line[PATH_MAX+1]; + char pm_install[PATH_MAX+1]; + pkginfo_t *info = NULL; + pkginfo_t *depinfo = NULL; + struct stat buf; + char *newpath = NULL; + depend_t depend; + PMList *alltargs = NULL; + PMList *targ, *lp, *j; + + if(targets == NULL) { + return(0); + } + + /* load package info from all targets */ + for(lp = targets; lp; lp = lp->next) { + info = db_scan(db, (char*)lp->data, INFRQ_ALL); + if(info == NULL) { + fprintf(stderr, "error: could not find %s in database\n", (char*)lp->data); + return(1); + } + alltargs = list_add(alltargs, info); + } + if(!pmo_nodeps && !pmo_upgrade) { + vprint("Checking dependencies...\n"); + lp = checkdeps(db, PM_REMOVE, alltargs); + if(lp) { + fprintf(stderr, "error: this will break the following dependencies:\n"); + for(j = lp; j; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + printf(" %s: is required by %s\n", miss->target, miss->depend.name); + } + list_free(lp); + return(1); + } + list_free(lp); + } + + for(targ = alltargs; targ; targ = targ->next) { + info = (pkginfo_t*)targ->data; + + if(!pmo_upgrade) { + printf("removing %s... ", info->name); + fflush(stdout); + /* run the pre-remove script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, PKGDIR, db->treename, info->name, info->version); + if(!stat(pm_install, &buf)) { + vprint("Executing pre-remove script...\n"); + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, PKGDIR, db->treename, info->name, info->version); + snprintf(line, PATH_MAX, "chroot %s /bin/sh %s pre_remove %s", pmo_root, pm_install, info->version); + + system(line); + } + } + + /* iterate through the list backwards, unlinking files */ + for(lp = list_last(info->files); lp; lp = lp->prev) { + int nb = 0; + if(needbackup((char*)lp->data, info->backup)) { + nb = 1; + } + snprintf(line, PATH_MAX, "%s%s", pmo_root, (char*)lp->data); + if(lstat(line, &buf)) { + vprint("file %s does not exist\n", line); + continue; + } + if(S_ISDIR(buf.st_mode)) { + /*vprint(" removing directory %s\n", 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(pmo_upgrade) { + /* we're upgrading so just leave the file as is. pacman_add() will handle it */ + } else { + if(!pmo_nosave) { + newpath = (char*)realloc(newpath, strlen(line)+strlen(".pacsave")+1); + sprintf(newpath, "%s.pacsave", line); + rename(line, newpath); + fprintf(stderr, "warning: %s saved as %s\n", line, newpath); + } else { + /*vprint(" unlinking %s\n", line);*/ + if(unlink(line)) { + perror("cannot remove file"); + } + } + } + } else { + /*vprint(" unlinking %s\n", line);*/ + if(unlink(line)) { + perror("cannot remove file"); + } + } + } + } + + /* remove the package from the database */ + snprintf(line, PATH_MAX, "%s%s/%s/%s-%s", pmo_root, PKGDIR, db->treename, + info->name, info->version); + + /* DESC */ + snprintf(pm_install, PATH_MAX, "%s/desc", line); + unlink(pm_install); + /* FILES */ + snprintf(pm_install, PATH_MAX, "%s/files", line); + unlink(pm_install); + /* DEPENDS */ + snprintf(pm_install, PATH_MAX, "%s/depends", line); + unlink(pm_install); + /* INSTALL */ + snprintf(pm_install, PATH_MAX, "%s/install", line); + unlink(pm_install); + /* directory */ + rmdir(line); + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + PMList *last, *j; + + if(splitdep((char*)lp->data, &depend)) { + continue; + } + depinfo = db_scan(db, depend.name, INFRQ_ALL); + if(depinfo == NULL) { + continue; + } + /* splice out this entry from requiredby */ + last = list_last(depinfo->requiredby); + 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; + list_free(j); + break; + } + } + db_write(db, depinfo); + freepkg(depinfo); + } + if(!pmo_upgrade) { + printf("done.\n"); + } + } + + return(0); +} + +int pacman_query(pacdb_t *db, PMList *targets) +{ + char *package = NULL; + char path[PATH_MAX+1]; + pkginfo_t *info = NULL; + PMList *targ, *lp, *q; + int done = 0; + + for(targ = targets; !done; targ = (targ ? targ->next : NULL)) { + if(targets == NULL) { + done = 1; + } else { + if(targ->next == NULL) { + done = 1; + } + package = (char*)targ->data; + } + /* output info for a .tar.gz package */ + if(pmo_q_isfile) { + if(package == NULL) { + fprintf(stderr, "error: no package file was specified (-p)\n"); + return(1); + } + info = load_pkg(package, pmo_q_info); + if(info == NULL) { + fprintf(stderr, "error: %s is not a package\n", package); + return(1); + } + if(pmo_q_list) { + for(lp = info->files; lp; lp = lp->next) { + if(strcmp(lp->data, ".PKGINFO")) { + printf("%s\n", (char*)lp->data); + } + } + } else { + if(!pmo_q_info) { + printf("%s %s\n", info->name, info->version); + } + } + continue; + } + + /* determine the owner of a file */ + if(pmo_q_owns) { + char rpath[PATH_MAX]; + if(package == NULL) { + fprintf(stderr, "error: no file was specified for --owns\n"); + return(1); + } + if(realpath(package, rpath)) { + int gotcha = 0; + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_FILES)) != NULL && !gotcha) { + for(lp = info->files; lp; lp = lp->next) { + sprintf(path, "%s%s", pmo_root, (char*)lp->data); + if(!strcmp(path, rpath)) { + printf("%s %s\n", info->name, info->version); + gotcha = 1; + } + } + } + if(!gotcha) { + fprintf(stderr, "No package owns %s\n", package); + } + return(2); + } else { + fprintf(stderr, "error: %s is not a file.\n", package); + return(1); + } + } + + /* find packages in the db */ + if(package == NULL) { + /* no target */ + for(lp = pm_packages; lp; lp = lp->next) { + pkginfo_t *tmpp = (pkginfo_t*)lp->data; + if(pmo_q_list) { + info = db_scan(db, tmpp->name, INFRQ_DESC | INFRQ_FILES); + if(info == NULL) { + /* something weird happened */ + return(1); + } + for(q = info->files; q; q = q->next) { + printf("%s%s\n", pmo_root, (char*)q->data); + } + } else { + printf("%s %s\n", tmpp->name, tmpp->version); + } + } + } else { + /* find a target */ + if(pmo_q_list) { + info = db_scan(db, package, INFRQ_DESC | INFRQ_FILES); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + for(lp = info->files; lp; lp = lp->next) { + printf("%s%s\n", pmo_root, (char*)lp->data); + } + } else if(pmo_q_info) { + int cols; + + info = db_scan(db, package, INFRQ_DESC | INFRQ_DEPENDS); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + + printf("Name : %s\n", info->name); + printf("Version : %s\n", info->version); + printf("Packager : %s\n", info->packager); + printf("Size : %ld\n", info->size); + printf("Build Date : %s %s\n", info->builddate, strlen(info->builddate) ? "UTC" : ""); + printf("Install Date : %s %s\n", info->installdate, strlen(info->installdate) ? "UTC" : ""); + printf("Install Script: %s\n", (info->scriptlet ? "yes" : "no")); + printf("Depends On : "); + if(info->depends) { + for(lp = info->depends, cols = 16; lp; lp = lp->next) { + int s = strlen((char*)lp->data)+1; + if(s+cols > 79) { + cols = 16; + printf("\n%16s%s ", " ", (char*)lp->data); + } else { + printf("%s ", (char*)lp->data); + } + cols += s; + } + printf("\n"); + } else { + printf("None\n"); + } + printf("Required By : "); + if(info->requiredby) { + for(lp = info->requiredby, cols = 16; lp; lp = lp->next) { + int s = strlen((char*)lp->data)+1; + if(s+cols > 79) { + cols = 16; + printf("\n%16s%s ", " ", (char*)lp->data); + } else { + printf("%s ", (char*)lp->data); + } + cols += s; + } + printf("\n"); + } else { + printf("None\n"); + } + printf("Conflicts With: "); + if(info->conflicts) { + for(lp = info->conflicts, cols = 16; lp; lp = lp->next) { + int s = strlen((char*)lp->data)+1; + if(s+cols > 79) { + cols = 16; + printf("\n%16s%s ", " ", (char*)lp->data); + } else { + printf("%s ", (char*)lp->data); + } + cols += s; + } + printf("\n"); + } else { + printf("None\n"); + } + printf("Description : %s\n", info->desc); + printf("\n"); + } else { + info = db_scan(db, package, INFRQ_DESC); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + printf("%s %s\n", info->name, info->version); + } + } + } + + if(info) { + freepkg(info); + } + + return(0); +} + +int pacman_upgrade(pacdb_t *db, PMList *targets) +{ + /* this is basically just a remove,add process. pacman_add() will */ + /* handle it */ + pmo_upgrade = 1; + return(pacman_add(db, targets)); +} + +/* populates *list with packages that need to be installed to satisfy all + * dependencies (recursive) for *package + * + * make sure *list and *trail are already initialized + */ +int resolvedeps(pacdb_t *local, pacdb_t *sync, pkginfo_t *package, PMList *list, PMList *trail) +{ + pkginfo_t *info; + PMList *i; + PMList *targ = NULL; + PMList *deps = NULL; + + targ = list_new(); + targ = list_add(targ, package); + deps = checkdeps(local, PM_ADD, targ); + targ->data = NULL; + list_free(targ); + for(i = deps; i; i = i->next) { + depmissing_t *miss = (depmissing_t*)i->data; + info = db_scan(sync, miss->depend.name, INFRQ_DESC | INFRQ_DEPENDS); + if(info == NULL) { + fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target); + fprintf(stderr, " \"%s\" is not in the package set\n", miss->depend.name); + return(1); + } + if(is_pkgin(info, list) == 1) { + /* this dep is already in the target list */ + continue; + } + if(miss->type == CONFLICT) { + fprintf(stderr, "error: %s conflicts with %s\n", miss->target, miss->depend.name); + return(1); + } else if(miss->type == DEPEND) { + /*printf("resolving %s\n", info->name); fflush(stdout);*/ + if(!is_pkgin(info, trail)) { + list_add(trail, info); + if(resolvedeps(local, sync, info, list, trail)) { + return(1); + } + list_add(list, info); + } else { + /* cycle detected -- skip it */ + /*printf("cycle detected\n"); fflush(stdout);*/ + } + } + } + return(0); +} + +/* + * returns a PMList* of missing_t pointers. + * + * conflicts are always name only, but dependencies can include versions + * with depmod operators. + * + */ +PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) +{ + pkginfo_t *info = NULL; + depend_t depend; + PMList *i, *j, *k; + int cmp; + int found = 0; + PMList *baddeps = NULL; + depmissing_t *miss = NULL; + + if(op == PM_UPGRADE) { + /* PM_UPGRADE handles the backwards dependencies, ie, the packages + * listed in the requiredby field. + */ + for(i = targets; i; i = i->next) { + pkginfo_t *tp, *oldpkg; + if(i->data == NULL) { + continue; + } + tp = (pkginfo_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; + pkginfo_t *p; + found = 0; + if((p = db_scan(db, j->data, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) { + continue; + } + for(k = p->depends; k; k = k->next) { + if(splitdep(k->data, &depend)) { + continue; + } + if(!strcmp(depend.name, oldpkg->name)) { + found = 1; + } + } + if(found == 0) { + continue; + } + found = 0; + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + if(!found) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = 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); + baddeps = list_add(baddeps, miss); + } + } + freepkg(oldpkg); + } + } + if(op == PM_ADD || op == PM_UPGRADE) { + for(i = targets; i; i = i->next) { + pkginfo_t *tp; + if(i->data == NULL) { + continue; + } + tp = (pkginfo_t*)i->data; + + /* CONFLICTS */ + for(j = tp->conflicts; j; j = j->next) { + /* check targets against database */ + for(k = pm_packages; k; k = k->next) { + pkginfo_t *dp = (pkginfo_t*)k->data; + if(!strcmp(j->data, dp->name)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, dp->name, 256); + baddeps = list_add(baddeps, miss); + } + } + /* check targets against targets */ + for(k = targets; k; k = k->next) { + pkginfo_t *a = (pkginfo_t*)k->data; + if(!strcmp(a->name, (char*)j->data)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, a->name, 256); + baddeps = list_add(baddeps, miss); + } + } + } + /* check database against targets */ + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_DEPENDS)) != NULL) { + for(j = info->conflicts; j; j = j->next) { + if(!strcmp((char*)j->data, tp->name)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, (char*)j->data, 256); + baddeps = list_add(baddeps, miss); + } + } + } + + /* DEPENDENCIES */ + /* cycle thru this targets dependency list */ + for(j = tp->depends; j; j = j->next) { + /* split into name/version pairs */ + if(splitdep((char*)j->data, &depend)) { + fprintf(stderr, "warning: invalid dependency in %s\n", (char*)tp->name); + continue; + } + found = 0; + /* check database */ + for(k = pm_packages; k && !found; k = k->next) { + pkginfo_t *p = (pkginfo_t*)k->data; + if(!strcmp(p->name, depend.name)) { + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check other targets */ + for(k = targets; k && !found; k = k->next) { + pkginfo_t *p = (pkginfo_t*)k->data; + if(!strcmp(p->name, depend.name)) { + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + /* TODO: switch positions in targets if one package should precede + * the other (wrt deps) + */ + } + if(!found) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = 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); + baddeps = list_add(baddeps, miss); + } + } + } + } else if(op == PM_REMOVE) { + /* check requiredby fields */ + for(i = targets; i; i = i->next) { + pkginfo_t* tp; + if(i->data == NULL) { + continue; + } + tp = (pkginfo_t*)i->data; + for(j = tp->requiredby; j; j = j->next) { + if(!is_in((char*)j->data, targets)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = REQUIRED; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, (char*)j->data, 256); + baddeps = list_add(baddeps, miss); + } + } + } + } + + return(baddeps); +} + +int splitdep(char *depstr, depend_t *depend) +{ + char *str = NULL; + char *ptr = NULL; + + str = strdup(depstr); + + if((ptr = strstr(str, ">="))) { + depend->mod = DEP_GE; + } else if((ptr = strstr(str, "<="))) { + depend->mod = DEP_LE; + } else if((ptr = strstr(str, "="))) { + depend->mod = DEP_EQ; + } else { + /* no version specified - accept any */ + depend->mod = DEP_ANY; + strcpy(depend->name, str); + strcpy(depend->version, ""); + } + + if(ptr == NULL) { + return(0); + } + *ptr = '\0'; + strcpy(depend->name, str); + ptr++; + if(depend->mod != DEP_EQ) { + ptr++; + } + strcpy(depend->version, ptr); + FREE(str); + return(0); +} + +int lckmk(char *file, int retries, unsigned int sleep_secs) +{ + int fd, count = 0; + + while((fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000)) == -1 && errno == EACCES) { + if(++count < retries) { + sleep(sleep_secs); + } else { + return(-1); + } + } + return(fd > 0 ? 0 : -1); +} + +int lckrm(char *file) +{ + return(unlink(file)); +} + +void cleanup(int signum) +{ + if(lckrm(lckfile)) { + fprintf(stderr, "warning: could not remove lock file %s\n", lckfile); + } + exit(signum); +} + +/* vim: set ts=2 sw=2 noet: */ -- cgit v1.2.3-24-g4f1b