summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/convertdb.c163
-rw-r--r--src/db.c508
-rw-r--r--src/db.h46
-rw-r--r--src/list.c122
-rw-r--r--src/list.h20
-rw-r--r--src/md5.c338
-rw-r--r--src/md5.h48
-rw-r--r--src/md5driver.c83
-rw-r--r--src/package.c228
-rw-r--r--src/package.h67
-rw-r--r--src/pacman.c1660
-rw-r--r--src/pacman.h62
-rw-r--r--src/pacsync.c183
-rw-r--r--src/pacsync.h29
-rw-r--r--src/rpmvercmp.c120
-rw-r--r--src/util.c571
-rw-r--r--src/util.h52
17 files changed, 4300 insertions, 0 deletions
diff --git a/src/convertdb.c b/src/convertdb.c
new file mode 100644
index 00000000..b3387162
--- /dev/null
+++ b/src/convertdb.c
@@ -0,0 +1,163 @@
+/*
+ * pacman
+ *
+ * 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 <ctype.h>
+#include <limits.h>
+#include <string.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "list.h"
+
+char* trim(char *str);
+
+int main(int argc, char* argv[])
+{
+ FILE* db = NULL;
+ FILE* fp = NULL;
+ char* ptr = NULL;
+ char* dbdir = "/var/lib/pacman/pacman.db";
+ char name[256];
+ char ver[256];
+ char line[PATH_MAX+1];
+ char topdir[PATH_MAX+1];
+ char path[PATH_MAX+1];
+ mode_t oldumask;
+ struct stat buf;
+
+ if(argc < 2) {
+ printf("converts a pacman 1.x database to a pacman 2.0 format\n");
+ printf("usage: %s <target_dir>\n\n", basename(argv[0]));
+ printf("convertdb will convert all package data from /var/lib/pacman/pacman.db\n");
+ printf("to a 2.0 format and place it in target_dir.\n\n");
+ return(0);
+ }
+
+ db = fopen(dbdir, "r");
+ if(db == NULL) {
+ perror(dbdir);
+ return(1);
+ }
+
+ oldumask = umask(0000);
+ while(fgets(name, 255, db)) {
+ PMList *backup = NULL;
+ PMList *lp;
+
+ if(!fgets(ver, 255, db)) {
+ perror(dbdir);
+ return(1);
+ }
+ trim(name);
+ trim(ver);
+ fprintf(stderr, "converting %s\n", name);
+ /* package directory */
+ snprintf(topdir, PATH_MAX, "%s/%s-%s", argv[1], name, ver);
+ mkdir(topdir, 0755);
+
+ /* DESC */
+ snprintf(path, PATH_MAX, "%s/desc", topdir);
+ if(!(fp = fopen(path, "w"))) {
+ perror(path);
+ return(1);
+ }
+ fputs("%NAME%\n", fp);
+ fprintf(fp, "%s\n\n", name);
+ fputs("%VERSION%\n", fp);
+ fprintf(fp, "%s\n\n", ver);
+ fputs("%DESC%\n\n", fp);
+ fclose(fp);
+
+ /* DEPENDS */
+ snprintf(path, PATH_MAX, "%s/depends", topdir);
+ if(!(fp = fopen(path, "w"))) {
+ perror(path);
+ return(1);
+ }
+ fputs("%DEPENDS%\n\n", fp);
+ fputs("%REQUIREDBY%\n\n", fp);
+ fputs("%CONFLICTS%\n\n", fp);
+ fclose(fp);
+
+ /* FILES */
+ snprintf(path, PATH_MAX, "%s/files", topdir);
+ if(!(fp = fopen(path, "w"))) {
+ perror(path);
+ return(1);
+ }
+ fputs("%FILES%\n", fp);
+ while(fgets(line, 255, db) && strcmp(trim(line), "")) {
+ trim(line);
+ ptr = line;
+
+ /* check for backup designation and frontslashes that shouldn't be there */
+ if(line[0] == '*') ptr++;
+ if(*ptr == '/') ptr++;
+ if(line[0] == '*') backup = list_add(backup, strdup(ptr));
+
+ fprintf(fp, "%s\n", ptr);
+ }
+ fprintf(fp, "\n");
+ fputs("%BACKUP%\n", fp);
+ for(lp = backup; lp; lp = lp->next) {
+ /* print the filename and a bad md5 hash. we just use 32 f's cuz we can't
+ * md5 the original file since we don't have it
+ */
+ fprintf(fp, "%s\tffffffffffffffffffffffffffffffff\n", (char*)lp->data);
+ }
+ fputs("\n", fp);
+ fclose(fp);
+ snprintf(path, PATH_MAX, "/var/lib/pacman/scripts/%s", name);
+ if(!stat(path, &buf)) {
+ snprintf(line, PATH_MAX, "/bin/cp %s %s/install", path, topdir);
+ system(line);
+ }
+ list_free(backup);
+ }
+ umask(oldumask);
+ return(0);
+}
+
+/* Trim whitespace and newlines from a string
+ */
+char* trim(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;
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/db.c b/src/db.c
new file mode 100644
index 00000000..f747be5b
--- /dev/null
+++ b/src/db.c
@@ -0,0 +1,508 @@
+/*
+ * pacman
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "util.h"
+#include "pacman.h"
+
+extern PMList *pm_packages;
+extern char *pmo_root;
+extern unsigned short pmo_upgrade;
+
+extern int errno;
+
+/* Verify database integrity and build a list of
+ * installed packages
+ *
+ * returns: 0 on success
+ * 1 if db is not initialized
+ * 2 if db is corrupt
+ */
+pacdb_t* db_open(char *dbpath, char *treename)
+{
+ pacdb_t *db = NULL;
+ char path[PATH_MAX];
+
+ MALLOC(db, sizeof(pacdb_t));
+ snprintf(path, PATH_MAX-1, "%s/%s", dbpath, treename);
+ db->dir = opendir(path);
+ if(db->dir == NULL) {
+ return(NULL);
+ }
+ strncpy(db->treename, treename, 128);
+
+ return(db);
+}
+
+void db_close(pacdb_t* db)
+{
+ closedir(db->dir);
+ return;
+}
+
+/* frees pkgcache if necessary and returns a new package
+ * cache from db
+ */
+PMList* db_loadpkgs(pacdb_t *db, PMList *pkgcache)
+{
+ pkginfo_t *info;
+ PMList *lp;
+ pkginfo_t **arr = NULL;
+ unsigned int arrct = 0;
+ int i;
+ PMList *cache = NULL;
+
+ /* if pm_packages already contains data, free it first */
+ for(lp = pkgcache; lp; lp = lp->next) {
+ if(lp->data) {
+ freepkg(lp->data);
+ lp->data = NULL;
+ }
+ }
+ list_free(pkgcache);
+
+ rewinddir(db->dir);
+ while((info = db_scan(db, NULL, INFRQ_DESC)) != NULL) {
+ /* add to the collective */
+ /* we load all package names into a linear array first, so qsort can handle it */
+ if(arr == NULL) {
+ arr = (pkginfo_t**)malloc(sizeof(pkginfo_t*));
+ arrct++;
+ } else {
+ arr = (pkginfo_t**)realloc(arr, (++arrct)*sizeof(pkginfo_t*));
+ }
+ if(arr == NULL) {
+ fprintf(stderr, "error: out of memory\n");
+ exit(1);
+ }
+ arr[arrct-1] = info;
+ }
+
+ /* sort the package list */
+ qsort(arr, (size_t)arrct, sizeof(pkginfo_t*), pkgcmp);
+
+ /* now load them into the proper PMList */
+ for(i = 0; i < arrct; i++) {
+ cache = list_add(cache, arr[i]);
+ }
+
+ return(cache);
+}
+
+pkginfo_t* db_scan(pacdb_t *db, char *target, unsigned int inforeq)
+{
+ struct dirent *ent = NULL;
+ char name[256];
+ char *ptr = NULL;
+ int found = 0;
+
+ if(target != NULL) {
+ /* search for a specific package (by name only) */
+ rewinddir(db->dir);
+ /* read the . and .. first */
+ ent = readdir(db->dir);
+ ent = readdir(db->dir);
+
+ ent = readdir(db->dir);
+ while(!found && ent != NULL) {
+ 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;
+ continue;
+ }
+ ent = readdir(db->dir);
+ }
+ if(!found) {
+ return(NULL);
+ }
+ } else {
+ /* normal iteration */
+ ent = readdir(db->dir);
+ if(ent && !strcmp(ent->d_name, ".")) {
+ ent = readdir(db->dir);
+ }
+ if(ent && !strcmp(ent->d_name, "..")) {
+ ent = readdir(db->dir);
+ }
+ if(ent == NULL) {
+ return(NULL);
+ }
+ }
+ return(db_read(db, ent, inforeq));
+}
+
+pkginfo_t* db_read(pacdb_t *db, struct dirent *ent, unsigned int inforeq)
+{
+ FILE *fp = NULL;
+ struct stat buf;
+ pkginfo_t *info = NULL;
+ char path[PATH_MAX];
+ char topdir[PATH_MAX];
+ char line[512];
+
+ if(ent == NULL) {
+ return(NULL);
+ }
+ snprintf(topdir, PATH_MAX, "%s%s/%s", pmo_root, PKGDIR, db->treename);
+ info = newpkg();
+
+ /* we always load DESC */
+ inforeq |= INFRQ_DESC;
+
+ snprintf(path, PATH_MAX, "%s/%s", topdir, ent->d_name);
+ if(stat(path, &buf)) {
+ /* directory doesn't exist or can't be opened */
+ return(NULL);
+ }
+
+ /* DESC */
+ if(inforeq & INFRQ_DESC) {
+ snprintf(path, PATH_MAX, "%s/%s/desc", topdir, ent->d_name);
+ fp = fopen(path, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "error: %s: %s\n", path, strerror(errno));
+ return(NULL);
+ }
+ while(!feof(fp)) {
+ if(fgets(line, 256, fp) == NULL) {
+ break;
+ }
+ trim(line);
+ if(!strcmp(line, "%NAME%")) {
+ if(fgets(info->name, 256, fp) == NULL) {
+ return(NULL);
+ }
+ trim(info->name);
+ } else if(!strcmp(line, "%VERSION%")) {
+ if(fgets(info->version, 64, fp) == NULL) {
+ return(NULL);
+ }
+ trim(info->version);
+ } else if(!strcmp(line, "%DESC%")) {
+ if(fgets(info->desc, 512, fp) == NULL) {
+ return(NULL);
+ }
+ trim(info->desc);
+ } else if(!strcmp(line, "%BUILDDATE%")) {
+ if(fgets(info->builddate, 32, fp) == NULL) {
+ return(NULL);
+ }
+ trim(info->builddate);
+ } else if(!strcmp(line, "%INSTALLDATE%")) {
+ if(fgets(info->installdate, 32, fp) == NULL) {
+ return(NULL);
+ }
+ trim(info->installdate);
+ } else if(!strcmp(line, "%PACKAGER%")) {
+ if(fgets(info->packager, 64, fp) == NULL) {
+ return(NULL);
+ }
+ trim(info->packager);
+ } else if(!strcmp(line, "%SIZE%")) {
+ char tmp[32];
+ if(fgets(tmp, sizeof(tmp), fp) == NULL) {
+ return(NULL);
+ }
+ trim(tmp);
+ info->size = atol(tmp);
+ }
+ }
+ fclose(fp);
+ }
+
+ /* FILES */
+ if(inforeq & INFRQ_FILES) {
+ snprintf(path, PATH_MAX, "%s/%s/files", topdir, ent->d_name);
+ fp = fopen(path, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "error: %s: %s\n", path, strerror(errno));
+ return(NULL);
+ }
+ while(fgets(line, 256, fp)) {
+ trim(line);
+ if(!strcmp(line, "%FILES%")) {
+ while(fgets(line, 512, fp) && strlen(trim(line))) {
+ char *s = strdup(line);
+ info->files = list_add(info->files, s);
+ }
+ }
+ if(!strcmp(line, "%BACKUP%")) {
+ while(fgets(line, 512, fp) && strlen(trim(line))) {
+ char *s = strdup(line);
+ info->backup = list_add(info->backup, s);
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+ /* DEPENDS */
+ if(inforeq & INFRQ_DEPENDS) {
+ snprintf(path, PATH_MAX, "%s/%s/depends", topdir, ent->d_name);
+ fp = fopen(path, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "db_read: error: %s: %s\n", path, strerror(errno));
+ return(NULL);
+ }
+ while(!feof(fp)) {
+ fgets(line, 255, fp);
+ trim(line);
+ if(!strcmp(line, "%DEPENDS%")) {
+ while(fgets(line, 512, fp) && strlen(trim(line))) {
+ char *s = strdup(line);
+ info->depends = list_add(info->depends, s);
+ }
+ }
+ if(!strcmp(line, "%REQUIREDBY%")) {
+ while(fgets(line, 512, fp) && strlen(trim(line))) {
+ char *s = strdup(line);
+ info->requiredby = list_add(info->requiredby, s);
+ }
+ }
+ if(!strcmp(line, "%CONFLICTS%")) {
+ while(fgets(line, 512, fp) && strlen(trim(line))) {
+ char *s = strdup(line);
+ info->conflicts = list_add(info->conflicts, s);
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+ /* INSTALL */
+ snprintf(path, PATH_MAX, "%s/%s/install", topdir, ent->d_name);
+ if(!stat(path, &buf)) {
+ info->scriptlet = 1;
+ }
+
+ return(info);
+}
+
+int db_write(pacdb_t *db, pkginfo_t *info)
+{
+ FILE *fp = NULL;
+ char topdir[PATH_MAX];
+ char path[PATH_MAX];
+ mode_t oldmask;
+ PMList *lp = NULL;
+
+ if(info == NULL) {
+ return(1);
+ }
+ snprintf(topdir, PATH_MAX, "%s%s/%s/%s-%s", pmo_root, PKGDIR, db->treename,
+ info->name, info->version);
+
+ oldmask = umask(0000);
+ mkdir(topdir, 0755);
+ umask(oldmask);
+
+ /* DESC */
+ snprintf(path, PATH_MAX, "%s/desc", topdir);
+ fp = fopen(path, "w");
+ if(fp == NULL) {
+ perror("db_write");
+ 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("%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);
+ fclose(fp);
+
+ /* FILES */
+ snprintf(path, PATH_MAX, "%s/files", topdir);
+ fp = fopen(path, "w");
+ if(fp == NULL) {
+ perror("db_write");
+ 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 */
+ snprintf(path, PATH_MAX, "%s/depends", topdir);
+ fp = fopen(path, "w");
+ if(fp == NULL) {
+ perror("db_write");
+ 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");
+ fclose(fp);
+
+ /* INSTALL */
+ /* nothing needed here (script is automatically extracted) */
+
+ return(0);
+}
+
+
+PMList* db_find_conflicts(pacdb_t *db, PMList *targets)
+{
+ PMList *i, *j, *k;
+ char *filestr = NULL;
+ char path[PATH_MAX+1];
+ char *str = NULL;
+ struct stat buf;
+ PMList *conflicts = 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.
+ *
+ pkginfo_t *info = NULL;
+ char *dbstr = NULL;
+ vprint("Checking database against targets...\n");
+ 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) {
+ pkginfo_t *targ = (pkginfo_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 = list_add(conflicts, str);
+ }
+ }
+ }
+ }
+ }
+ }*/
+
+ /* CHECK 2: check every target against every target */
+ vprint("Checking targets against targets...\n");
+ for(i = targets; i; i = i->next) {
+ pkginfo_t *p1 = (pkginfo_t*)i->data;
+ for(j = i; j; j = j->next) {
+ pkginfo_t *p2 = (pkginfo_t*)j->data;
+ if(strcmp(p1->name, p2->name)) {
+ for(k = p1->files; k; k = k->next) {
+ filestr = k->data;
+ if(!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(is_in(filestr, p2->files)) {
+ MALLOC(str, 512);
+ snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (target)",
+ filestr, p1->name, p2->name);
+ conflicts = list_add(conflicts, str);
+ }
+ }
+ }
+ }
+ }
+
+ /* CHECK 3: check every target against the filesystem */
+ vprint("Checking targets against filesystem...\n");
+ for(i = targets; i; i = i->next) {
+ pkginfo_t *p = (pkginfo_t*)i->data;
+ pkginfo_t *dbpkg = NULL;
+ for(j = p->files; j; j = j->next) {
+ filestr = (char*)j->data;
+ snprintf(path, PATH_MAX, "%s%s", pmo_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 && is_in(j->data, dbpkg->files)) {
+ ok = 1;
+ }
+ if(!ok) {
+ MALLOC(str, 512);
+ snprintf(str, 512, "%s: exists in filesystem", path);
+ conflicts = list_add(conflicts, str);
+ }
+ }
+ }
+ freepkg(dbpkg);
+ dbpkg = NULL;
+ }
+
+ return(conflicts);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/db.h b/src/db.h
new file mode 100644
index 00000000..7428e270
--- /dev/null
+++ b/src/db.h
@@ -0,0 +1,46 @@
+/*
+ * pacman
+ *
+ * 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 _PAC_DB_H
+#define _PAC_DB_H
+
+#include <dirent.h>
+
+/* info requests for db_read */
+#define INFRQ_DESC 0x01
+#define INFRQ_DEPENDS 0x02
+#define INFRQ_FILES 0x04
+#define INFRQ_ALL 0xFF
+
+typedef struct __pacdb_t {
+ char treename[128];
+ DIR* dir;
+} pacdb_t;
+
+pacdb_t* db_open(char *dbpath, char *treename);
+void db_close(pacdb_t *db);
+PMList* db_loadpkgs(pacdb_t *db, PMList *pkgcache);
+pkginfo_t* db_scan(pacdb_t *db, char *target, unsigned int inforeq);
+pkginfo_t* db_read(pacdb_t *db, struct dirent *ent, unsigned int inforeq);
+int db_write(pacdb_t *db, pkginfo_t *info);
+PMList* db_find_conflicts(pacdb_t *db, PMList* targets);
+
+#endif
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/list.c b/src/list.c
new file mode 100644
index 00000000..2a33b6e3
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,122 @@
+/*
+ * pacman
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "list.h"
+
+PMList* 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 list_free(PMList *list)
+{
+ if(list == NULL) {
+ return;
+ }
+ if(list->data != NULL) {
+ free(list->data);
+ list->data = NULL;
+ }
+ if(list->next != NULL) {
+ list_free(list->next);
+ }
+ free(list);
+ return;
+}
+
+PMList* list_add(PMList *list, void *data)
+{
+ PMList *ptr, *lp;
+
+ ptr = list;
+ if(ptr == NULL) {
+ ptr = list_new();
+ }
+
+ lp = list_last(ptr);
+ if(lp == ptr && lp->data == NULL) {
+ /* nada */
+ } else {
+ lp->next = list_new();
+ if(lp->next == NULL) {
+ return(NULL);
+ }
+ lp->next->prev = lp;
+ lp = lp->next;
+ }
+ lp->data = data;
+ return(ptr);
+}
+
+int list_count(PMList *list)
+{
+ int i;
+ PMList *lp;
+
+ for(lp = list, i = 0; lp; lp = lp->next, i++);
+ return(i);
+}
+
+/* List one is extended and returned
+ * List two is freed (but not its data)
+ */
+PMList* list_merge(PMList *one, PMList *two)
+{
+ PMList *lp;
+
+ for(lp = two; lp; lp = lp->next) {
+ if(lp->data) {
+ list_add(one, lp->data);
+ lp->data = NULL;
+ }
+ }
+ list_free(two);
+
+ return(one);
+}
+
+PMList* 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/src/list.h b/src/list.h
new file mode 100644
index 00000000..9749d934
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,20 @@
+#ifndef _PAC_LIST_H
+#define _PAC_LIST_H
+
+/* your average linked list */
+typedef struct __pmlist_t {
+ void* data;
+ struct __pmlist_t* prev;
+ struct __pmlist_t* next;
+} PMList;
+
+PMList* list_new();
+void list_free(PMList* list);
+PMList* list_add(PMList* list, void* data);
+int list_count(PMList* list);
+PMList* list_merge(PMList *one, PMList *two);
+PMList* list_last(PMList* list);
+
+#endif
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/md5.c b/src/md5.c
new file mode 100644
index 00000000..fcb1611e
--- /dev/null
+++ b/src/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/src/md5.h b/src/md5.h
new file mode 100644
index 00000000..b5ce6fd9
--- /dev/null
+++ b/src/md5.h
@@ -0,0 +1,48 @@
+/* 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 *);
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/md5driver.c b/src/md5driver.c
new file mode 100644
index 00000000..52c7189a
--- /dev/null
+++ b/src/md5driver.c
@@ -0,0 +1,83 @@
+/* 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 <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
+
+char* MDFile(char *);
+void MDPrint(unsigned char [16]);
+
+#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/src/package.c b/src/package.c
new file mode 100644
index 00000000..69461f67
--- /dev/null
+++ b/src/package.c
@@ -0,0 +1,228 @@
+/*
+ * pacman
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "util.h"
+#include "pacman.h"
+
+extern tartype_t gztype;
+
+pkginfo_t* load_pkg(char *pkgfile, unsigned short output)
+{
+ char *expath;
+ char *descfile;
+ int i;
+ TAR *tar;
+ pkginfo_t *info = NULL;
+ PMList *backup = NULL;
+ PMList *lp;
+
+ info = newpkg();
+ descfile = strdup("/tmp/pacman_XXXXXX");
+
+ if(tar_open(&tar, pkgfile, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
+ perror("could not open package");
+ return(NULL);
+ }
+ vprint("load_pkg: loading filelist from package...\n");
+ for(i = 0; !th_read(tar); i++) {
+ if(!strcmp(th_get_pathname(tar), ".PKGINFO")) {
+ /* extract this file into /tmp. it has info for us */
+ vprint("load_pkg: found package description file.\n");
+ mkstemp(descfile);
+ tar_extract_file(tar, descfile);
+ /* parse the info file */
+ parse_descfile(descfile, info, &backup, output);
+ if(!strlen(info->name)) {
+ fprintf(stderr, "error: missing package name in description file.\n");
+ return(NULL);
+ }
+ if(!strlen(info->version)) {
+ fprintf(stderr, "error: missing package version in description file.\n");
+ return(NULL);
+ }
+ for(lp = backup; lp; lp = lp->next) {
+ if(lp->data) {
+ info->backup = list_add(info->backup, lp->data);
+ }
+ }
+ continue;
+ }
+ if(!strcmp(th_get_pathname(tar), "._install")) {
+ info->scriptlet = 1;
+ } else {
+ expath = strdup(th_get_pathname(tar));
+ /* add the path to the list */
+ info->files = list_add(info->files, expath);
+ }
+
+ if(TH_ISREG(tar) && tar_skip_regfile(tar)) {
+ perror("bad package file");
+ return(NULL);
+ }
+ expath = NULL;
+ }
+ tar_close(tar);
+ FREE(descfile);
+
+ if(!strlen(info->name) || !strlen(info->version)) {
+ fprintf(stderr, "Error: Missing .PKGINFO file in %s\n", pkgfile);
+ return(NULL);
+ }
+
+ return(info);
+}
+
+/* Parses the package description file for the current package
+ *
+ * Returns: 0 on success, 1 on error
+ *
+ */
+int parse_descfile(char *descfile, pkginfo_t *info, PMList **backup, int output)
+{
+ FILE* fp = NULL;
+ char line[PATH_MAX+1];
+ char* ptr = NULL;
+ char* key = NULL;
+ int linenum = 0;
+ PMList* bak = NULL;
+
+ if((fp = fopen(descfile, "r")) == NULL) {
+ perror(descfile);
+ return(1);
+ }
+
+ while(!feof(fp)) {
+ fgets(line, PATH_MAX, fp);
+ if(output) {
+ printf("%s", line);
+ }
+ linenum++;
+ trim(line);
+ if(line[0] == '#') {
+ continue;
+ }
+ if(strlen(line) == 0) {
+ continue;
+ }
+ ptr = line;
+ key = strsep(&ptr, "=");
+ if(key == NULL || ptr == NULL) {
+ fprintf(stderr, "Syntax error in description file line %d\n", linenum);
+ } else {
+ trim(key);
+ key = strtoupper(key);
+ trim(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, "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, "SIZE")) {
+ char tmp[32];
+ strncpy(tmp, ptr, sizeof(tmp));
+ info->size = atol(tmp);
+ } else if(!strcmp(key, "DEPEND")) {
+ char *s = strdup(ptr);
+ info->depends = list_add(info->depends, s);
+ } else if(!strcmp(key, "CONFLICT")) {
+ char *s = strdup(ptr);
+ info->conflicts = list_add(info->conflicts, s);
+ } else if(!strcmp(key, "BACKUP")) {
+ char *s = strdup(ptr);
+ bak = list_add(bak, s);
+ } else {
+ fprintf(stderr, "Syntax error in description file line %d\n", linenum);
+ }
+ }
+ line[0] = '\0';
+ }
+ fclose(fp);
+ unlink(descfile);
+
+ *backup = bak;
+ return(0);
+}
+
+pkginfo_t* newpkg()
+{
+ pkginfo_t* pkg = NULL;
+ MALLOC(pkg, sizeof(pkginfo_t));
+
+ pkg->name[0] = '\0';
+ pkg->version[0] = '\0';
+ pkg->desc[0] = '\0';
+ pkg->builddate[0] = '\0';
+ pkg->installdate[0] = '\0';
+ pkg->packager[0] = '\0';
+ pkg->size = 0;
+ pkg->scriptlet = 0;
+ pkg->requiredby = NULL;
+ pkg->conflicts = NULL;
+ pkg->files = NULL;
+ pkg->backup = NULL;
+ pkg->depends = NULL;
+
+ return(pkg);
+}
+
+void freepkg(pkginfo_t *pkg)
+{
+ if(pkg == NULL) {
+ return;
+ }
+
+ list_free(pkg->files);
+ list_free(pkg->backup);
+ list_free(pkg->depends);
+ list_free(pkg->conflicts);
+ list_free(pkg->requiredby);
+ FREE(pkg);
+ return;
+}
+
+/* Helper function for sorting packages
+ */
+int pkgcmp(const void *p1, const void *p2)
+{
+ pkginfo_t **pkg1 = (pkginfo_t**)p1;
+ pkginfo_t **pkg2 = (pkginfo_t**)p2;
+
+ return(strcmp(pkg1[0]->name, pkg2[0]->name));
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/package.h b/src/package.h
new file mode 100644
index 00000000..f9dc9477
--- /dev/null
+++ b/src/package.h
@@ -0,0 +1,67 @@
+/*
+ * pacman
+ *
+ * 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 _PAC_PACKAGE_H
+#define _PAC_PACKAGE_H
+
+/* mods for depend_t.mod */
+#define DEP_ANY 0
+#define DEP_EQ 1
+#define DEP_GE 2
+#define DEP_LE 3
+
+/* Package Structures */
+typedef char** fileset_t;
+typedef struct __pkginfo_t {
+ char name[256];
+ char version[64];
+ char desc[512];
+ char builddate[32];
+ char installdate[32];
+ char packager[64];
+ unsigned long size;
+ unsigned short scriptlet;
+ PMList *files;
+ PMList *backup;
+ PMList *depends;
+ PMList *requiredby;
+ PMList *conflicts;
+} pkginfo_t;
+
+typedef struct __depend_t {
+ unsigned short mod;
+ char name[256];
+ char version[64];
+} depend_t;
+
+typedef struct __depmissing_t {
+ enum {DEPEND, REQUIRED, CONFLICT} type;
+ char target[256];
+ depend_t depend;
+} depmissing_t;
+
+pkginfo_t* load_pkg(char *pkgfile, unsigned short output);
+int parse_descfile(char *descfile, pkginfo_t *info, PMList **backup, int output);
+pkginfo_t* newpkg();
+void freepkg(pkginfo_t *pkg);
+int pkgcmp(const void *p1, const void *p2);
+
+#endif
+/* vim: set ts=2 sw=2 noet: */
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 <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 <stdlib.h>
+#include <signal.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+/* 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 <pkg>'\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: */
diff --git a/src/pacman.h b/src/pacman.h
new file mode 100644
index 00000000..330969ce
--- /dev/null
+++ b/src/pacman.h
@@ -0,0 +1,62 @@
+/*
+ * pacman
+ *
+ * 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 _PAC_PACMAN_H
+#define _PAC_PACMAN_H
+
+#ifndef PACVER
+#define PACVER "2.0"
+#endif
+
+#ifndef PKGDIR
+#define PKGDIR "var/lib/pacman"
+#endif
+
+#ifndef PACCONF
+#define PACCONF "etc/pacman.conf"
+#endif
+
+/* Operations */
+#define PM_MAIN 1
+#define PM_ADD 2
+#define PM_REMOVE 3
+#define PM_QUERY 4
+#define PM_UPGRADE 5
+#define PM_SYNC 6
+#define PM_DEPTEST 7
+
+int pacman_add(pacdb_t *db, PMList *targets);
+int pacman_remove(pacdb_t *db, PMList *targets);
+int pacman_upgrade(pacdb_t *db, PMList *targets);
+int pacman_query(pacdb_t *db, PMList *targets);
+int pacman_sync(pacdb_t *db, PMList *targets);
+int pacman_deptest(pacdb_t *db, PMList *targets);
+
+PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets);
+int resolvedeps(pacdb_t *local, pacdb_t *sync, pkginfo_t *package, PMList *list, PMList *trail);
+int splitdep(char *depstr, depend_t *depend);
+
+int lckmk(char *file, int retries, unsigned int sleep_secs);
+int lckrm(char *lckfile);
+void cleanup(int signum);
+
+#endif /* PACMAN_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacsync.c b/src/pacsync.c
new file mode 100644
index 00000000..224528f8
--- /dev/null
+++ b/src/pacsync.c
@@ -0,0 +1,183 @@
+/*
+ * pacman
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ftplib.h>
+/* pacman */
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "util.h"
+#include "pacsync.h"
+#include "pacman.h"
+
+static int log_progress(netbuf *ctl, int xfered, void *arg);
+static char sync_fnm[25];
+
+/* pacman options */
+extern char *pmo_root;
+/* configuration options */
+extern char pmc_syncserver[512];
+extern char pmc_syncpath[512];
+extern char pmc_syncname[512];
+
+int sync_synctree()
+{
+ char ldir[PATH_MAX] = "";
+ char path[PATH_MAX];
+ mode_t oldmask;
+ char *str;
+ PMList *files = NULL;
+
+ snprintf(ldir, PATH_MAX, "%s%s/%s", pmo_root, PKGDIR, pmc_syncname);
+
+ /* remove the old dir */
+ vprint("Removing %s (if it exists)\n", ldir);
+ rmrf(ldir);
+
+ /* make the new dir */
+ oldmask = umask(0000);
+ mkdir(ldir, 0755);
+ umask(oldmask);
+
+ /* build out list of one */
+ snprintf(path, PATH_MAX, "%s.db.tar.gz", pmc_syncname);
+ str = strdup(path);
+ files = list_add(files, str);
+ if(downloadfiles(pmc_syncserver, pmc_syncpath, ldir, files)) {
+ list_free(files);
+ return(1);
+ }
+
+ /* uncompress the sync database */
+ snprintf(path, PATH_MAX, "%s/%s", ldir, (char*)files->data);
+ list_free(files);
+ vprint("Unpacking %s...\n", path);
+ if(unpack(path, ldir)) {
+ return(1);
+ }
+
+ /* remove the .tar.gz */
+ unlink(path);
+
+ return(0);
+}
+
+int downloadfiles(char *server, char *remotepath, char *localpath, PMList *files)
+{
+ int fsz;
+ netbuf *control = NULL;
+ PMList *lp;
+ int ret = 0;
+
+ if(files == NULL) {
+ return(0);
+ }
+
+ FtpInit();
+ if(!FtpConnect(server, &control)) {
+ fprintf(stderr, "error: cannot connect to %s\n", server);
+ return(1);
+ }
+ if(!FtpLogin("anonymous", "arch@guest", control)) {
+ fprintf(stderr, "error: anonymous login failed\n");
+ FtpQuit(control);
+ return(1);
+ }
+
+
+ if(!FtpChdir(remotepath, control)) {
+ fprintf(stderr, "error: could not cwd to %s: %s\n", remotepath,
+ FtpLastResponse(control));
+ return(1);
+ }
+
+ /* get each file in the list */
+ for(lp = files; lp; lp = lp->next) {
+ char output[PATH_MAX];
+ int j;
+
+ snprintf(output, PATH_MAX, "%s/%s", localpath, (char*)lp->data);
+
+ /* passive mode */
+ /* TODO: make passive ftp an option */
+ if(!FtpOptions(FTPLIB_CONNMODE, FTPLIB_PASSIVE, control)) {
+ fprintf(stderr, "warning: failed to set passive mode\n");
+ }
+
+ if(!FtpSize((char*)lp->data, &fsz, FTPLIB_IMAGE, control)) {
+ fprintf(stderr, "warning: failed to get filesize for %s\n", (char*)lp->data);
+ }
+
+ /* set up our progress bar's callback */
+ FtpOptions(FTPLIB_CALLBACK, (long)log_progress, control);
+ FtpOptions(FTPLIB_IDLETIME, (long)1000, control);
+ FtpOptions(FTPLIB_CALLBACKARG, (long)&fsz, control);
+ FtpOptions(FTPLIB_CALLBACKBYTES, (10*1024), control);
+
+ strncpy(sync_fnm, lp->data, 24);
+ for(j = strlen(sync_fnm); j < 24; j++) {
+ sync_fnm[j] = ' ';
+ }
+ sync_fnm[24] = '\0';
+ if(!FtpGet(output, lp->data, FTPLIB_IMAGE, control)) {
+ fprintf(stderr, "\nerror: could not download %s: %s\n", (char*)lp->data,
+ FtpLastResponse(control));
+ /* unlink the file */
+ unlink(output);
+ ret = 1;
+ } else {
+ log_progress(control, fsz, &fsz);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+
+ FtpQuit(control);
+ return(ret);
+}
+
+static int log_progress(netbuf *ctl, int xfered, void *arg)
+{
+ int fsz = *(int*)arg;
+ int pct = (unsigned int)(xfered * 100) / fsz;
+ int i;
+
+ printf("%s [", sync_fnm);
+ for(i = 0; i < (int)(pct/3); i++) {
+ printf("#");
+ }
+ for(i = (int)(pct/3); i < (int)(100/3); i++) {
+ printf(" ");
+ }
+ printf("] %3d%% | %6dK\r", pct, (xfered/1024));
+ fflush(stdout);
+ return(1);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacsync.h b/src/pacsync.h
new file mode 100644
index 00000000..d7c666f3
--- /dev/null
+++ b/src/pacsync.h
@@ -0,0 +1,29 @@
+/*
+ * pacman
+ *
+ * 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 _PAC_PACSYNC_H
+#define _PAC_PACSYNC_H
+
+int sync_synctree();
+int downloadfiles(char *server, char *remotepath, char *localpath, PMList *files);
+
+#endif
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/rpmvercmp.c b/src/rpmvercmp.c
new file mode 100644
index 00000000..de4aefe2
--- /dev/null
+++ b/src/rpmvercmp.c
@@ -0,0 +1,120 @@
+/*
+ * pacman
+ *
+ * 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 <ctype.h>
+#include <string.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);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 00000000..1fe2e037
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,571 @@
+/*
+ * pacman
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <ctype.h>
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "util.h"
+#include "pacman.h"
+
+extern char* pmo_root;
+extern unsigned short pmo_op;
+extern unsigned short pmo_version;
+extern unsigned short pmo_verbose;
+extern unsigned short pmo_help;
+extern unsigned short pmo_force;
+extern unsigned short pmo_nodeps;
+extern unsigned short pmo_upgrade;
+extern unsigned short pmo_nosave;
+extern unsigned short pmo_vertest;
+extern unsigned short pmo_q_isfile;
+extern unsigned short pmo_q_info;
+extern unsigned short pmo_q_list;
+extern unsigned short pmo_q_owns;
+extern unsigned short pmo_s_sync;
+extern unsigned short pmo_s_search;
+extern unsigned short pmo_s_clean;
+extern unsigned short pmo_s_upgrade;
+
+extern char pmc_syncserver[512];
+extern char pmc_syncname[512];
+extern char pmc_syncpath[512];
+
+extern PMList *pm_targets;
+
+/* borrowed and modifed from Per Liden's pkgutils (http://crux.nu) */
+static int 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 (int)gzf;
+}
+
+tartype_t gztype = {
+ (openfunc_t) gzopen_frontend,
+ (closefunc_t)gzclose,
+ (readfunc_t) gzread,
+ (writefunc_t)gzwrite
+};
+
+int unpack(char *archive, char *prefix)
+{
+ TAR *tar = NULL;
+ char expath[PATH_MAX];
+
+ /* 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)) {
+ 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));
+ }
+ }
+ tar_close(tar);
+
+ return(0);
+}
+
+/* Parse command-line arguments for each operation
+ * op: the operation code requested
+ * argc: argc
+ * argv: argv
+ *
+ * Returns: 0 on success, 1 on error
+ */
+int parseargs(int op, int argc, char **argv)
+{
+ int opt;
+ int option_index = 0;
+ static struct option opts[] =
+ {
+ {"add", no_argument, 0, 'A'},
+ {"remove", no_argument, 0, 'R'},
+ {"upgrade", no_argument, 0, 'U'},
+ {"query", no_argument, 0, 'Q'},
+ {"sync", no_argument, 0, 'S'},
+ {"deptest", no_argument, 0, 'T'},
+ {"vertest", no_argument, 0, 'Y'},
+ {"root", required_argument, 0, 'r'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {"search", no_argument, 0, 's'},
+ {"clean", no_argument, 0, 'c'},
+ {"force", no_argument, 0, 'f'},
+ {"nodeps", no_argument, 0, 'd'},
+ {"nosave", no_argument, 0, 'n'},
+ {"owns", no_argument, 0, 'o'},
+ {"list", no_argument, 0, 'l'},
+ {"file", no_argument, 0, 'p'},
+ {"info", no_argument, 0, 'i'},
+ {"sysupgrade", no_argument, 0, 'u'},
+ {"refresh", no_argument, 0, 'y'},
+ {0, 0, 0, 0}
+ };
+
+ while((opt = getopt_long(argc, argv, "ARUQSTYr:vhscVfnoldpiuy", opts, &option_index))) {
+ if(opt < 0) {
+ break;
+ }
+ switch(opt) {
+ case 0: break;
+ case 'A': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_ADD); break;
+ case 'R': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_REMOVE); break;
+ case 'U': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_UPGRADE); break;
+ case 'Q': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_QUERY); break;
+ case 'S': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_SYNC); break;
+ case 'T': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_DEPTEST); break;
+ case 'Y': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_DEPTEST); pmo_vertest = 1; break;
+ case 'h': pmo_help = 1; break;
+ case 'V': pmo_version = 1; break;
+ case 'v': pmo_verbose = 1; break;
+ case 'f': pmo_force = 1; break;
+ case 'd': pmo_nodeps = 1; break;
+ case 'n': pmo_nosave = 1; break;
+ case 'l': pmo_q_list = 1; break;
+ case 'p': pmo_q_isfile = 1; break;
+ case 'i': pmo_q_info = 1; break;
+ case 'o': pmo_q_owns = 1; break;
+ case 'u': pmo_s_upgrade = 1; break;
+ case 'y': pmo_s_sync = 1; break;
+ case 's': pmo_s_search = 1; break;
+ case 'c': pmo_s_clean = 1; break;
+ case 'r': if(realpath(optarg, pmo_root) == NULL) {
+ perror("bad root path");
+ return(1);
+ } break;
+ case '?': return(1);
+ default: return(1);
+ }
+ }
+
+ if(pmo_op == 0) {
+ fprintf(stderr, "error: only one operation may be used at a time\n\n");
+ return(1);
+ }
+
+ if(pmo_help) {
+ usage(pmo_op, (char*)basename(argv[0]));
+ return(2);
+ }
+ if(pmo_version) {
+ version();
+ return(2);
+ }
+
+ while(optind < argc) {
+ /* add the target to our target array */
+ char *s = strdup(argv[optind]);
+ pm_targets = list_add(pm_targets, s);
+ optind++;
+ }
+
+ return(0);
+}
+
+int parseconfig(char *configfile)
+{
+ FILE *fp = NULL;
+ char line[PATH_MAX+1];
+ char *ptr = NULL;
+ char *key = NULL;
+ int linenum = 0;
+
+ if((fp = fopen(configfile, "r")) == NULL) {
+ perror(configfile);
+ return(1);
+ }
+
+ while(fgets(line, PATH_MAX, fp)) {
+ linenum++;
+ trim(line);
+ if(strlen(line) == 0) {
+ continue;
+ }
+ if(line[0] == '#') {
+ continue;
+ }
+ ptr = line;
+ key = strsep(&ptr, "=");
+ if(key == NULL || ptr == NULL) {
+ fprintf(stderr, "syntax error in config file (line %d)\n", linenum);
+ } else {
+ trim(key);
+ key = strtoupper(key);
+ trim(ptr);
+ if(!strcmp(key, "SYNC_SERVER")) {
+ strncpy(pmc_syncserver, ptr, sizeof(pmc_syncserver)-1);
+ } else if(!strcmp(key, "SYNC_TREE_PATH")) {
+ strncpy(pmc_syncpath, ptr, sizeof(pmc_syncpath)-1);
+ } else if(!strcmp(key, "SYNC_TREE_NAME")) {
+ strncpy(pmc_syncname, ptr, sizeof(pmc_syncname)-1);
+ } else {
+ fprintf(stderr, "Syntax error in description file line %d\n", linenum);
+ }
+ }
+ line[0] = '\0';
+ }
+ fclose(fp);
+
+ return(0);
+}
+
+int copyfile(char *src, char *dest)
+{
+ FILE *in, *out;
+ size_t len;
+ char buf[1025];
+
+ in = fopen(src, "r");
+ if(in == NULL) {
+ return(1);
+ }
+ out = fopen(dest, "w");
+ if(out == NULL) {
+ return(1);
+ }
+
+ while((len = fread(buf, 1, 1024, in))) {
+ fwrite(buf, 1, len, out);
+ }
+
+ fclose(in);
+ fclose(out);
+ return(0);
+}
+
+int 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);
+ return(1);
+ }
+ }
+ }
+ }
+ free(orig);
+ umask(oldmask);
+ return(0);
+}
+
+int 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 += rmrf(name);
+ }
+ }
+ }
+ closedir(dirp);
+ if(rmdir(path)) {
+ errflag++;
+ }
+ return(errflag);
+ }
+ return(0);
+}
+
+/* Display usage/syntax for the specified operation.
+ * op: the operation code requested
+ * myname: basename(argv[0])
+ */
+void usage(int op, char *myname)
+{
+ if(op == PM_MAIN) {
+ printf("usage: %s {-h --help}\n", myname);
+ printf(" %s {-V --version}\n", myname);
+ printf(" %s {-A --add} [options] <file>\n", myname);
+ printf(" %s {-R --remove} [options] <package>\n", myname);
+ printf(" %s {-U --upgrade} [options] <file>\n", myname);
+ printf(" %s {-Q --query} [options] [package]\n", myname);
+ printf(" %s {-S --sync} [options] [package]\n", myname);
+ printf("\nuse '%s --help' with other options for more syntax\n\n", myname);
+ } else {
+ if(op == PM_ADD) {
+ printf("usage: %s {-A --add} [options] <file>\n", myname);
+ printf("options:\n");
+ printf(" -f, --force force install, overwrite conflicting files\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ } else if(op == PM_REMOVE) {
+ printf("usage: %s {-R --remove} [options] <package>\n", myname);
+ printf("options:\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ printf(" -n, --nosave remove configuration files as well\n");
+ } else if(op == PM_UPGRADE) {
+ printf("usage: %s {-U --upgrade} [options] <file>\n", myname);
+ printf("options:\n");
+ printf(" -f, --force force install, overwrite conflicting files\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ } else if(op == PM_QUERY) {
+ printf("usage: %s {-Q --query} [options] [package]\n", myname);
+ printf("options:\n");
+ printf(" -o, --owns <file> query the package that owns <file>\n");
+ printf(" -l, --list list the contents of the queried package\n");
+ printf(" -i, --info view package information\n");
+ printf(" -p, --file pacman will query the package file [package] instead of\n");
+ printf(" looking in the database\n");
+ } else if(op == PM_SYNC) {
+ printf("usage: %s {-S --sync} [options] [package]\n", myname);
+ printf("options:\n");
+ printf(" -s, --search search sync database for matching strings\n");
+ printf(" -f, --force force install, overwrite conflicting files\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ printf(" -y, --refresh download a fresh package sync database from the server\n");
+ printf(" -u, --sysupgrade upgrade all packages that are out of date\n");
+ printf(" -c, --clean remove packages from cache directory to free up diskspace\n");
+ }
+ printf(" -v, --verbose be verbose\n");
+ printf(" -r, --root <path> set an alternate installation root\n");
+ }
+}
+
+/* Version
+ */
+void version(void)
+{
+ printf("\n");
+ printf(" .--. Pacman v%s\n", PACVER);
+ printf("/ _.-' .-. .-. .-. Copyright (C) 2002 Judd Vinet <jvinet@zeroflux.org>\n");
+ printf("\\ '-. '-' '-' '-' \n");
+ printf(" '--' This program may be freely redistributed under\n");
+ printf(" the terms of the GNU GPL\n\n");
+}
+
+/* Check verbosity option and, if set, print the
+ * string to stdout
+ */
+int vprint(char *fmt, ...)
+{
+ va_list args;
+ if(pmo_verbose) {
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ fflush(stdout);
+ }
+ return(0);
+}
+
+int yesno(char *fmt, ...)
+{
+ char response[32];
+ va_list args;
+
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ fflush(stdout);
+ if(fgets(response, 32, stdin)) {
+ trim(response);
+ if(!strcasecmp(response, "Y") || !strcasecmp(response, "YES") || !strlen(response)) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/* Test for existence of a string in a PMList
+ */
+int is_in(char *needle, PMList *haystack)
+{
+ PMList *lp;
+
+ for(lp = haystack; lp; lp = lp->next) {
+ if(lp->data && !strcmp(lp->data, needle)) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+
+/* 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* needbackup(char* file, PMList *backup)
+{
+ PMList *lp;
+
+ /* 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 = index(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)) {
+ char *md5 = strdup(ptr);
+ FREE(str);
+ return(md5);
+ }
+ FREE(str);
+ }
+ return(NULL);
+}
+
+/* Test for existence of a package in a PMList*
+ * of pkginfo_t*
+ *
+ * returns: 0 for no match
+ * 1 for identical match
+ * -1 for name-only match (version mismatch)
+ */
+int is_pkgin(pkginfo_t *needle, PMList *haystack)
+{
+ PMList *lp;
+ pkginfo_t *info;
+
+ for(lp = haystack; lp; lp = lp->next) {
+ info = (pkginfo_t*)lp->data;
+ if(info && !strcmp(info->name, needle->name)) {
+ if(!strcmp(info->version, needle->version)) {
+ return(1);
+ }
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/* Convert a string to uppercase
+ */
+char* strtoupper(char *str)
+{
+ char *ptr = str;
+
+ while(*ptr) {
+ (*ptr) = toupper(*ptr);
+ ptr++;
+ }
+ return str;
+}
+
+/* Trim whitespace and newlines from a string
+ */
+char* trim(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;
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 00000000..132d7c06
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,52 @@
+/*
+ * pacman
+ *
+ * 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 _PAC_UTIL_H
+#define _PAC_UTIL_H
+
+#include <libtar.h>
+#include <zlib.h>
+
+#define MALLOC(p, b) { if((b) > 0) { \
+ p = malloc(b); if (!(p)) { \
+ fprintf(stderr, "malloc failure: could not allocate %d byets\n", b); \
+ exit(1); }} else p = NULL; }
+
+#define FREE(p) { if (p) { free(p); (p)= NULL; }}
+
+int unpack(char *archive, char *prefix);
+int parseargs(int op, int argc, char **argv);
+int parseconfig(char *configfile);
+int copyfile(char *src, char *dest);
+int makepath(char *path);
+int rmrf(char *path);
+int vprint(char *fmt, ...);
+int yesno(char* fmt, ...);
+void usage(int op, char *myname);
+void version(void);
+char* needbackup(char *file, PMList *backup);
+int is_in(char *needle, PMList *haystack);
+int is_pkgin(pkginfo_t *needle, PMList *haystack);
+char* trim(char *str);
+char* strtoupper(char *str);
+
+
+#endif
+/* vim: set ts=2 sw=2 noet: */