summaryrefslogtreecommitdiffstats
path: root/src/pacman
diff options
context:
space:
mode:
Diffstat (limited to 'src/pacman')
-rw-r--r--src/pacman/Makefile39
-rw-r--r--src/pacman/add.c130
-rw-r--r--src/pacman/add.h28
-rw-r--r--src/pacman/conf.c308
-rw-r--r--src/pacman/conf.h28
-rw-r--r--src/pacman/db.c98
-rw-r--r--src/pacman/db.h28
-rw-r--r--src/pacman/download.c467
-rw-r--r--src/pacman/download.h38
-rw-r--r--src/pacman/list.c176
-rw-r--r--src/pacman/list.h45
-rw-r--r--src/pacman/package.c203
-rw-r--r--src/pacman/package.h35
-rw-r--r--src/pacman/pacman.c688
-rw-r--r--src/pacman/pacman.h75
-rw-r--r--src/pacman/query.c247
-rw-r--r--src/pacman/query.h28
-rw-r--r--src/pacman/remove.c137
-rw-r--r--src/pacman/remove.h28
-rw-r--r--src/pacman/sync.c806
-rw-r--r--src/pacman/sync.h35
-rw-r--r--src/pacman/upgrade.c42
-rw-r--r--src/pacman/upgrade.h28
-rw-r--r--src/pacman/util.c297
-rw-r--r--src/pacman/util.h42
25 files changed, 4076 insertions, 0 deletions
diff --git a/src/pacman/Makefile b/src/pacman/Makefile
new file mode 100644
index 00000000..3d2874ac
--- /dev/null
+++ b/src/pacman/Makefile
@@ -0,0 +1,39 @@
+
+CC=gcc
+CFLAGS=-g -Wall -D_GNU_SOURCE -I. -I../.. -I../../lib/libalpm -I../../lib/libftp
+LDFLAGS=-L../../lib/libalpm -lalpm -L../../lib/libftp -lftp -ltar -lz
+AR=ar rc
+RAN=ranlib
+
+OBJECTS=util.o \
+ list.o \
+ package.o \
+ db.o \
+ download.o \
+ add.o \
+ remove.o \
+ upgrade.o \
+ query.o \
+ sync.o \
+ conf.o \
+ pacman.o
+
+all: pacman
+
+%.o: %.c %.h
+ $(CC) -c $(CFLAGS) -o $@ $<
+
+pacman: $(OBJECTS) ../../lib/libalpm/libalpm.a
+ $(CC) $(OBJECTS) -o $@ $(CFLAGS) $(LDFLAGS)
+# $(CC) $(OBJECTS) -o $@.static $(CFLAGS) $(LDFLAGS)
+
+clean:
+ rm -f *.o *~ core
+ rm -f pacman pacman.static convertdb vercmp
+
+install: pacman vercmp convertdb
+ $(INSTALL) -D -m0755 pacman $(DESTDIR)$(BINDIR)/pacman
+ $(INSTALL) -D -m0755 pacman.static $(DESTDIR)$(BINDIR)/pacman.static
+ $(INSTALL) -D -m0755 vercmp $(DESTDIR)$(BINDIR)/vercmp
+ $(INSTALL) -D -m0755 convertdb $(DESTDIR)$(BINDIR)/convertdb
+
diff --git a/src/pacman/add.c b/src/pacman/add.c
new file mode 100644
index 00000000..1bf46f36
--- /dev/null
+++ b/src/pacman/add.c
@@ -0,0 +1,130 @@
+/*
+ * add.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <alpm.h>
+/* pacman */
+#include "list.h"
+#include "download.h"
+#include "pacman.h"
+
+extern unsigned char pmo_upgrade;
+extern unsigned char pmo_flags;
+
+int pacman_add(list_t *targets)
+{
+ PM_LIST *data;
+ list_t *i;
+
+ if(targets == NULL) {
+ return(0);
+ }
+
+ /* Check for URL targets and process them
+ */
+ for(i = targets; i; i = i->next) {
+ if(strstr(i->data, "://")) {
+ char *str = fetch_pkgurl(i->data);
+ if(str == NULL) {
+ return(1);
+ } else {
+ free(i->data);
+ i->data = str;
+ }
+ }
+ }
+
+ /* Step 1: create a new transaction
+ */
+ if(alpm_trans_init((pmo_upgrade == 0) ? PM_TRANS_TYPE_ADD : PM_TRANS_TYPE_UPGRADE,
+ pmo_flags, cb_trans) == -1) {
+ ERR(NL, "%s\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+
+ /* and add targets to it */
+ MSG(NL, "loading package data... ");
+ for(i = targets; i; i = i->next) {
+ if(alpm_trans_addtarget(i->data) == -1) {
+ ERR(NL, "failed to add target '%s' (%s)\n", (char *)i->data, alpm_strerror(pm_errno));
+ return(1);
+ }
+ }
+ MSG(CL, "done");
+
+ /* Step 2: "compute" the transaction based on targets and flags
+ */
+ if(alpm_trans_prepare(&data) == -1) {
+ PM_LIST *i;
+
+ ERR(NL, "failed to prepare transaction (%s)\n", alpm_strerror(pm_errno));
+ switch(pm_errno) {
+ case PM_ERR_UNSATISFIED_DEPS:
+ for(i = alpm_list_first(data); i; i = alpm_list_next(i)) {
+ pmdepmissing_t *miss = alpm_list_getdata(i);
+
+ MSG(NL, ":: %s: requires %s", miss->target, miss->depend.name);
+ switch(miss->depend.mod) {
+ case PM_DEP_EQ: MSG(CL, "=%s", miss->depend.version); break;
+ case PM_DEP_GE: MSG(CL, ">=%s", miss->depend.version); break;
+ case PM_DEP_LE: MSG(CL, "<=%s", miss->depend.version); break;
+ }
+ MSG(CL, "\n");
+ }
+ alpm_list_free(data);
+ break;
+ case PM_ERR_CONFLICTING_DEPS:
+ for(i = alpm_list_first(data); i; i = alpm_list_next(i)) {
+ pmdepmissing_t *miss = alpm_list_getdata(i);
+
+ MSG(NL, ":: %s: conflicts with %s",
+ miss->target, miss->depend.name, miss->depend.name);
+ }
+ alpm_list_free(data);
+ break;
+ case PM_ERR_FILE_CONFLICTS:
+ for(i = alpm_list_first(data); i; i = alpm_list_next(i)) {
+ MSG(NL, ":: %s\n", (char *)alpm_list_getdata(i));
+ }
+ alpm_list_free(data);
+ MSG(NL, "\nerrors occurred, no packages were upgraded.\n\n");
+ break;
+ default:
+ break;
+ }
+ alpm_trans_release();
+ return(1);
+ }
+
+ /* Step 3: actually perform the installation
+ */
+ if(alpm_trans_commit() == -1) {
+ ERR(NL, "failed to commit transaction (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+
+ return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/add.h b/src/pacman/add.h
new file mode 100644
index 00000000..5efe6a8a
--- /dev/null
+++ b/src/pacman/add.h
@@ -0,0 +1,28 @@
+/*
+ * add.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_ADD_H
+#define _PM_ADD_H
+
+int pacman_add(list_t *targets);
+
+#endif /* _PM_ADD_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/conf.c b/src/pacman/conf.c
new file mode 100644
index 00000000..89389422
--- /dev/null
+++ b/src/pacman/conf.c
@@ -0,0 +1,308 @@
+/*
+ * conf.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include <alpm.h>
+/* pacman */
+#include "util.h"
+#include "list.h"
+#include "sync.h"
+#include "download.h"
+#include "pacman.h"
+
+#define min(X, Y) ((X) < (Y) ? (X) : (Y))
+
+extern list_t *pmo_holdpkg;
+extern char *pmo_proxyhost;
+extern unsigned short pmo_proxyport;
+extern unsigned short pmo_nopassiveftp;
+
+extern list_t *pmc_syncs;
+
+int parseconfig(char *file)
+{
+ FILE *fp = NULL;
+ char line[PATH_MAX+1];
+ char *ptr = NULL;
+ char *key = NULL;
+ int linenum = 0;
+ char section[256] = "";
+ sync_t *sync = NULL;
+
+ fp = fopen(file, "r");
+ if(fp == NULL) {
+ perror(file);
+ return(1);
+ }
+
+ while(fgets(line, PATH_MAX, fp)) {
+ linenum++;
+ strtrim(line);
+ if(strlen(line) == 0 || line[0] == '#') {
+ continue;
+ }
+ if(line[0] == '[' && line[strlen(line)-1] == ']') {
+ /* new config section */
+ ptr = line;
+ ptr++;
+ strncpy(section, ptr, min(255, strlen(ptr)-1));
+ section[min(255, strlen(ptr)-1)] = '\0';
+ vprint("config: new section '%s'\n", section);
+ if(!strlen(section)) {
+ ERR(NL, "config: line %d: bad section name\n", linenum);
+ return(1);
+ }
+ if(!strcmp(section, "local")) {
+ ERR(NL, "config: line %d: '%s' is reserved and cannot be used as a package tree\n",
+ linenum, section);
+ return(1);
+ }
+ if(strcmp(section, "options")) {
+ list_t *i;
+ int found = 0;
+ for(i = pmc_syncs; i && !found; i = i->next) {
+ sync = (sync_t *)i->data;
+ if(!strcmp(sync->treename, section)) {
+ found = 1;
+ }
+ }
+ if(!found) {
+ /* start a new sync record */
+ sync = (sync_t *)malloc(sizeof(sync_t));
+ if(sync == NULL) {
+ ERR(NL, "could not allocate %d bytes\n", sizeof(sync_t));
+ return(1);
+ }
+ sync->treename = strdup(section);
+ sync->servers = NULL;
+ pmc_syncs = list_add(pmc_syncs, sync);
+ }
+ }
+ } else {
+ /* directive */
+ ptr = line;
+ key = strsep(&ptr, "=");
+ if(key == NULL) {
+ ERR(NL, "config: line %d: syntax error\n", linenum);
+ return(1);
+ }
+ strtrim(key);
+ key = strtoupper(key);
+ if(!strlen(section) && strcmp(key, "INCLUDE")) {
+ ERR(NL, "config: line %d: all directives must belong to a section\n", linenum);
+ return(1);
+ }
+ if(ptr == NULL) {
+ if(!strcmp(key, "NOPASSIVEFTP")) {
+ pmo_nopassiveftp = 1;
+ vprint("config: nopassiveftp\n");
+ } else if(!strcmp(key, "USESYSLOG")) {
+ if(alpm_set_option(PM_OPT_USESYSLOG, (long)1) == -1) {
+ ERR(NL, "failed to set option USESYSLOG (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: usesyslog\n");
+ } else {
+ ERR(NL, "config: line %d: syntax error\n", linenum);
+ return(1);
+ }
+ } else {
+ strtrim(ptr);
+ if(!strcmp(key, "INCLUDE")) {
+ char conf[PATH_MAX];
+ strncpy(conf, ptr, PATH_MAX);
+ vprint("config: including %s\n", conf);
+ parseconfig(conf);
+ } else if(!strcmp(section, "options")) {
+ if(!strcmp(key, "NOUPGRADE")) {
+ char *p = ptr;
+ char *q;
+ while((q = strchr(p, ' '))) {
+ *q = '\0';
+ if(alpm_set_option(PM_OPT_NOUPGRADE, (long)p) == -1) {
+ ERR(NL, "failed to set option NOUPGRADE (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: noupgrade: %s\n", p);
+ p = q;
+ p++;
+ }
+ if(alpm_set_option(PM_OPT_NOUPGRADE, (long)p) == -1) {
+ ERR(NL, "failed to set option NOUPGRADE (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: noupgrade: %s\n", p);
+ } else if(!strcmp(key, "IGNOREPKG")) {
+ char *p = ptr;
+ char *q;
+ while((q = strchr(p, ' '))) {
+ *q = '\0';
+ if(alpm_set_option(PM_OPT_IGNOREPKG, (long)p) == -1) {
+ ERR(NL, "failed to set option IGNOREPKG (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: ignorepkg: %s\n", p);
+ p = q;
+ p++;
+ }
+ if(alpm_set_option(PM_OPT_IGNOREPKG, (long)p) == -1) {
+ ERR(NL, "failed to set option IGNOREPKG (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: ignorepkg: %s\n", p);
+ } else if(!strcmp(key, "HOLDPKG")) {
+ char *p = ptr;
+ char *q;
+ while((q = strchr(p, ' '))) {
+ *q = '\0';
+ pmo_holdpkg = list_add(pmo_holdpkg, strdup(p));
+ vprint("config: holdpkg: %s\n", p);
+ p = q;
+ p++;
+ }
+ pmo_holdpkg = list_add(pmo_holdpkg, strdup(p));
+ vprint("config: holdpkg: %s\n", p);
+ } else if(!strcmp(key, "DBPATH")) {
+ /* shave off the leading slash, if there is one */
+ if(*ptr == '/') {
+ ptr++;
+ }
+ if(alpm_set_option(PM_OPT_DBPATH, (long)ptr) == -1) {
+ ERR(NL, "failed to set option DBPATH (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: dbpath: %s\n", ptr);
+ } else if (!strcmp(key, "LOGFILE")) {
+ if(alpm_set_option(PM_OPT_LOGFILE, (long)ptr) == -1) {
+ ERR(NL, "failed to set option LOGFILE (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ vprint("config: log file: %s\n", ptr);
+ } else if (!strcmp(key, "PROXYSERVER")) {
+ char *p;
+ if(pmo_proxyhost) {
+ FREE(pmo_proxyhost);
+ }
+ p = strstr(ptr, "://");
+ if(p) {
+ p += 3;
+ if(p == NULL || *p == '\0') {
+ ERR(NL, "config: line %d: bad server location\n", linenum);
+ return(1);
+ }
+ ptr = p;
+ }
+ pmo_proxyhost = strndup(ptr, PATH_MAX);
+ vprint("config: proxyserver: %s\n", pmo_proxyhost);
+ } else if (!strcmp(key, "PROXYPORT")) {
+ pmo_proxyport = (unsigned short)atoi(ptr);
+ vprint("config: proxyport: %u\n", pmo_proxyport);
+ } else if (!strcmp(key, "INCLUDE")) {
+ /* ORE
+ TBD */
+ } else {
+ ERR(NL, "config: line %d: syntax error\n", linenum);
+ return(1);
+ }
+ } else {
+ if(!strcmp(key, "SERVER")) {
+ /* parse our special url */
+ server_t *server;
+ char *p;
+
+ if((server = (server_t *)malloc(sizeof(server_t))) == NULL) {
+ ERR(NL, "could not allocate %d bytes\n", sizeof(server_t));
+ return(1);
+ }
+ server->server = server->path = NULL;
+ server->protocol = NULL;
+
+ p = strstr(ptr, "://");
+ if(p == NULL) {
+ ERR(NL, "config: line %d: bad server location\n", linenum);
+ return(1);
+ }
+ *p = '\0';
+ p++; p++; p++;
+ if(p == NULL || *p == '\0') {
+ ERR(NL, "config: line %d: bad server location\n", linenum);
+ return(1);
+ }
+ server->protocol = strdup(ptr);
+ if(!strcmp(server->protocol, "ftp") || !strcmp(server->protocol, "http")) {
+ char *slash;
+ /* split the url into domain and path */
+ slash = strchr(p, '/');
+ if(slash == NULL) {
+ /* no path included, default to / */
+ server->path = strdup("/");
+ } else {
+ /* add a trailing slash if we need to */
+ if(slash[strlen(slash)-1] == '/') {
+ server->path = strdup(slash);
+ } else {
+ if((server->path = (char *)malloc(strlen(slash)+2)) == NULL) {
+ ERR(NL, "could not allocate %d bytes\n", sizeof(strlen(slash+2)));
+ return(1);
+ }
+ sprintf(server->path, "%s/", slash);
+ }
+ *slash = '\0';
+ }
+ server->server = strdup(p);
+ } else if(!strcmp(server->protocol, "file")){
+ /* add a trailing slash if we need to */
+ if(p[strlen(p)-1] == '/') {
+ server->path = strdup(p);
+ } else {
+ server->path = (char *)malloc(strlen(p)+2);
+ if(server->path == NULL) {
+ ERR(NL, "could not allocate %d bytes\n", sizeof(strlen(p+2)));
+ return(1);
+ }
+ sprintf(server->path, "%s/", p);
+ }
+ } else {
+ ERR(NL, "config: line %d: protocol %s is not supported\n", linenum, ptr);
+ return(1);
+ }
+ /* add to the list */
+ vprint("config: %s: server: %s %s %s\n", section, server->protocol, server->server, server->path);
+ sync->servers = list_add(sync->servers, server);
+ } else {
+ ERR(NL, "config: line %d: syntax error\n", linenum);
+ return(1);
+ }
+ }
+ line[0] = '\0';
+ }
+ }
+ }
+ fclose(fp);
+
+ return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
new file mode 100644
index 00000000..809e702e
--- /dev/null
+++ b/src/pacman/conf.h
@@ -0,0 +1,28 @@
+/*
+ * conf.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_CONF_H
+#define _PM_CONF_H
+
+int parseconfig(char *file);
+
+#endif /* _PM_CONF_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/db.c b/src/pacman/db.c
new file mode 100644
index 00000000..e6e17775
--- /dev/null
+++ b/src/pacman/db.c
@@ -0,0 +1,98 @@
+/*
+ * db.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <alpm.h>
+/* pacman */
+#include "util.h"
+#include "list.h"
+#include "sync.h"
+#include "db.h"
+
+int db_search(PM_DB *db, char *treename, char *needle)
+{
+ PM_LIST *lp;
+ char *targ;
+
+ targ = strdup(needle);
+ strtoupper(targ);
+
+ for(lp = alpm_db_getpkgcache(db); lp; lp = alpm_list_next(lp)) {
+ PM_PKG *pkg = alpm_list_getdata(lp);
+ char *haystack;
+ char *pkgname, *pkgdesc;
+ int match = 0;
+
+ pkgname = alpm_pkg_getinfo(pkg, PM_PKG_NAME);
+ pkgdesc = alpm_pkg_getinfo(pkg, PM_PKG_DESC);
+
+ /* check name */
+ haystack = strdup(pkgname);
+ strtoupper(haystack);
+ if(strstr(haystack, targ)) {
+ match = 1;
+ }
+ FREE(haystack);
+
+ /* check description */
+ if(!match) {
+ haystack = strdup(pkgdesc);
+ strtoupper(haystack);
+ if(strstr(haystack, targ)) {
+ match = 1;
+ }
+ FREE(haystack);
+ }
+
+ /* check provides */
+ if(!match) {
+ PM_LIST *m;
+
+ for(m = alpm_pkg_getinfo(pkg, PM_PKG_PROVIDES); m; m = alpm_list_next(m)) {
+ haystack = strdup(alpm_list_getdata(m));
+ strtoupper(haystack);
+ if(strstr(haystack, targ)) {
+ match = 1;
+ }
+ FREE(haystack);
+ }
+ }
+
+ if(match) {
+ printf("%s/%s %s\n ", treename, pkgname, (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION));
+ indentprint(pkgdesc, 4);
+ printf("\n");
+ }
+ }
+
+ FREE(targ);
+
+ return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/db.h b/src/pacman/db.h
new file mode 100644
index 00000000..7b38719f
--- /dev/null
+++ b/src/pacman/db.h
@@ -0,0 +1,28 @@
+/*
+ * db.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_DB_H
+#define _PM_DB_H
+
+int db_search(PM_DB *db, char *treename, char *needle);
+
+#endif /* _PM_DB_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/download.c b/src/pacman/download.c
new file mode 100644
index 00000000..e5ed2c6a
--- /dev/null
+++ b/src/pacman/download.c
@@ -0,0 +1,467 @@
+/*
+ * download.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "config.h"
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <ftplib.h>
+
+#include <alpm.h>
+/* pacman */
+#include "list.h"
+#include "download.h"
+#include "pacman.h"
+
+/* progress bar */
+static char sync_fnm[25];
+static int offset;
+static struct timeval t0, t;
+static float rate;
+static int xfered1;
+static unsigned char eta_h, eta_m, eta_s;
+
+/* pacman options */
+extern char *pmo_root;
+extern char *pmo_dbpath;
+extern char *pmo_proxyhost;
+extern char *pmo_xfercommand;
+
+extern unsigned short pmo_proxyport;
+extern unsigned short pmo_nopassiveftp;
+
+extern int maxcols;
+
+static int log_progress(netbuf *ctl, int xfered, void *arg)
+{
+ int fsz = *(int*)arg;
+ int pct = ((float)(xfered+offset) / fsz) * 100;
+ int i, cur;
+ struct timeval t1;
+ float timediff;
+
+ gettimeofday(&t1, NULL);
+ if(xfered+offset == fsz) {
+ t = t0;
+ }
+ timediff = t1.tv_sec-t.tv_sec + (float)(t1.tv_usec-t.tv_usec) / 1000000;
+
+ if(xfered+offset == fsz) {
+ /* average download rate */
+ rate = xfered / (timediff * 1024);
+ /* total download time */
+ eta_s = (int)timediff;
+ eta_h = eta_s / 3600;
+ eta_s -= eta_h * 3600;
+ eta_m = eta_s / 60;
+ eta_s -= eta_m * 60;
+ } else if(timediff > 1) {
+ /* we avoid computing the rate & ETA on too small periods of time, so that
+ results are more significant */
+ rate = (xfered-xfered1) / (timediff * 1024);
+ xfered1 = xfered;
+ gettimeofday(&t, NULL);
+ eta_s = (fsz-(xfered+offset)) / (rate * 1024);
+ eta_h = eta_s / 3600;
+ eta_s -= eta_h * 3600;
+ eta_m = eta_s / 60;
+ eta_s -= eta_m * 60;
+ }
+
+ printf(" %s [", sync_fnm);
+ cur = (int)((maxcols-64)*pct/100);
+ for(i = 0; i < maxcols-64; i++) {
+ (i < cur) ? printf("#") : printf(" ");
+ }
+ if(rate > 1000) {
+ printf("] %3d%% %6dK %6.0fK/s %02d:%02d:%02d\r", pct, ((xfered+offset) / 1024), rate, eta_h, eta_m, eta_s);
+ } else {
+ printf("] %3d%% %6dK %6.1fK/s %02d:%02d:%02d\r", pct, ((xfered+offset) / 1024), rate, eta_h, eta_m, eta_s);
+ }
+ fflush(stdout);
+ return(1);
+}
+
+static int copyfile(char *src, char *dest)
+{
+ FILE *in, *out;
+ size_t len;
+ char buf[4097];
+
+ in = fopen(src, "r");
+ if(in == NULL) {
+ return(1);
+ }
+ out = fopen(dest, "w");
+ if(out == NULL) {
+ return(1);
+ }
+
+ while((len = fread(buf, 1, 4096, in))) {
+ fwrite(buf, 1, len, out);
+ }
+
+ fclose(in);
+ fclose(out);
+ return(0);
+}
+
+int downloadfiles(list_t *servers, const char *localpath, list_t *files)
+{
+ int fsz;
+ netbuf *control = NULL;
+ list_t *lp;
+ int done = 0;
+ list_t *complete = NULL;
+ list_t *i;
+
+ if(files == NULL) {
+ return(0);
+ }
+
+ for(i = servers; i && !done; i = i->next) {
+ server_t *server = (server_t*)i->data;
+
+ if(!pmo_xfercommand && strcmp(server->protocol, "file")) {
+ if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) {
+ FtpInit();
+ vprint("Connecting to %s:21\n", server->server);
+ if(!FtpConnect(server->server, &control)) {
+ fprintf(stderr, "error: cannot connect to %s\n", server->server);
+ continue;
+ }
+ if(!FtpLogin("anonymous", "arch@guest", control)) {
+ fprintf(stderr, "error: anonymous login failed\n");
+ FtpQuit(control);
+ continue;
+ }
+ if(!FtpChdir(server->path, control)) {
+ fprintf(stderr, "error: could not cwd to %s: %s\n", server->path,
+ FtpLastResponse(control));
+ continue;
+ }
+ if(!pmo_nopassiveftp) {
+ if(!FtpOptions(FTPLIB_CONNMODE, FTPLIB_PASSIVE, control)) {
+ fprintf(stderr, "warning: failed to set passive mode\n");
+ }
+ } else {
+ vprint("FTP passive mode not set\n");
+ }
+ } else if(pmo_proxyhost) {
+ char *host;
+ unsigned port;
+ host = (pmo_proxyhost) ? pmo_proxyhost : server->server;
+ port = (pmo_proxyhost) ? pmo_proxyport : 80;
+ if(strchr(host, ':')) {
+ vprint("Connecting to %s\n", host);
+ } else {
+ vprint("Connecting to %s:%u\n", host, port);
+ }
+ if(!HttpConnect(host, port, &control)) {
+ fprintf(stderr, "error: cannot connect to %s\n", host);
+ continue;
+ }
+ }
+
+ /* set up our progress bar's callback (and idle timeout) */
+ if(strcmp(server->protocol, "file") && control) {
+ 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);
+ }
+ }
+
+ /* get each file in the list */
+ for(lp = files; lp; lp = lp->next) {
+ char *fn = (char *)lp->data;
+
+ if(list_is_strin(fn, complete)) {
+ continue;
+ }
+
+ if(pmo_xfercommand && strcmp(server->protocol, "file")) {
+ int ret;
+ int usepart = 0;
+ char *ptr1, *ptr2;
+ char origCmd[PATH_MAX];
+ char parsedCmd[PATH_MAX] = "";
+ char url[PATH_MAX];
+ char cwd[PATH_MAX];
+ /* build the full download url */
+ snprintf(url, PATH_MAX, "%s://%s%s%s", server->protocol, server->server,
+ server->path, fn);
+ /* replace all occurrences of %o with fn.part */
+ strncpy(origCmd, pmo_xfercommand, sizeof(origCmd));
+ ptr1 = origCmd;
+ while((ptr2 = strstr(ptr1, "%o"))) {
+ usepart = 1;
+ ptr2[0] = '\0';
+ strcat(parsedCmd, ptr1);
+ strcat(parsedCmd, fn);
+ strcat(parsedCmd, ".part");
+ ptr1 = ptr2 + 2;
+ }
+ strcat(parsedCmd, ptr1);
+ /* replace all occurrences of %u with the download URL */
+ strncpy(origCmd, parsedCmd, sizeof(origCmd));
+ parsedCmd[0] = '\0';
+ ptr1 = origCmd;
+ while((ptr2 = strstr(ptr1, "%u"))) {
+ ptr2[0] = '\0';
+ strcat(parsedCmd, ptr1);
+ strcat(parsedCmd, url);
+ ptr1 = ptr2 + 2;
+ }
+ strcat(parsedCmd, ptr1);
+ /* cwd to the download directory */
+ getcwd(cwd, PATH_MAX);
+ if(chdir(localpath)) {
+ fprintf(stderr, "error: could not chdir to %s\n", localpath);
+ return(1);
+ }
+ /* execute the parsed command via /bin/sh -c */
+ vprint("running command: %s\n", parsedCmd);
+ ret = system(parsedCmd);
+ if(ret == -1) {
+ fprintf(stderr, "error running XferCommand: fork failed!\n");
+ return(1);
+ } else if(ret != 0) {
+ /* download failed */
+ vprint("XferCommand command returned non-zero status code (%d)\n", ret);
+ } else {
+ /* download was successful */
+ complete = list_add(complete, fn);
+ if(usepart) {
+ char fnpart[PATH_MAX];
+ /* rename "output.part" file to "output" file */
+ snprintf(fnpart, PATH_MAX, "%s.part", fn);
+ rename(fnpart, fn);
+ }
+ }
+ chdir(cwd);
+ } else {
+ char output[PATH_MAX];
+ int j, filedone = 0;
+ char *ptr;
+ struct stat st;
+ snprintf(output, PATH_MAX, "%s/%s.part", localpath, fn);
+ strncpy(sync_fnm, fn, 24);
+ /* drop filename extension */
+ ptr = strstr(fn, ".db.tar.gz");
+ if(ptr && (ptr-fn) < 24) {
+ sync_fnm[ptr-fn] = '\0';
+ }
+ ptr = strstr(fn, ".pkg.tar.gz");
+ if(ptr && (ptr-fn) < 24) {
+ sync_fnm[ptr-fn] = '\0';
+ }
+ for(j = strlen(sync_fnm); j < 24; j++) {
+ sync_fnm[j] = ' ';
+ }
+ sync_fnm[24] = '\0';
+ offset = 0;
+
+ /* ETA setup */
+ gettimeofday(&t0, NULL);
+ t = t0;
+ rate = 0;
+ xfered1 = 0;
+ eta_h = 0;
+ eta_m = 0;
+ eta_s = 0;
+
+ if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) {
+ if(!FtpSize(fn, &fsz, FTPLIB_IMAGE, control)) {
+ fprintf(stderr, "warning: failed to get filesize for %s\n", fn);
+ }
+ if(!stat(output, &st)) {
+ offset = (int)st.st_size;
+ if(!FtpRestart(offset, control)) {
+ fprintf(stderr, "warning: failed to resume download -- restarting\n");
+ /* can't resume: */
+ /* unlink the file in order to restart download from scratch */
+ unlink(output);
+ }
+ }
+ if(!FtpGet(output, fn, FTPLIB_IMAGE, control)) {
+ fprintf(stderr, "\nfailed downloading %s from %s: %s\n",
+ fn, server->server, FtpLastResponse(control));
+ /* we leave the partially downloaded file in place so it can be resumed later */
+ } else {
+ filedone = 1;
+ }
+ } else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && strcmp(server->protocol, "file"))) {
+ char src[PATH_MAX];
+ char *host;
+ unsigned port;
+ if(!strcmp(server->protocol, "http") && !pmo_proxyhost) {
+ /* HTTP servers hang up after each request (but not proxies), so
+ * we have to re-connect for each file.
+ */
+ host = (pmo_proxyhost) ? pmo_proxyhost : server->server;
+ port = (pmo_proxyhost) ? pmo_proxyport : 80;
+ if(strchr(host, ':')) {
+ vprint("Connecting to %s\n", host);
+ } else {
+ vprint("Connecting to %s:%u\n", host, port);
+ }
+ if(!HttpConnect(host, port, &control)) {
+ fprintf(stderr, "error: cannot connect to %s\n", host);
+ continue;
+ }
+ /* set up our progress bar's callback (and idle timeout) */
+ if(strcmp(server->protocol, "file") && control) {
+ 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);
+ }
+ }
+
+ if(!stat(output, &st)) {
+ offset = (int)st.st_size;
+ }
+ if(!pmo_proxyhost) {
+ snprintf(src, PATH_MAX, "%s%s", server->path, fn);
+ } else {
+ snprintf(src, PATH_MAX, "%s://%s%s%s", server->protocol, server->server, server->path, fn);
+ }
+ if(!HttpGet(server->server, output, src, &fsz, control, offset)) {
+ fprintf(stderr, "\nfailed downloading %s from %s: %s\n",
+ src, server->server, FtpLastResponse(control));
+ /* we leave the partially downloaded file in place so it can be resumed later */
+ } else {
+ filedone = 1;
+ }
+ } else if(!strcmp(server->protocol, "file")) {
+ char src[PATH_MAX];
+ snprintf(src, PATH_MAX, "%s%s", server->path, fn);
+ vprint("copying %s to %s/%s\n", src, localpath, fn);
+ /* local repository, just copy the file */
+ if(copyfile(src, output)) {
+ fprintf(stderr, "failed copying %s\n", src);
+ } else {
+ filedone = 1;
+ }
+ }
+
+ if(filedone) {
+ char completefile[PATH_MAX];
+ if(!strcmp(server->protocol, "file")) {
+ char out[56];
+ printf(" %s [", sync_fnm);
+ strncpy(out, server->path, 33);
+ printf("%s", out);
+ for(j = strlen(out); j < maxcols-64; j++) {
+ printf(" ");
+ }
+ fputs("] 100% LOCAL ", stdout);
+ } else {
+ log_progress(control, fsz-offset, &fsz);
+ }
+ complete = list_add(complete, fn);
+ /* rename "output.part" file to "output" file */
+ snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn);
+ rename(output, completefile);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+ }
+ if(!pmo_xfercommand) {
+ if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) {
+ FtpQuit(control);
+ } else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && strcmp(server->protocol, "file"))) {
+ HttpQuit(control);
+ }
+ }
+
+ if(list_count(complete) == list_count(files)) {
+ done = 1;
+ }
+ }
+
+ return(!done);
+}
+
+char *fetch_pkgurl(char *target)
+{
+ char spath[PATH_MAX];
+ char url[PATH_MAX];
+ server_t *server;
+ list_t *servers = NULL;
+ list_t *files = NULL;
+ char *host, *path, *fn;
+
+ /* ORE
+ do not download the file if it exists in the current dir */
+
+ strncpy(url, target, PATH_MAX);
+ host = strstr(url, "://");
+ *host = '\0';
+ host += 3;
+ path = strchr(host, '/');
+ *path = '\0';
+ path++;
+ fn = strrchr(path, '/');
+ if(fn) {
+ *fn = '\0';
+ if(path[0] == '/') {
+ snprintf(spath, PATH_MAX, "%s/", path);
+ } else {
+ snprintf(spath, PATH_MAX, "/%s/", path);
+ }
+ fn++;
+ } else {
+ fn = path;
+ strcpy(spath, "/");
+ }
+
+ server = (server_t *)malloc(sizeof(server_t));
+ if(server == NULL) {
+ fprintf(stderr, "error: failed to allocate %d bytes\n", sizeof(server_t));
+ return(NULL);
+ }
+ server->protocol = url;
+ server->server = host;
+ server->path = spath;
+ servers = list_add(servers, server);
+
+ files = list_add(files, fn);
+ if(downloadfiles(servers, ".", files)) {
+ fprintf(stderr, "error: failed to download %s\n", target);
+ return(NULL);
+ }
+
+ FREELIST(servers);
+ files->data = NULL;
+ FREELIST(files);
+
+ /* return the target with the raw filename, no URL */
+ return(strndup(fn, PATH_MAX));
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/download.h b/src/pacman/download.h
new file mode 100644
index 00000000..22ecf574
--- /dev/null
+++ b/src/pacman/download.h
@@ -0,0 +1,38 @@
+/*
+ * download.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_DOWNLOAD_H
+#define _PM_DOWNLOAD_H
+
+#include "list.h"
+
+/* Servers */
+typedef struct __server_t {
+ char *protocol;
+ char *server;
+ char *path;
+} server_t;
+
+int downloadfiles(list_t *servers, const char *localpath, list_t *files);
+char *fetch_pkgurl(char *target);
+
+#endif /* _PM_DOWNLOAD_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/list.c b/src/pacman/list.c
new file mode 100644
index 00000000..84af1784
--- /dev/null
+++ b/src/pacman/list.c
@@ -0,0 +1,176 @@
+/*
+ * list.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+/* pacman */
+#include "list.h"
+
+extern int maxcols;
+
+static list_t *list_last(list_t *list);
+
+list_t *list_new()
+{
+ list_t *list = NULL;
+
+ list = (list_t *)malloc(sizeof(list_t));
+ if(list == NULL) {
+ return(NULL);
+ }
+ list->data = NULL;
+ list->next = NULL;
+ return(list);
+}
+
+void list_free(list_t *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;
+}
+
+list_t *list_add(list_t *list, void *data)
+{
+ list_t *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 = lp->next;
+ }
+ lp->data = data;
+ return(ptr);
+}
+
+int list_count(list_t *list)
+{
+ int i;
+ list_t *lp;
+
+ for(lp = list, i = 0; lp; lp = lp->next, i++);
+
+ return(i);
+}
+
+static list_t *list_last(list_t *list)
+{
+ list_t *ptr;
+
+ for(ptr = list; ptr && ptr->next; ptr = ptr->next);
+ return(ptr);
+}
+
+/* Test for existence of a string in a list_t
+ */
+int list_is_strin(char *needle, list_t *haystack)
+{
+ list_t *lp;
+
+ for(lp = haystack; lp; lp = lp->next) {
+ if(lp->data && !strcmp(lp->data, needle)) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/* Display the content of a list_t struct of strings
+ */
+
+void list_display(const char *title, list_t *list)
+{
+ list_t *lp;
+ int cols, len;
+
+ len = strlen(title);
+ printf("%s ", title);
+
+ if(list) {
+ for(lp = list, cols = len; lp; lp = lp->next) {
+ int s = strlen((char *)lp->data)+1;
+ if(s+cols >= maxcols) {
+ int i;
+ cols = len;
+ printf("\n");
+ for (i = 0; i < len+1; i++) {
+ printf(" ");
+ }
+ }
+ printf("%s ", (char *)lp->data);
+ cols += s;
+ }
+ printf("\n");
+ } else {
+ printf("None\n");
+ }
+}
+
+void PM_LIST_display(const char *title, PM_LIST *list)
+{
+ PM_LIST *lp;
+ int cols, len;
+
+ len = strlen(title);
+ printf("%s ", title);
+
+ if(list) {
+ for(lp = list, cols = len; lp; lp = alpm_list_next(lp)) {
+ int s = strlen(alpm_list_getdata(lp))+1;
+ if(s+cols >= maxcols) {
+ int i;
+ cols = len;
+ printf("\n");
+ for (i = 0; i < len+1; i++) {
+ printf(" ");
+ }
+ }
+ printf("%s ", (char *)alpm_list_getdata(lp));
+ cols += s;
+ }
+ printf("\n");
+ } else {
+ printf("None\n");
+ }
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/list.h b/src/pacman/list.h
new file mode 100644
index 00000000..ca738f9e
--- /dev/null
+++ b/src/pacman/list.h
@@ -0,0 +1,45 @@
+/*
+ * list.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_LIST_H
+#define _PM_LIST_H
+
+#include <alpm.h>
+
+/* Chained list struct */
+typedef struct __list_t {
+ void *data;
+ struct __list_t *next;
+} list_t;
+
+#define FREELIST(p) do { if(p) { list_free(p); p = NULL; } } while(0)
+
+list_t *list_new();
+void list_free(list_t* list);
+list_t *list_add(list_t* list, void *data);
+int list_count(list_t* list);
+int list_is_strin(char *needle, list_t *haystack);
+void list_display(const char *title, list_t *list);
+
+void PM_LIST_display(const char *title, PM_LIST *list);
+
+#endif /* _PM_LIST_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/package.c b/src/pacman/package.c
new file mode 100644
index 00000000..aa47d77f
--- /dev/null
+++ b/src/pacman/package.c
@@ -0,0 +1,203 @@
+/*
+ * package.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <alpm.h>
+/* pacman */
+#include "util.h"
+#include "list.h"
+#include "package.h"
+
+extern char *pmo_root;
+
+/* Display the content of an installed package
+ */
+void dump_pkg_full(PM_PKG *pkg, int level)
+{
+ char *date;
+
+ if(pkg == NULL) {
+ return;
+ }
+
+ printf("Name : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME));
+ printf("Version : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION));
+
+ PM_LIST_display("Groups :", alpm_pkg_getinfo(pkg, PM_PKG_GROUPS));
+
+ printf("Packager : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_PACKAGER));
+ printf("URL : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_URL));
+ printf("License : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_LICENSE));
+ printf("Architecture : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_ARCH));
+ printf("Size : %ld\n", (long int)alpm_pkg_getinfo(pkg, PM_PKG_SIZE));
+
+ date = alpm_pkg_getinfo(pkg, PM_PKG_BUILDDATE);
+ printf("Build Date : %s %s\n", date, strlen(date) ? "UTC" : "");
+
+ date = alpm_pkg_getinfo(pkg, PM_PKG_INSTALLDATE);
+ printf("Install Date : %s %s\n", date, strlen(date) ? "UTC" : "");
+
+ printf("Install Script : %s\n", (alpm_pkg_getinfo(pkg, PM_PKG_SCRIPLET) ? "Yes" : "No"));
+
+ printf("Reason: : ");
+ switch((int)alpm_pkg_getinfo(pkg, PM_PKG_REASON)) {
+ case PM_PKG_REASON_EXPLICIT:
+ printf("explicitly installed\n");
+ break;
+ case PM_PKG_REASON_DEPEND:
+ printf("installed as a dependency for another package\n");
+ break;
+ default:
+ printf("unknown\n");
+ break;
+ }
+
+ PM_LIST_display("Provides :", alpm_pkg_getinfo(pkg, PM_PKG_PROVIDES));
+ PM_LIST_display("Depends On :", alpm_pkg_getinfo(pkg, PM_PKG_DEPENDS));
+ PM_LIST_display("Required By :", alpm_pkg_getinfo(pkg, PM_PKG_REQUIREDBY));
+ PM_LIST_display("Conflicts With :", alpm_pkg_getinfo(pkg, PM_PKG_CONFLICTS));
+
+ printf("Description : ");
+ indentprint(alpm_pkg_getinfo(pkg, PM_PKG_DESC), 17);
+ printf("\n");
+
+ /*if(level > 1 && info->backup) {
+ PM_LIST *i;
+ fprintf(stdout, "\n");
+ for(i = alpm_first_entry(info->backup); i; i = alpm_next_entry(i)) {
+ struct stat buf;
+ char path[PATH_MAX];
+ char *md5sum;
+ char *str = strdup(alpm_get_entry(i));
+ char *ptr = index(str, '\t');
+ if(ptr == NULL) {
+ free(str);
+ str = NULL;
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ snprintf(path, PATH_MAX-1, "%s%s", pmo_root, str);
+ if(!stat(path, &buf)) {
+ md5sum = alpm_get_md5sum(path);
+ if(md5sum == NULL) {
+ fprintf(stderr, "error calculating md5sum for %s\n", path);
+ continue;
+ }
+ if(strcmp(md5sum, ptr)) {
+ fprintf(stdout, "MODIFIED\t%s\n", path);
+ } else {
+ fprintf(stdout, "NOT MODIFIED\t%s\n", path);
+ }
+ } else {
+ fprintf(stdout, "MISSING\t\t%s\n", path);
+ }
+ free(str);
+ str = NULL;
+ }
+ }*/
+
+ printf("\n");
+}
+
+
+/* Display the content of a sync package
+ */
+void dump_pkg_sync(PM_PKG *pkg, char *treename)
+{
+ if(pkg == NULL) {
+ return;
+ }
+
+ printf("Repository : %s\n", treename);
+ printf("Name : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME));
+ printf("Version : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION));
+
+ PM_LIST_display("Groups :", alpm_pkg_getinfo(pkg, PM_PKG_GROUPS));
+ PM_LIST_display("Provides :", alpm_pkg_getinfo(pkg, PM_PKG_PROVIDES));
+ PM_LIST_display("Depends On :", alpm_pkg_getinfo(pkg, PM_PKG_DEPENDS));
+ PM_LIST_display("Conflicts With :", alpm_pkg_getinfo(pkg, PM_PKG_CONFLICTS));
+ PM_LIST_display("Replaces :", alpm_pkg_getinfo(pkg, PM_PKG_REPLACES));
+
+ printf("Size (compressed) : %ld\n", (long)alpm_pkg_getinfo(pkg, PM_PKG_SIZE));
+ printf("Description : ");
+ indentprint(alpm_pkg_getinfo(pkg, PM_PKG_DESC), 17);
+ printf("\nMD5 Sum : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_MD5SUM));
+}
+
+void dump_pkg_files(PM_PKG *pkg)
+{
+ char *pkgname;
+ PM_LIST *i, *pkgfiles;
+
+ pkgname = alpm_pkg_getinfo(pkg, PM_PKG_NAME);
+ pkgfiles = alpm_pkg_getinfo(pkg, PM_PKG_FILES);
+
+ for(i = pkgfiles; i; i = alpm_list_next(i)) {
+ fprintf(stdout, "%s %s\n", (char *)pkgname, (char *)alpm_list_getdata(i));
+ }
+
+ fflush(stdout);
+}
+
+int split_pkgname(char *target, char *name, char *version)
+{
+ char tmp[512];
+ char *p, *q;
+
+ if(target == NULL) {
+ return(-1);
+ }
+
+ /* trim path name (if any) */
+ if((p = strrchr(target, '/')) == NULL) {
+ p = target;
+ } else {
+ p++;
+ }
+ strncpy(tmp, p, 512);
+ /* trim file extension (if any) */
+ if((p = strstr(tmp, ".pkg.tar.gz"))) {
+ *p = 0;
+ }
+
+ p = tmp + strlen(tmp);
+
+ for(q = --p; *q && *q != '-'; q--);
+ if(*q != '-' || q == tmp) {
+ return(-1);
+ }
+ for(p = --q; *p && *p != '-'; p--);
+ if(*p != '-' || p == tmp) {
+ return(-1);
+ }
+ strncpy(version, p+1, 64);
+ *p = 0;
+
+ strncpy(name, tmp, 256);
+
+ return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/package.h b/src/pacman/package.h
new file mode 100644
index 00000000..5ffdd278
--- /dev/null
+++ b/src/pacman/package.h
@@ -0,0 +1,35 @@
+/*
+ * package.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_PACKAGE_H
+#define _PM_PACKAGE_H
+
+void dump_pkg_full(PM_PKG *pkg, int level);
+void dump_pkg_sync(PM_PKG *pkg, char *treename);
+
+void dump_pkg_files(PM_PKG *pkg);
+
+int split_pkgname(char *target, char *name, char *version);
+
+#define FREEPKG(p) { alpm_pkg_free(p); p = NULL; }
+
+#endif /* _PM_PACKAGE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
new file mode 100644
index 00000000..1ecc3ec6
--- /dev/null
+++ b/src/pacman/pacman.c
@@ -0,0 +1,688 @@
+/*
+ * pacman.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <getopt.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <mcheck.h> /* debug */
+
+#include <alpm.h>
+/* pacman */
+#include "list.h"
+#include "util.h"
+#include "download.h"
+#include "conf.h"
+#include "package.h"
+#include "add.h"
+#include "remove.h"
+#include "upgrade.h"
+#include "query.h"
+#include "sync.h"
+#include "pacman.h"
+
+/* command line options */
+char *pmo_root = NULL;
+char *pmo_dbpath = NULL;
+char *pmo_configfile = NULL;
+unsigned short pmo_op = PM_OP_MAIN;
+unsigned short pmo_verbose = 0;
+unsigned short pmo_version = 0;
+unsigned short pmo_help = 0;
+unsigned short pmo_upgrade = 0;
+unsigned short pmo_nosave = 0;
+unsigned short pmo_noconfirm = 0;
+unsigned short pmo_d_vertest = 0;
+unsigned short pmo_d_resolve = 0;
+unsigned short pmo_q_isfile = 0;
+unsigned short pmo_q_info = 0;
+unsigned short pmo_q_list = 0;
+unsigned short pmo_q_orphans = 0;
+unsigned short pmo_q_owns = 0;
+unsigned short pmo_q_search = 0;
+unsigned short pmo_s_upgrade = 0;
+unsigned short pmo_s_downloadonly = 0;
+unsigned short pmo_s_printuris = 0;
+unsigned short pmo_s_sync = 0;
+unsigned short pmo_s_search = 0;
+unsigned short pmo_s_clean = 0;
+unsigned short pmo_group = 0;
+unsigned char pmo_flags = 0;
+/* configuration file option */
+list_t *pmo_holdpkg = NULL;
+char *pmo_proxyhost = NULL;
+unsigned short pmo_proxyport = 0;
+char *pmo_xfercommand = NULL;
+unsigned short pmo_nopassiveftp = 0;
+
+PM_DB *db_local;
+/* list of (sync_t *) structs for sync locations */
+list_t *pmc_syncs = NULL;
+/* list of targets specified on command line */
+list_t *pm_targets = NULL;
+
+int maxcols = 80;
+
+int neednl = 0; /* for cleaner message output */
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ char *cenv = NULL;
+
+ /* debug */
+ mtrace();
+
+ cenv = getenv("COLUMNS");
+ if(cenv != NULL) {
+ maxcols = atoi(cenv);
+ }
+
+ if(argc < 2) {
+ usage(PM_OP_MAIN, basename(argv[0]));
+ return(0);
+ }
+
+ /* set signal handlers */
+ signal(SIGINT, cleanup);
+ signal(SIGTERM, cleanup);
+
+ /* parse the command line */
+ ret = parseargs(argc, argv);
+ if(ret != 0) {
+ exit(ret);
+ }
+
+ if(pmo_root == NULL) {
+ pmo_root = strdup("/");
+ }
+
+ /* initialize pm library */
+ if(alpm_initialize(pmo_root) == -1) {
+ ERR(NL, "failed to initilize alpm library (%s)\n", alpm_strerror(pm_errno));
+ cleanup(1);
+ }
+
+ /* add a trailing '/' if there isn't one */
+ if(pmo_root[strlen(pmo_root)-1] != '/') {
+ char *ptr;
+ MALLOC(ptr, strlen(pmo_root)+2);
+ strcpy(ptr, pmo_root);
+ strcat(ptr, "/");
+ FREE(pmo_root);
+ pmo_root = ptr;
+ }
+
+ if(pmo_configfile == NULL) {
+ pmo_configfile = strdup(PACCONF);
+ }
+ if(parseconfig(pmo_configfile) == -1) {
+ cleanup(1);
+ }
+ if(pmo_dbpath == NULL) {
+ pmo_dbpath = strdup("var/lib/pacman");
+ }
+
+ /* set library parameters */
+ if(pmo_verbose == 1) {
+ if(alpm_set_option(PM_OPT_LOGMASK, (long)0xFF) == -1) {
+ ERR(NL, "failed to set option LOGMASK (%s)\n", alpm_strerror(pm_errno));
+ cleanup(1);
+ }
+ if(alpm_set_option(PM_OPT_LOGCB, (long)cb_log) == -1) {
+ ERR(NL, "failed to set option LOGCB (%s)\n", alpm_strerror(pm_errno));
+ cleanup(1);
+ }
+ }
+ if(alpm_set_option(PM_OPT_DBPATH, (long)pmo_dbpath) == -1) {
+ ERR(NL, "failed to set option DBPATH (%s)\n", alpm_strerror(pm_errno));
+ cleanup(1);
+ }
+
+ if(pmo_verbose > 1) {
+ printf("Root : %s\n", pmo_root);
+ printf("DBPath: %s\n", pmo_dbpath);
+ list_display("Targets:", pm_targets);
+ }
+
+ /* Opening local database */
+ if(alpm_db_register("local", &db_local) == -1) {
+ ERR(NL, "could not register 'local' database (%s)\n", alpm_strerror(pm_errno));
+ cleanup(1);
+ }
+
+ /* start the requested operation */
+ switch(pmo_op) {
+ case PM_OP_ADD: ret = pacman_add(pm_targets); break;
+ case PM_OP_REMOVE: ret = pacman_remove(pm_targets); break;
+ case PM_OP_UPGRADE: ret = pacman_upgrade(pm_targets); break;
+ case PM_OP_QUERY: ret = pacman_query(pm_targets); break;
+ case PM_OP_SYNC: ret = pacman_sync(pm_targets); break;
+ case PM_OP_DEPTEST: ret = pacman_deptest(pm_targets); break;
+ case PM_OP_MAIN: ret = 0; break;
+ default:
+ ERR(NL, "no operation specified (use -h for help)\n\n");
+ ret = 1;
+ }
+
+ cleanup(ret);
+ /* not reached */
+ return(0);
+}
+
+void cleanup(int signum)
+{
+ list_t *lp;
+
+ /* free pm library resources */
+ if(alpm_release() == -1) {
+ ERR(NL, "%s\n", alpm_strerror(pm_errno));
+ }
+
+ /* free memory */
+ for(lp = pmc_syncs; lp; lp = lp->next) {
+ sync_t *sync = lp->data;
+ list_t *i;
+ for(i = sync->servers; i; i = i->next) {
+ server_t *server = i->data;
+ FREE(server->protocol);
+ FREE(server->server);
+ FREE(server->path);
+ }
+ FREELIST(sync->servers);
+ FREE(sync->treename);
+ }
+ FREELIST(pmc_syncs);
+ FREE(pmo_root);
+ FREE(pmo_dbpath);
+ FREE(pmo_configfile);
+ FREE(pmo_proxyhost);
+ FREE(pmo_xfercommand);
+
+ FREELIST(pm_targets);
+
+ /* debug */
+ muntrace();
+
+ fflush(stdout);
+
+ exit(signum);
+}
+
+/* Callback to handle notifications from the library
+ */
+void cb_log(unsigned short level, char *msg)
+{
+ char str[8] = "";
+
+ switch(level) {
+ case PM_LOG_DEBUG:
+ sprintf(str, "DEBUG");
+ break;
+ case PM_LOG_ERROR:
+ sprintf(str, "ERROR");
+ break;
+ case PM_LOG_WARNING:
+ sprintf(str, "WARNING");
+ break;
+ case PM_LOG_FLOW1:
+ sprintf(str, "FLOW1");
+ break;
+ case PM_LOG_FLOW2:
+ sprintf(str, "FLOW2");
+ break;
+ case PM_LOG_FUNCTION:
+ sprintf(str, "FUNCTION");
+ break;
+ default:
+ sprintf(str, "???");
+ break;
+ }
+
+ if(strlen(str) > 0) {
+ MSG(NL, "%s: %s\n", str, msg);
+ }
+}
+
+/* Callback to handle transaction events
+ */
+void cb_trans(unsigned short event, void *data1, void *data2)
+{
+ char str[256] = "";
+
+ switch(event) {
+ case PM_TRANS_CB_DEPS_START:
+ MSG(NL, "checking dependencies... ");
+ break;
+ case PM_TRANS_CB_CONFLICTS_START:
+ MSG(NL, "checking for file conflicts... ");
+ break;
+ case PM_TRANS_CB_DEPS_DONE:
+ case PM_TRANS_CB_CONFLICTS_DONE:
+ MSG(CL, "done.\n");
+ break;
+ case PM_TRANS_CB_ADD_START:
+ MSG(NL, "installing %s... ", (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME));
+ break;
+ case PM_TRANS_CB_ADD_DONE:
+ MSG(CL, "done.\n");
+ snprintf(str, 256, "installed %s (%s)",
+ (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME),
+ (char *)alpm_pkg_getinfo(data1, PM_PKG_VERSION));
+ alpm_logaction(str);
+ break;
+ case PM_TRANS_CB_REMOVE_START:
+ MSG(NL, "removing %s... ", (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME));
+ break;
+ case PM_TRANS_CB_REMOVE_DONE:
+ MSG(CL, "done.\n");
+ snprintf(str, 256, "removed %s (%s)",
+ (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME),
+ (char *)alpm_pkg_getinfo(data1, PM_PKG_VERSION));
+ alpm_logaction(str);
+ break;
+ case PM_TRANS_CB_UPGRADE_START:
+ MSG(NL, "upgrading %s... ", (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME));
+ break;
+ case PM_TRANS_CB_UPGRADE_DONE:
+ MSG(CL, "done.\n");
+ snprintf(str, 256, "upgraded %s (%s -> %s)",
+ (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME),
+ (char *)alpm_pkg_getinfo(data1, PM_PKG_VERSION),
+ (char *)alpm_pkg_getinfo(data2, PM_PKG_VERSION));
+ alpm_logaction(str);
+ break;
+ }
+}
+
+int pacman_deptest(list_t *targets)
+{
+ PM_LIST *lp, *data;
+ PM_PKG *dummy;
+
+ if(pmo_d_vertest) {
+ /* ORE
+ 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);
+ }
+
+ /* we create a transaction to hold a dummy package to be able to use
+ * pm_trans_prepare() */
+ /* ORE
+ trans = alpm_trans_new(PM_TRANS_ADD, 0);
+ if(trans == NULL) {
+ FREEPKG(dummy);
+ ERR(NL, "error: can't allocate %d bytes\n", sizeof(pm_pkginfo_t));
+ exit(1);
+ }
+
+ dummy = (pm_pkginfo_t *)malloc(sizeof(pm_pkginfo_t));
+ if(dummy == NULL) {
+ ERR(NL, "error: can't allocate %d bytes\n", sizeof(pm_pkginfo_t));
+ exit(1);
+ }
+ sprintf(dummy->name, "_dummy_");
+ sprintf(dummy->version, "1.0-1");
+
+ for(i = targets; i; i = i->next) {
+ if(i->data == NULL) continue;
+ dummy->depends = list_add(dummy->depends, strdup(i->data)));
+ }
+
+ trans->targets = list_add(trans->targets, strdup(dummy->name));*/
+
+ if(alpm_trans_prepare(&data) == -1) {
+ int ret = 126;
+ list_t *synctargs = NULL;
+ switch(pm_errno) {
+ case PM_ERR_UNSATISFIED_DEPS:
+ for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) {
+ pmdepmissing_t *miss = alpm_list_getdata(lp);
+ if(!pmo_d_resolve) {
+ MSG(NL, "requires: %s", miss->depend.name);
+ switch(miss->depend.mod) {
+ case PM_DEP_EQ: MSG(CL, "=%s", miss->depend.version); break;
+ case PM_DEP_GE: MSG(CL, ">=%s", miss->depend.version); break;
+ case PM_DEP_LE: MSG(CL, "<=%s", miss->depend.version); break;
+ }
+ MSG(CL, "\n");
+ }
+ synctargs = list_add(synctargs, strdup(miss->depend.name));
+ }
+ alpm_list_free(data);
+ break;
+ case PM_ERR_CONFLICTING_DEPS:
+ /* we can't auto-resolve conflicts */
+ for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) {
+ pmdepmissing_t *miss = alpm_list_getdata(lp);
+ MSG(NL, "conflict: %s", miss->depend.name);
+ }
+ ret = 127;
+ alpm_list_free(data);
+ break;
+ default:
+ ret = 127;
+ break;
+ }
+ /* attempt to resolve missing dependencies */
+ /* TODO: handle version comparators (eg, glibc>=2.2.5) */
+ if(ret == 126 && synctargs != NULL) {
+ if(!pmo_d_resolve || pacman_sync(synctargs) != 0) {
+ /* error (or -D not used) */
+ ret = 127;
+ }
+ }
+ FREELIST(synctargs);
+ FREEPKG(dummy);
+ return(ret);
+ }
+
+ FREEPKG(dummy);
+
+ return(0);
+}
+
+/* Parse command-line arguments for each operation
+ * argc: argc
+ * argv: argv
+ *
+ * Returns: 0 on success, 1 on error
+ */
+int parseargs(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'},
+ {"freshen", no_argument, 0, 'F'},
+ {"query", no_argument, 0, 'Q'},
+ {"sync", no_argument, 0, 'S'},
+ {"deptest", no_argument, 0, 'T'},
+ {"vertest", no_argument, 0, 'Y'},
+ {"resolve", no_argument, 0, 'D'},
+ {"root", required_argument, 0, 'r'},
+ {"dbpath", required_argument, 0, 'b'},
+ {"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'},
+ {"orphans", no_argument, 0, 'e'},
+ {"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'},
+ {"downloadonly", no_argument, 0, 'w'},
+ {"refresh", no_argument, 0, 'y'},
+ {"dbonly", no_argument, 0, 'k'},
+ {"cascade", no_argument, 0, 'c'},
+ {"recursive", no_argument, 0, 's'},
+ {"groups", no_argument, 0, 'g'},
+ {"noconfirm", no_argument, 0, 1000},
+ {"config", required_argument, 0, 1001},
+ {0, 0, 0, 0}
+ };
+ char root[256];
+
+ while((opt = getopt_long(argc, argv, "ARUFQSTDYr:b:vkhscVfnoldepiuwyg", opts, &option_index))) {
+ if(opt < 0) {
+ break;
+ }
+ switch(opt) {
+ case 0: break;
+ case 1000: pmo_noconfirm = 1; break;
+ case 1001: pmo_configfile = strndup(optarg, PATH_MAX); break;
+ case 'A': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_ADD); break;
+ case 'R': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
+ case 'U': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
+ case 'F': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); pmo_flags |= PM_TRANS_FLAG_FRESHEN; break;
+ case 'Q': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
+ case 'S': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
+ case 'T': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
+ case 'Y': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); pmo_d_vertest = 1; break;
+ case 'D': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); pmo_d_resolve = 1; break;
+ case 'h': pmo_help = 1; break;
+ case 'V': pmo_version = 1; break;
+ case 'b': pmo_dbpath = strdup(optarg); break;
+ case 'c': pmo_s_clean++; pmo_flags |= PM_TRANS_FLAG_CASCADE; break;
+ case 'd': pmo_flags |= PM_TRANS_FLAG_NODEPS; break;
+ case 'e': pmo_q_orphans = 1; break;
+ case 'f': pmo_flags |= PM_TRANS_FLAG_FORCE; break;
+ case 'g': pmo_group = 1; break;
+ case 'i': pmo_q_info++; break;
+ case 'k': pmo_flags |= PM_TRANS_FLAG_DBONLY; break;
+ case 'l': pmo_q_list = 1; break;
+ case 'n': pmo_flags |= PM_TRANS_FLAG_NOSAVE; break;
+ case 'p': pmo_q_isfile = 1; break;
+ case 'o': pmo_q_owns = 1; break;
+ case 'r':
+ if(realpath(optarg, root) == NULL) {
+ perror("bad root path");
+ return(1);
+ }
+ pmo_root = strdup(root);
+ break;
+ case 's': pmo_s_search = 1; pmo_q_search = 1; pmo_flags |= PM_TRANS_FLAG_RECURSE; break;
+ case 'u': pmo_s_upgrade = 1; break;
+ case 'v': pmo_verbose++; break;
+ case 'w': pmo_s_downloadonly = 1; break;
+ case 'y': pmo_s_sync = 1; break;
+ case '?': return(1);
+ default: return(1);
+ }
+ }
+
+ if(pmo_op == 0) {
+ ERR(NL, "only one operation may be used at a time\n\n");
+ return(1);
+ }
+
+ if(pmo_help) {
+ usage(pmo_op, 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);
+}
+
+/* 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_OP_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 {-F --freshen} [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_OP_ADD) {
+ printf("usage: %s {-A --add} [options] <file>\n", myname);
+ printf("options:\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ printf(" -f, --force force install, overwrite conflicting files\n");
+ } else if(op == PM_OP_REMOVE) {
+ printf("usage: %s {-R --remove} [options] <package>\n", myname);
+ printf("options:\n");
+ printf(" -c, --cascade remove packages and all packages that depend on them\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ printf(" -k, --dbonly only remove database entry, do not remove files\n");
+ printf(" -n, --nosave remove configuration files as well\n");
+ printf(" -s, --recursive remove dependencies also (that won't break packages)\n");
+ } else if(op == PM_OP_UPGRADE) {
+ if(pmo_flags & PM_TRANS_FLAG_FRESHEN) {
+ printf("usage: %s {-F --freshen} [options] <file>\n", myname);
+ } else {
+ printf("usage: %s {-U --upgrade} [options] <file>\n", myname);
+ }
+ printf("options:\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ printf(" -f, --force force install, overwrite conflicting files\n");
+ } else if(op == PM_OP_QUERY) {
+ printf("usage: %s {-Q --query} [options] [package]\n", myname);
+ printf("options:\n");
+ printf(" -e, --orphans list all packages that were explicitly installed\n");
+ printf(" and are not required by any other packages\n");
+ printf(" -g, --groups view all members of a package group\n");
+ printf(" -i, --info view package information\n");
+ printf(" -l, --list list the contents of the queried package\n");
+ printf(" -o, --owns <file> query the package that owns <file>\n");
+ printf(" -p, --file pacman will query the package file [package] instead of\n");
+ printf(" looking in the database\n");
+ printf(" -s, --search search locally-installed packages for matching strings\n");
+ } else if(op == PM_OP_SYNC) {
+ printf("usage: %s {-S --sync} [options] [package]\n", myname);
+ printf("options:\n");
+ printf(" -c, --clean remove old packages from cache directory (use -cc for all)\n");
+ printf(" -d, --nodeps skip dependency checks\n");
+ printf(" -f, --force force install, overwrite conflicting files\n");
+ printf(" -g, --groups view all members of a package group\n");
+ printf(" -s, --search search remote repositories for matching strings\n");
+ printf(" -u, --sysupgrade upgrade all packages that are out of date\n");
+ printf(" -w, --downloadonly download packages but do not install/upgrade anything\n");
+ printf(" -y, --refresh download fresh package databases from the server\n");
+ }
+ printf(" --config <path> set an alternate configuration file\n");
+ printf(" --noconfirm do not ask for anything confirmation\n");
+ printf(" -v, --verbose be verbose\n");
+ printf(" -r, --root <path> set an alternate installation root\n");
+ printf(" -b, --dbpath <path> set an alternate database location\n");
+ }
+}
+
+/* Version
+ */
+void version()
+{
+ printf("\n");
+ printf(" .--. Pacman v%s\n", ALPM_VERSION);
+ printf("/ _.-' .-. .-. .-. Copyright (C) 2002-2003 Judd Vinet <jvinet@zeroflux.org>\n");
+ printf("\\ '-. '-' '-' '-' \n");
+ printf(" '--' This program may be freely redistributed under\n");
+ printf(" the terms of the GNU General Public License\n\n");
+}
+
+/*
+ * Misc functions
+ */
+
+/* Condense a list of strings into one long (space-delimited) string
+ */
+char *buildstring(list_t *strlist)
+{
+ char *str;
+ int size = 1;
+ list_t *lp;
+
+ for(lp = strlist; lp; lp = lp->next) {
+ size += strlen(lp->data) + 1;
+ }
+ str = (char *)malloc(size);
+ if(str == NULL) {
+ ERR(NL, "failed to allocated %d bytes\n", size);
+ }
+ str[0] = '\0';
+ for(lp = strlist; lp; lp = lp->next) {
+ strcat(str, lp->data);
+ strcat(str, " ");
+ }
+ /* shave off the last space */
+ str[strlen(str)-1] = '\0';
+
+ return(str);
+}
+
+/* Check verbosity option and, if set, print the
+ * string to stdout
+ */
+void vprint(char *fmt, ...)
+{
+ va_list args;
+
+ if(pmo_verbose > 1) {
+ if(neednl == 1) {
+ fprintf(stdout, "\n");
+ neednl = 0;
+ }
+ va_start(args, fmt);
+ pm_fprintf(stdout, NL, fmt, args);
+ va_end(args);
+ }
+}
+
+void pm_fprintf(FILE *file, unsigned short line, char *fmt, ...)
+{
+ va_list args;
+
+ char str[256];
+
+ if(neednl == 1 && line == NL) {
+ fprintf(stdout, "\n");
+ neednl = 0;
+ }
+
+ va_start(args, fmt);
+ vsnprintf(str, 256, fmt, args);
+ va_end(args);
+
+ fprintf(file, str);
+ fflush(file);
+
+ neednl = (str[strlen(str)-1] == 10) ? 0 : 1;
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/pacman.h b/src/pacman/pacman.h
new file mode 100644
index 00000000..e4e99b50
--- /dev/null
+++ b/src/pacman/pacman.h
@@ -0,0 +1,75 @@
+/*
+ * pacman.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_PACMAN_H
+#define _PM_PACMAN_H
+
+#ifndef PACCONF
+#define PACCONF "/etc/pacman.conf"
+#endif
+#ifndef CACHEDIR
+#define CACHEDIR "var/cache/pacman/pkg"
+#endif
+
+/* Operations */
+#define PM_OP_MAIN 1
+#define PM_OP_ADD 2
+#define PM_OP_REMOVE 3
+#define PM_OP_UPGRADE 4
+#define PM_OP_QUERY 5
+#define PM_OP_SYNC 6
+#define PM_OP_DEPTEST 7
+
+#define MSG(line, fmt, args...) pm_fprintf(stdout, line, fmt, ##args)
+#define ERR(line, fmt, args...) do { \
+ pm_fprintf(stderr, line, "error: "); \
+ pm_fprintf(stderr, CL, fmt, ##args); \
+} while(0)
+#define DBG(line, fmt, args...) do { \
+ char str[256]; \
+ snprintf(str, 256, fmt, ##args); \
+ cb_log(PM_LOG_DEBUG, str); \
+} while(0)
+
+enum {
+ NL, /* new line */
+ CL /* current line */
+};
+/* callback to handle messages/notifications from pacman library */
+void cb_log(unsigned short level, char *msg);
+/* callback to handle messages/notifications from pacman transactions */
+void cb_trans(unsigned short event, void *data1, void *data2);
+
+void cleanup(int signum);
+
+int pacman_deptest(list_t *targets);
+
+int parseargs(int argc, char **argv);
+
+void usage(int op, char *myname);
+void version();
+
+char *buildstring(list_t *strlist);
+void vprint(char *fmt, ...);
+void pm_fprintf(FILE *file, unsigned short line, char *fmt, ...);
+
+#endif /* _PM_PACMAN_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/query.c b/src/pacman/query.c
new file mode 100644
index 00000000..5eec0004
--- /dev/null
+++ b/src/pacman/query.c
@@ -0,0 +1,247 @@
+/*
+ * query.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <alpm.h>
+/* pacman */
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "query.h"
+#include "pacman.h"
+
+extern char *pmo_root;
+extern unsigned short pmo_q_isfile;
+extern unsigned short pmo_q_info;
+extern unsigned short pmo_q_list;
+extern unsigned short pmo_q_orphans;
+extern unsigned short pmo_q_owns;
+extern unsigned short pmo_q_search;
+extern unsigned short pmo_group;
+extern PM_DB *db_local;
+
+static int query_fileowner(PM_DB *db, char *filename)
+{
+ struct stat buf;
+ int gotcha = 0;
+ char rpath[PATH_MAX];
+ PM_LIST *lp;
+
+ if(db == NULL) {
+ return(0);
+ }
+ if(filename == NULL || strlen(filename) == 0) {
+ fprintf(stderr, "error: no file was specified for --owns\n");
+ return(1);
+ }
+
+ if(stat(filename, &buf) == -1 || S_ISDIR(buf.st_mode) || realpath(filename, rpath) == NULL) {
+ fprintf(stderr, "error: %s is not a file.\n", filename);
+ return(1);
+ }
+
+ for(lp = alpm_db_getpkgcache(db); lp && !gotcha; lp = alpm_list_next(lp)) {
+ PM_PKG *info;
+ char *pkgname;
+ PM_LIST *i;
+
+ pkgname = alpm_pkg_getinfo(alpm_list_getdata(lp), PM_PKG_NAME);
+
+ info = alpm_db_readpkg(db, pkgname);
+ if(info == NULL) {
+ fprintf(stderr, "error: package %s not found\n", pkgname);
+ return(1);
+ }
+
+ for(i = alpm_pkg_getinfo(info, PM_PKG_FILES); i && !gotcha; i = alpm_list_next(i)) {
+ char path[PATH_MAX];
+
+ snprintf(path, PATH_MAX, "%s%s", pmo_root, (char *)alpm_list_getdata(i));
+ if(!strcmp(path, rpath)) {
+ printf("%s is owned by %s %s\n", filename, pkgname,
+ (char *)alpm_pkg_getinfo(info, PM_PKG_VERSION));
+ gotcha = 1;
+ break;
+ }
+ }
+ }
+ if(!gotcha) {
+ fprintf(stderr, "No package owns %s\n", filename);
+ return(1);
+ }
+
+ return(0);
+}
+
+int pacman_query(list_t *targets)
+{
+ PM_PKG *info = NULL;
+ list_t *targ;
+ char *package = NULL;
+ int done = 0;
+
+ if(pmo_q_search) {
+ for(targ = targets; targ; targ = targ->next) {
+ db_search(db_local, "local", targ->data);
+ }
+ return(0);
+ }
+
+ for(targ = targets; !done; targ = (targ ? targ->next : NULL)) {
+ if(targets == NULL) {
+ done = 1;
+ } else {
+ if(targ->next == NULL) {
+ done = 1;
+ }
+ package = targ->data;
+ }
+
+ /* looking for groups */
+ if(pmo_group) {
+ PM_LIST *lp;
+ if(targets == NULL) {
+ for(lp = alpm_db_getgrpcache(db_local); lp; lp = alpm_list_next(lp)) {
+ PM_GRP *grp = alpm_list_getdata(lp);
+ PM_LIST *i, *pkgnames;
+ char *grpname;
+
+ grpname = alpm_grp_getinfo(grp, PM_GRP_NAME);
+ pkgnames = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES);
+
+ for(i = pkgnames; i; i = alpm_list_next(i)) {
+ MSG(NL, "%s %s\n", grpname, (char *)alpm_list_getdata(i));
+ }
+ }
+ } else {
+ PM_GRP *grp = alpm_db_readgrp(db_local, package);
+ if(grp) {
+ PM_LIST *i, *pkgnames = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES);
+ for(i = pkgnames; i; i = alpm_list_next(i)) {
+ MSG(NL, "%s %s\n", package, (char *)alpm_list_getdata(i));
+ }
+ } else {
+ ERR(NL, "group \"%s\" was not found\n", package);
+ return(2);
+ }
+ }
+ continue;
+ }
+
+ /* output info for a .tar.gz package */
+ if(pmo_q_isfile) {
+ if(package == NULL) {
+ ERR(NL, "no package file was specified for --file\n");
+ return(1);
+ }
+ if(alpm_pkg_load(package, &info) == -1) {
+ ERR(NL, "failed to load package '%s' (%s)\n", package, alpm_strerror(pm_errno));
+ return(1);
+ }
+ if(pmo_q_info) {
+ dump_pkg_full(info, 0);
+ MSG(NL, "\n");
+ }
+ if(pmo_q_list) {
+ dump_pkg_files(info);
+ }
+ if(!pmo_q_info && !pmo_q_list) {
+ MSG(NL, "%s %s\n", (char *)alpm_pkg_getinfo(info, PM_PKG_NAME),
+ (char *)alpm_pkg_getinfo(info, PM_PKG_VERSION));
+ }
+ FREEPKG(info);
+ continue;
+ }
+
+ /* determine the owner of a file */
+ if(pmo_q_owns) {
+ return(query_fileowner(db_local, package));
+ }
+
+ /* find packages in the db */
+ if(package == NULL) {
+ PM_LIST *lp;
+ /* no target */
+ for(lp = alpm_db_getpkgcache(db_local); lp; lp = alpm_list_next(lp)) {
+ PM_PKG *tmpp = alpm_list_getdata(lp);
+ char *pkgname, *pkgver;
+
+ pkgname = alpm_pkg_getinfo(tmpp, PM_PKG_NAME);
+ pkgver = alpm_pkg_getinfo(tmpp, PM_PKG_VERSION);
+
+ if(pmo_q_list || pmo_q_orphans) {
+ info = alpm_db_readpkg(db_local, pkgname);
+ if(info == NULL) {
+ /* something weird happened */
+ ERR(NL, "package \"%s\" not found\n", pkgname);
+ return(1);
+ }
+ if(pmo_q_list) {
+ dump_pkg_files(info);
+ }
+ if(pmo_q_orphans) {
+ if(alpm_pkg_getinfo(info, PM_PKG_REQUIREDBY) == NULL
+ && alpm_pkg_getinfo(info, PM_PKG_REASON) == PM_PKG_REASON_EXPLICIT) {
+ MSG(NL, "%s %s\n", pkgname, pkgver);
+ }
+ }
+ } else {
+ MSG(NL, "%s %s\n", pkgname, pkgver);
+ }
+ }
+ } else {
+ char *pkgname, *pkgver;
+
+ info = alpm_db_readpkg(db_local, package);
+ if(info == NULL) {
+ ERR(NL, "package \"%s\" not found\n", package);
+ return(2);
+ }
+
+ /* find a target */
+ if(pmo_q_info || pmo_q_list) {
+ if(pmo_q_info) {
+ dump_pkg_full(info, pmo_q_info);
+ }
+ if(pmo_q_list) {
+ dump_pkg_files(info);
+ }
+ } else if(pmo_q_orphans) {
+ if(alpm_pkg_getinfo(info, PM_PKG_REQUIREDBY) == NULL) {
+ MSG(NL, "%s %s\n", pkgname, pkgver);
+ }
+ } else {
+ pkgname = alpm_pkg_getinfo(info, PM_PKG_NAME);
+ pkgver = alpm_pkg_getinfo(info, PM_PKG_VERSION);
+ MSG(NL, "%s %s\n", pkgname, pkgver);
+ }
+ }
+ }
+
+ return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/query.h b/src/pacman/query.h
new file mode 100644
index 00000000..6f1143d2
--- /dev/null
+++ b/src/pacman/query.h
@@ -0,0 +1,28 @@
+/*
+ * query.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_QUERY_H
+#define _PM_QUERY_H
+
+int pacman_query(list_t *targets);
+
+#endif /* _PM_QUERY_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/remove.c b/src/pacman/remove.c
new file mode 100644
index 00000000..2f543106
--- /dev/null
+++ b/src/pacman/remove.c
@@ -0,0 +1,137 @@
+/*
+ * remove.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <alpm.h>
+/* pacman */
+#include "util.h"
+#include "list.h"
+#include "pacman.h"
+
+extern unsigned char pmo_flags;
+
+extern PM_DB *db_local;
+
+int pacman_remove(list_t *targets)
+{
+ PM_LIST *data;
+ list_t *i;
+ list_t *finaltargs = NULL;
+
+ if(targets == NULL) {
+ return(0);
+ }
+
+ /* If the target is a group, ask if its packages should be removed
+ * (the library can't remove groups for now)
+ */
+ for(i = targets; i; i = i->next) {
+ PM_GRP *grp;
+
+ grp = alpm_db_readgrp(db_local, i->data);
+ if(grp) {
+ PM_LIST *lp, *pkgnames;
+ int all;
+
+ pkgnames = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES);
+
+ MSG(NL, ":: group %s:\n", alpm_grp_getinfo(grp, PM_GRP_NAME));
+ PM_LIST_display(" ", pkgnames);
+ all = yesno(" Remove whole content? [Y/n] ");
+ for(lp = alpm_list_first(pkgnames); lp; lp = alpm_list_next(lp)) {
+ if(all || yesno(":: Remove %s from group %s? [Y/n] ", (char *)alpm_list_getdata(lp), i->data)) {
+ finaltargs = list_add(finaltargs, strdup(alpm_list_getdata(lp)));
+ }
+ }
+ } else {
+ /* not a group, so add it to the final targets */
+ finaltargs = list_add(finaltargs, strdup(i->data));
+ }
+ }
+
+ /* Step 1: create a new transaction
+ */
+ if(alpm_trans_init(PM_TRANS_TYPE_REMOVE, pmo_flags, cb_trans) == -1) {
+ ERR(NL, "failed to init transaction (%s)\n", alpm_strerror(pm_errno));
+ goto error;
+ }
+ /* and add targets to it */
+ for(i = finaltargs; i; i = i->next) {
+ if(alpm_trans_addtarget(i->data) == -1) {
+ ERR(NL, "failed to add target '%s' (%s)\n", (char *)i->data, alpm_strerror(pm_errno));
+ goto error;
+ }
+ }
+
+ /* Step 2: prepare the transaction based on its type, targets and flags
+ */
+ if(alpm_trans_prepare(&data) == -1) {
+ PM_LIST *i;
+ ERR(NL, "failed to prepare transaction (%s)\n", alpm_strerror(pm_errno));
+ switch(pm_errno) {
+ case PM_ERR_UNSATISFIED_DEPS:
+ for(i = alpm_list_first(data); i; i = alpm_list_next(i)) {
+ pmdepmissing_t *miss = alpm_list_getdata(i);
+ MSG(NL, " %s: is required by %s\n", miss->target, miss->depend.name);
+ }
+ alpm_list_free(data);
+ break;
+ default:
+ ERR(NL, "%s\n", alpm_strerror(pm_errno));
+ }
+ goto error;
+ }
+
+ /* Warn user in case of dangerous operation
+ */
+ if(pmo_flags & PM_TRANS_FLAG_RECURSE || pmo_flags & PM_TRANS_FLAG_CASCADE) {
+ /* list transaction targets */
+ PM_LIST_display("\nTargets:", alpm_trans_getinfo(PM_TRANS_TARGETS));
+ /* get confirmation */
+ if(yesno("\nDo you want to remove these packages? [Y/n] ") == 0) {
+ goto error;
+ }
+ MSG(NL, "\n");
+ }
+
+ /* Step 3: actually perform the removal
+ */
+ if(alpm_trans_commit() == -1) {
+ ERR(NL, "failed to commit transaction (%s)\n", alpm_strerror(pm_errno));
+ goto error;
+ }
+
+ /* Step 4: cleanup */
+ FREELIST(finaltargs);
+
+ return(0);
+
+error:
+ FREELIST(finaltargs);
+ alpm_trans_release();
+
+ return(1);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/remove.h b/src/pacman/remove.h
new file mode 100644
index 00000000..20595337
--- /dev/null
+++ b/src/pacman/remove.h
@@ -0,0 +1,28 @@
+/*
+ * remove.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_REMOVE_H
+#define _PM_REMOVE_H
+
+int pacman_remove(list_t *targets);
+
+#endif /* _PM_REMOVE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/sync.c b/src/pacman/sync.c
new file mode 100644
index 00000000..da89a2b9
--- /dev/null
+++ b/src/pacman/sync.c
@@ -0,0 +1,806 @@
+/*
+ * sync.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <alpm.h>
+/* pacman */
+#include "util.h"
+#include "download.h"
+#include "list.h"
+#include "package.h"
+#include "db.h"
+#include "sync.h"
+#include "pacman.h"
+
+extern char *pmo_root;
+extern char *pmo_dbpath;
+
+extern unsigned short pmo_noconfirm;
+extern unsigned short pmo_d_resolve;
+extern unsigned short pmo_q_info;
+extern unsigned short pmo_q_list;
+extern unsigned short pmo_s_upgrade;
+extern unsigned short pmo_s_downloadonly;
+extern unsigned short pmo_s_printuris;
+extern unsigned short pmo_s_sync;
+extern unsigned short pmo_s_search;
+extern unsigned short pmo_s_clean;
+extern unsigned short pmo_group;
+extern unsigned char pmo_flags;
+
+extern PM_DB *db_local;
+extern list_t *pmc_syncs;
+
+extern int maxcols;
+
+static int sync_cleancache(int level)
+{
+ if(level == 1) {
+ /* incomplete cleanup: we keep latest packages and partial downloads */
+ DIR *dir;
+ struct dirent *ent;
+ list_t *cache = NULL;
+ list_t *clean = NULL;
+ list_t *i, *j;
+
+ printf("removing old packages from cache... ");
+ dir = opendir("/var/cache/pacman/pkg");
+ if(dir == NULL) {
+ fprintf(stderr, "error: could not access cache directory\n");
+ return(1);
+ }
+ rewinddir(dir);
+ while((ent = readdir(dir)) != NULL) {
+ if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+ continue;
+ }
+ cache = list_add(cache, strdup(ent->d_name));
+ }
+ closedir(dir);
+
+ for(i = cache; i; i = i->next) {
+ char *str = i->data;
+ char name[256], version[64];
+
+ if(strstr(str, ".pkg.tar.gz") == NULL) {
+ clean = list_add(clean, strdup(str));
+ continue;
+ }
+ /* we keep partially downloaded files */
+ if(strstr(str, ".pkg.tar.gz.part")) {
+ continue;
+ }
+ if(split_pkgname(str, name, version) != 0) {
+ clean = list_add(clean, strdup(str));
+ continue;
+ }
+ for(j = i->next; j; j = j->next) {
+ char *s = j->data;
+ char n[256], v[64];
+
+ if(strstr(s, ".pkg.tar.gz") == NULL) {
+ continue;
+ }
+ if(strstr(s, ".pkg.tar.gz.part")) {
+ continue;
+ }
+ if(split_pkgname(s, n, v) != 0) {
+ continue;
+ }
+ if(!strcmp(name, n)) {
+ char *ptr = (alpm_pkg_vercmp(version, v) < 0) ? str : s;
+ if(!list_is_strin(ptr, clean)) {
+ clean = list_add(clean, strdup(ptr));
+ }
+ }
+ }
+ }
+ FREELIST(cache);
+
+ for(i = clean; i; i = i->next) {
+ char path[PATH_MAX];
+
+ snprintf(path, PATH_MAX, "%s%s/%s", pmo_root, CACHEDIR, (char *)i->data);
+ unlink(path);
+ }
+ FREELIST(clean);
+ } else {
+ /* ORE
+ // full cleanup
+ mode_t oldmask;
+ char path[PATH_MAX];
+
+ snprintf(path, PATH_MAX, "%s%s", pmo_root, CACHEDIR);
+
+ printf("removing all packages from cache... ");
+ if(rmrf(path)) {
+ fprintf(stderr, "error: could not remove cache directory\n");
+ return(1);
+ }
+
+ oldmask = umask(0000);
+ if(makepath(path)) {
+ fprintf(stderr, "error: could not create new cache directory\n");
+ return(1);
+ }
+ umask(oldmask);*/
+ }
+ printf("done.\n");
+ return(0);
+}
+
+static int sync_synctree(list_t *syncs)
+{
+ char path[PATH_MAX];
+ mode_t oldmask;
+ list_t *files = NULL;
+ list_t *i;
+ int success = 0;
+
+ for(i = syncs; i; i = i->next) {
+ sync_t *sync = (sync_t *)i->data;
+
+ /* build a one-element list */
+ snprintf(path, PATH_MAX, "%s.db.tar.gz", sync->treename);
+ files = list_add(files, strdup(path));
+
+ success = 1;
+ if(downloadfiles(sync->servers, pmo_dbpath, files)) {
+ fprintf(stderr, "failed to synchronize %s\n", sync->treename);
+ success = 0;
+ }
+
+ FREELIST(files);
+ snprintf(path, PATH_MAX, "%s/%s.db.tar.gz", pmo_dbpath, sync->treename);
+
+ if(success) {
+ char ldir[PATH_MAX];
+
+ snprintf(ldir, PATH_MAX, "%s/%s", pmo_dbpath, sync->treename);
+ /* 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);
+
+ /* uncompress the sync database */
+ vprint("Unpacking %s...\n", path);
+ if(unpack(path, ldir, NULL)) {
+ return(1);
+ }
+ }
+ /* remove the .tar.gz */
+ unlink(path);
+ }
+
+ return(!success);
+}
+
+static int sync_search(list_t *syncs, list_t *targets)
+{
+ list_t *i;
+
+ for(i = syncs; i; i = i->next) {
+ sync_t *sync = i->data;
+ if(targets) {
+ list_t *j;
+
+ for(j = targets; j; j = j->next) {
+ db_search(sync->db, sync->treename, j->data);
+ }
+ } else {
+ PM_LIST *lp;
+
+ for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) {
+ PM_PKG *pkg = alpm_list_getdata(lp);
+
+ printf("%s/%s %s\n ", sync->treename, (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME), (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION));
+ indentprint(alpm_pkg_getinfo(pkg, PM_PKG_DESC), 4);
+ printf("\n");
+ }
+ }
+ }
+
+ return(0);
+}
+
+static int sync_group(list_t *syncs, list_t *targets)
+{
+ list_t *i, *j;
+
+ if(targets) {
+ for(i = targets; i; i = i->next) {
+ for(j = syncs; j; j = j->next) {
+ sync_t *sync = j->data;
+ PM_GRP *grp = alpm_db_readgrp(sync->db, i->data);
+
+ if(grp) {
+ printf("%s/%s\n", sync->treename, (char *)alpm_grp_getinfo(grp, PM_GRP_NAME));
+ PM_LIST_display(" ", alpm_grp_getinfo(grp, PM_GRP_PKGNAMES));
+ }
+ }
+ }
+ } else {
+ for(j = syncs; j; j = j->next) {
+ sync_t *sync = j->data;
+ PM_LIST *lp;
+
+ for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) {
+ PM_GRP *grp = alpm_list_getdata(lp);
+
+ printf("%s/%s\n", (char *)sync->treename, (char *)alpm_grp_getinfo(grp, PM_GRP_NAME));
+ PM_LIST_display(" ", alpm_grp_getinfo(grp, PM_GRP_PKGNAMES));
+ }
+ }
+ }
+
+ return(0);
+}
+
+static int sync_info(list_t *syncs, list_t *targets)
+{
+ list_t *i, *j;
+
+ if(targets) {
+ for(i = targets; i; i = i->next) {
+ int found = 0;
+
+ for(j = syncs; j && !found; j = j->next) {
+ sync_t *sync = j->data;
+ PM_LIST *lp;
+
+ for(lp = alpm_db_getpkgcache(sync->db); !found && lp; lp = alpm_list_next(lp)) {
+ PM_PKG *pkg = alpm_list_getdata(lp);
+
+ if(!strcmp(alpm_pkg_getinfo(pkg, PM_PKG_NAME), i->data)) {
+ dump_pkg_sync(pkg, sync->treename);
+ printf("\n");
+ found = 1;
+ }
+ }
+ }
+ if(!found) {
+ fprintf(stderr, "Package \"%s\" was not found.\n", (char *)i->data);
+ break;
+ }
+ }
+ } else {
+ for(j = syncs; j; j = j->next) {
+ sync_t *sync = j->data;
+ PM_LIST *lp;
+
+ for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) {
+ dump_pkg_sync(alpm_list_getdata(lp), sync->treename);
+ printf("\n");
+ }
+ }
+ }
+
+ return(0);
+}
+
+static int sync_list(list_t *syncs, list_t *targets)
+{
+ list_t *i, *treenames = NULL;
+
+ if(targets) {
+ for(i = targets; i; i = i->next) {
+ list_t *j;
+ sync_t *sync = NULL;
+
+ for(j = syncs; j; j = j->next) {
+ sync_t *s = j->data;
+
+ if(strcmp(i->data, s->treename) == 0) {
+ MALLOC(sync, sizeof(sync_t));
+ sync->treename = i->data;
+ sync->db = s->db;
+ }
+ }
+
+ if(sync == NULL) {
+ fprintf(stderr, "Repository \"%s\" was not found.\n\n", (char *)i->data);
+ list_free(treenames);
+ return(1);
+ }
+
+ treenames = list_add(treenames, sync);
+ }
+ } else {
+ treenames = syncs;
+ }
+
+ for(i = treenames; i; i = i->next) {
+ PM_LIST *lp;
+ sync_t *sync = i->data;
+
+ for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) {
+ PM_PKG *pkg = alpm_list_getdata(lp);
+
+ printf("%s %s %s\n", (char *)sync->treename, (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME), (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION));
+ }
+ }
+
+ if(targets) {
+ list_free(treenames);
+ }
+
+ return(0);
+}
+
+int pacman_sync(list_t *targets)
+{
+ int allgood = 1, confirm = 0;
+ int retval = 0;
+ list_t *final = NULL;
+ list_t *i, *j;
+ PM_LIST *lp, *data;
+
+ char ldir[PATH_MAX];
+ int varcache = 1;
+ int done = 0;
+ int count = 0;
+ sync_t *current = NULL;
+ list_t *processed = NULL;
+ list_t *files = NULL;
+
+ if(pmc_syncs == NULL || !list_count(pmc_syncs)) {
+ ERR(NL, "error: no usable package repositories configured.");
+ return(1);
+ }
+
+ if(pmo_s_clean) {
+ return(sync_cleancache(pmo_s_clean));
+ }
+
+ if(pmo_s_sync) {
+ /* grab a fresh package list */
+ MSG(NL, ":: Synchronizing package databases...\n");
+ alpm_logaction("synchronizing package lists");
+ sync_synctree(pmc_syncs);
+ }
+
+ /* open the database(s) */
+ for(i = pmc_syncs; i; i = i->next) {
+ sync_t *sync = i->data;
+ if(alpm_db_register(sync->treename, &sync->db) == -1) {
+ ERR(NL, "%s\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+ }
+
+ if(pmo_s_search) {
+ return(sync_search(pmc_syncs, targets));
+ }
+
+ if(pmo_group) {
+ return(sync_group(pmc_syncs, targets));
+ }
+
+ if(pmo_q_info) {
+ return(sync_info(pmc_syncs, targets));
+ }
+
+ if(pmo_q_list) {
+ return(sync_list(pmc_syncs, targets));
+ }
+
+ if(pmo_s_upgrade) {
+ /* ORE
+ alpm_logaction(NULL, "starting full system upgrade");*/
+ if(alpm_sync_sysupgrade(&data) == -1) {
+ if(pm_errno == PM_ERR_UNRESOLVABLE_DEPS) {
+ ERR(NL, "cannot resolve dependencies\n");
+ for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) {
+ pmdepmissing_t *miss = alpm_list_getdata(lp);
+ ERR(NL, " %s: \"%s\" is not in the package set\n", miss->target, miss->depend.name);
+ }
+ alpm_list_free(data);
+ } else {
+ ERR(NL, "%s\n", alpm_strerror(pm_errno));
+ }
+ return(1);
+ }
+
+ /* check if pacman itself is one of the packages to upgrade. If so, we
+ * we should upgrade ourselves first and then re-exec as the new version.
+ *
+ * this can prevent some of the "syntax error" problems users can have
+ * when sysupgrade'ing with an older version of pacman.
+ */
+ for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) {
+ PM_SYNC *sync = alpm_list_getdata(lp);
+
+ if(!strcmp("pacman", alpm_pkg_getinfo(alpm_sync_getinfo(sync, PM_SYNC_SYNCPKG), PM_PKG_NAME))) {
+ ERR(NL, "\n:: pacman has detected a newer version of the \"pacman\" package.\n");
+ ERR(NL, ":: It is recommended that you allow pacman to upgrade itself\n");
+ ERR(NL, ":: first, then you can re-run the operation with the newer version.\n");
+ ERR(NL, "::\n");
+ if(yesno(":: Upgrade pacman first? [Y/n] ")) {
+ alpm_list_free(data);
+ data = NULL;
+ }
+ }
+ }
+
+ for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) {
+ PM_SYNC *sync = alpm_list_getdata(lp);
+ PM_PKG *lpkg, *spkg;
+ char *spkgname, *spkgver, *lpkgname, *lpkgver;
+
+ lpkg = alpm_sync_getinfo(sync, PM_SYNC_LOCALPKG);
+ lpkgname = alpm_pkg_getinfo(lpkg, PM_PKG_NAME);
+ lpkgver = alpm_pkg_getinfo(lpkg, PM_PKG_VERSION);
+
+ spkg = alpm_sync_getinfo(sync, PM_SYNC_SYNCPKG);
+ spkgname = alpm_pkg_getinfo(spkg, PM_PKG_NAME);
+ spkgver = alpm_pkg_getinfo(spkg, PM_PKG_VERSION);
+
+ switch((int)alpm_sync_getinfo(sync, PM_SYNC_TYPE)) {
+ case PM_SYSUPG_REPLACE:
+ if(yesno(":: Replace %s with %s from \"%s\"? [Y/n] ", lpkgname, spkgname, NULL/*dbs->db->treename*/)) {
+ DBG("adding '%s-%s' to replaces candidates\n", spkgname, spkgver);
+ final = list_add(final, spkg);
+ }
+
+ break;
+ case PM_SYSUPG_UPGRADE:
+ DBG("Upgrade %s (%s => %s)\n", lpkgname, lpkgver, spkgver);
+ final = list_add(final, spkg);
+ break;
+ default:
+ break;
+ }
+ }
+ alpm_list_free(data);
+ } else {
+ /* process targets */
+ for(i = targets; i; i = i->next) {
+ char *treename;
+ char *targ;
+ char *targline;
+ PM_PKG *local = NULL;
+
+ targline = strdup((char *)i->data);
+ targ = index(targline, '/');
+ if(targ) {
+ *targ = '\0';
+ targ++;
+ treename = targline;
+ } else {
+ targ = targline;
+ treename = NULL;
+ }
+
+ if(treename == NULL) {
+ for(j = pmc_syncs; j && !local; j = j->next) {
+ sync_t *sync = j->data;
+ local = alpm_db_readpkg(sync->db, targ);
+ }
+ } else {
+ for(j = pmc_syncs; j && !local; j = j->next) {
+ sync_t *sync = j->data;
+ if(strcmp(sync->treename, treename) == 0) {
+ local = alpm_db_readpkg(sync->db, targ);
+ }
+ }
+ }
+
+ if(local == NULL) {
+ PM_GRP *grp = NULL;
+ /* target not found: check if it's a group */
+ for(j = pmc_syncs; j && !grp; j = j->next) {
+ sync_t *sync = j->data;
+ grp = alpm_db_readgrp(sync->db, targ);
+ if(grp) {
+ PM_LIST *k, *pkgs;
+ MSG(NL, ":: group %s:\n", targ);
+
+ pkgs = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES);
+ PM_LIST_display(" ", pkgs);
+
+ if(yesno(" Install whole content? [Y/n] ")) {
+ for(k = alpm_list_first(pkgs); k; k = alpm_list_next(k)) {
+ targets = list_add(targets, strdup(alpm_list_getdata(k)));
+ }
+ } else {
+ for(k = alpm_list_first(pkgs); k; k = alpm_list_next(k)) {
+ char *pkgname = alpm_list_getdata(k);
+ if(yesno(":: Install %s from group %s? [Y/n] ", pkgname, targ)) {
+ targets = list_add(targets, strdup(pkgname));
+ }
+ }
+ }
+ }
+ }
+ if(grp == NULL) {
+ ERR(NL, "package \"%s\" not found", targ);
+ return(1);
+ }
+ }
+ if(treename) {
+ FREE(targline);
+ }
+
+ }
+
+ if(!pmo_s_downloadonly && !pmo_s_printuris) {
+ /* this is an upgrade, compare versions and determine if it is necessary */
+ for(i = targets; i; i = i->next) {
+ int cmp;
+ PM_PKG *local, *sync;
+ char *lpkgname, *lpkgver, *spkgver;
+
+ local = alpm_db_readpkg(db_local, i->data);
+ lpkgname = alpm_pkg_getinfo(local, PM_PKG_NAME);
+ lpkgver = alpm_pkg_getinfo(local, PM_PKG_VERSION);
+
+ sync = alpm_db_readpkg(db_local, i->data);
+ spkgver = alpm_pkg_getinfo(sync, PM_PKG_VERSION);
+
+ cmp = alpm_pkg_vercmp(lpkgver, spkgver);
+ if(cmp > 0) {
+ /* local version is newer - get confirmation first */
+ if(!yesno(":: %s-%s: local version is newer. Upgrade anyway? [Y/n] ", lpkgname, lpkgver)) {
+ /* ORE
+ char *data = list_remove(targets, lpkgname);
+ free(data);*/
+ }
+ } else if(cmp == 0) {
+ /* versions are identical */
+ if(!yesno(":: %s-%s: is up to date. Upgrade anyway? [Y/n] ", lpkgname, lpkgver)) {
+ /* ORE
+ char *data = list_remove(targets, lpkgname);
+ free(data);*/
+ }
+ }
+ }
+ }
+ }
+
+ /* Step 1: create a new transaction */
+ if(alpm_trans_init(PM_TRANS_TYPE_SYNC, pmo_flags, NULL) == -1) {
+ ERR(NL, "failed to init transaction (%s)\n", alpm_strerror(pm_errno));
+ retval = 1;
+ goto cleanup;
+ }
+ /* and add targets to it */
+ for(i = targets; i; i = i->next) {
+ if(alpm_trans_addtarget(i->data) == -1) {
+ ERR(NL, "failed to add target '%s' (%s)\n", (char *)i->data, alpm_strerror(pm_errno));
+ retval = 1;
+ goto cleanup;
+ }
+ }
+
+ PM_LIST_display("target :", alpm_trans_getinfo(PM_TRANS_TARGETS));
+ /* ORE
+ TBD */
+
+ /* Step 2: "compute" the transaction based on targets and flags */
+ if(alpm_trans_prepare(&data) == -1) {
+ ERR(NL, "failed to prepare transaction (%s)\n", alpm_strerror(pm_errno));
+ return(1);
+ }
+
+ /* list targets */
+ if(final && !pmo_s_printuris) {
+ list_t *list = NULL;
+ char *str;
+ unsigned long totalsize = 0;
+ double mb;
+ /* ORE
+ for(i = rmtargs; i; i = i->next) {
+ list = list_add(list, strdup(i->data));
+ }
+ for(i = final; i; i = i->next) {
+ syncpkg_t *s = (syncpkg_t*)i->data;
+ for(j = s->replaces; j; j = j->next) {
+ pkginfo_t *p = (pkginfo_t*)j->data;
+ list = list_add(list, strdup(p->name));
+ }
+ }
+ if(list) {
+ printf("\nRemove: ");
+ str = buildstring(list);
+ indentprint(str, 9);
+ printf("\n");
+ FREELIST(list);
+ FREE(str);
+ }*/
+ /* ORE
+ for(i = final; i; i = i->next) {
+ MALLOC(str, strlen(s->pkg->name)+strlen(s->pkg->version)+2);
+ sprintf(str, "%s-%s", s->pkg->name, s->pkg->version);
+ list = list_add(list, str);
+ totalsize += s->pkg->size;
+ }*/
+ mb = (double)(totalsize / 1048576.0);
+ /* round up to 0.1 */
+ if(mb < 0.1) {
+ mb = 0.1;
+ }
+ printf("\nTargets: ");
+ str = buildstring(list);
+ indentprint(str, 9);
+ printf("\n\nTotal Package Size: %.1f MB\n", mb);
+ FREELIST(list);
+ FREE(str);
+ }
+
+ /* get confirmation */
+ if(pmo_s_downloadonly) {
+ if(pmo_noconfirm) {
+ MSG(NL, "\nBeginning download...\n");
+ confirm = 1;
+ } else {
+ confirm = yesno("\nProceed with download? [Y/n] ");
+ }
+ } else {
+ /* don't get any confirmation if we're called from makepkg */
+ if(pmo_d_resolve || pmo_s_printuris) {
+ confirm = 1;
+ } else {
+ if(pmo_noconfirm) {
+ MSG(NL, "\nBeginning upgrade process...\n");
+ confirm = 1;
+ } else {
+ confirm = yesno("\nProceed with upgrade? [Y/n] ");
+ }
+ }
+ }
+ if(!confirm) {
+ retval = 1;
+ goto cleanup;
+ }
+
+ /* ORE
+ group sync records by repository and download */
+
+ snprintf(ldir, PATH_MAX, "%svar/cache/pacman/pkg", pmo_root);
+
+ while(!done) {
+ if(current) {
+ processed = list_add(processed, current);
+ current = NULL;
+ }
+ for(i = final; i; i = i->next) {
+ if(current == NULL) {
+ /* we're starting on a new repository */
+ }
+ /*if(current && !strcmp(current->treename, sync->dbs->sync->treename)) {
+ }*/
+ }
+
+ if(files) {
+ if(pmo_s_printuris) {
+ server_t *server = (server_t*)current->servers->data;
+ for(j = files; j; j = j->next) {
+ if(!strcmp(server->protocol, "file")) {
+ MSG(NL, "%s://%s%s\n", server->protocol, server->path,
+ (char *)j->data);
+ } else {
+ MSG(NL, "%s://%s%s%s\n", server->protocol,
+ server->server, server->path, (char *)j->data);
+ }
+ }
+ } else {
+ struct stat buf;
+
+ MSG(NL, "\n:: Retrieving packages from %s...\n", current->treename);
+ fflush(stdout);
+ if(stat(ldir, &buf)) {
+ mode_t oldmask;
+
+ /* no cache directory.... try creating it */
+ /* ORE
+ * alpm_logaction(stderr, "warning: no %s cache exists. creating...", ldir);*/
+ oldmask = umask(0000);
+ if(makepath(ldir)) {
+ /* couldn't mkdir the cache directory, so fall back to /tmp and unlink
+ * the package afterwards.
+ */
+ /* ORE
+ * logaction(stderr, "warning: couldn't create package cache, using /tmp instead");*/
+ snprintf(ldir, PATH_MAX, "/tmp");
+ varcache = 0;
+ }
+ umask(oldmask);
+ }
+ if(downloadfiles(current->servers, ldir, files)) {
+ ERR(NL, "failed to retrieve some files from %s\n", current->treename);
+ retval = 1;
+ goto cleanup;
+ }
+ }
+
+ count += list_count(files);
+ FREELIST(files);
+ }
+ if(count == list_count(final)) {
+ done = 1;
+ }
+ }
+ printf("\n");
+
+ /* Check integrity of files */
+ MSG(NL, "checking package integrity... ");
+
+ allgood = 1;
+ for(i = final; i; i = i->next) {
+ char /*str[PATH_MAX],*/ pkgname[PATH_MAX];
+ char *md5sum1, *md5sum2;
+
+ snprintf(pkgname, PATH_MAX, "%s-%s.pkg.tar.gz", "", "");
+
+ md5sum1 = NULL;
+ md5sum2 = NULL;
+
+ if(strcmp(md5sum1, md5sum2) != 0) {
+ if(allgood) {
+ printf("\n");
+ }
+ ERR(NL, "error: archive %s is corrupted\n", "");
+ allgood = 0;
+ }
+
+ FREE(md5sum2);
+
+ }
+ if(!allgood) {
+ retval = 1;
+ goto cleanup;
+ }
+ MSG(CL, "done.\n");
+
+ /* Step 3: actually perform the installation */
+ if(!pmo_s_downloadonly) {
+ if(alpm_trans_commit(&data) == -1) {
+ ERR(NL, "failed to commit transaction (%s)\n", alpm_strerror(pm_errno));
+ retval = 1;
+ goto cleanup;
+ }
+ }
+
+ if(!varcache && !pmo_s_downloadonly) {
+ /* delete packages */
+ for(i = files; i; i = i->next) {
+ unlink(i->data);
+ }
+ }
+
+cleanup:
+
+ return(retval);
+
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/sync.h b/src/pacman/sync.h
new file mode 100644
index 00000000..7780caa1
--- /dev/null
+++ b/src/pacman/sync.h
@@ -0,0 +1,35 @@
+/*
+ * sync.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_SYNC_H
+#define _PM_SYNC_H
+
+/* Repositories */
+typedef struct __sync_t {
+ char *treename;
+ PM_DB *db;
+ list_t *servers; /* List of server_t */
+} sync_t;
+
+int pacman_sync(list_t *targets);
+
+#endif /* _PM_SYNC_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c
new file mode 100644
index 00000000..9149ee69
--- /dev/null
+++ b/src/pacman/upgrade.c
@@ -0,0 +1,42 @@
+/*
+ * upgrade.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <alpm.h>
+/* pacman */
+#include "list.h"
+#include "add.h"
+
+extern unsigned char pmo_upgrade;
+
+int pacman_upgrade(list_t *targets)
+{
+ /* this is basically just a remove-then-add process. pacman_add() will */
+ /* handle it */
+ pmo_upgrade = 1;
+ return(pacman_add(targets));
+}
+
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/upgrade.h b/src/pacman/upgrade.h
new file mode 100644
index 00000000..4eeac4ed
--- /dev/null
+++ b/src/pacman/upgrade.h
@@ -0,0 +1,28 @@
+/*
+ * upgrade.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_UPGRADE_H
+#define _PM_UPGRADE_H
+
+int pacman_upgrade(list_t *targets);
+
+#endif /* _PM_UPGRADE_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/util.c b/src/pacman/util.c
new file mode 100644
index 00000000..56c3349b
--- /dev/null
+++ b/src/pacman/util.c
@@ -0,0 +1,297 @@
+/*
+ * util.c
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <zlib.h>
+#include <libtar.h>
+
+/* pacman */
+#include "util.h"
+
+/* borrowed and modified from Per Liden's pkgutils (http://crux.nu) */
+long gzopen_frontend(char *pathname, int oflags, int mode)
+{
+ char *gzoflags;
+ int fd;
+ gzFile gzf;
+
+ switch (oflags & O_ACCMODE) {
+ case O_WRONLY:
+ gzoflags = "w";
+ break;
+ case O_RDONLY:
+ gzoflags = "r";
+ break;
+ case O_RDWR:
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if((fd = open(pathname, oflags, mode)) == -1) {
+ return -1;
+ }
+ if((oflags & O_CREAT) && fchmod(fd, mode)) {
+ return -1;
+ }
+ if(!(gzf = gzdopen(fd, gzoflags))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return (long)gzf;
+}
+
+int unpack(char *archive, const char *prefix, const char *fn)
+{
+ TAR *tar = NULL;
+ char expath[PATH_MAX];
+ tartype_t gztype = {
+ (openfunc_t) gzopen_frontend,
+ (closefunc_t)gzclose,
+ (readfunc_t) gzread,
+ (writefunc_t)gzwrite
+ };
+
+ /* open the .tar.gz package */
+ if(tar_open(&tar, archive, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
+ perror(archive);
+ return(1);
+ }
+ while(!th_read(tar)) {
+ if(fn && strcmp(fn, th_get_pathname(tar))) {
+ if(TH_ISREG(tar) && tar_skip_regfile(tar)) {
+ char errorstr[255];
+ snprintf(errorstr, 255, "bad tar archive: %s", archive);
+ perror(errorstr);
+ tar_close(tar);
+ return(1);
+ }
+ continue;
+ }
+ snprintf(expath, PATH_MAX, "%s/%s", prefix, th_get_pathname(tar));
+ if(tar_extract_file(tar, expath)) {
+ fprintf(stderr, "could not extract %s: %s\n", th_get_pathname(tar), strerror(errno));
+ }
+ if(fn) break;
+ }
+ tar_close(tar);
+
+ return(0);
+}
+
+/* does the same thing as 'mkdir -p' */
+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);
+ umask(oldmask);
+ return(1);
+ }
+ }
+ }
+ }
+ free(orig);
+ umask(oldmask);
+ return(0);
+}
+
+/* does the same thing as 'rm -rf' */
+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);
+}
+
+/* output a string, but wrap words properly with a specified indentation
+ */
+void indentprint(char *str, int indent)
+{
+ char *p = str;
+ char *cenv = NULL;
+ int cols = 80;
+ int cidx = indent;
+
+ cenv = getenv("COLUMNS");
+ if(cenv) {
+ cols = atoi(cenv);
+ }
+
+ while(*p) {
+ if(*p == ' ') {
+ char *next = NULL;
+ int len;
+ p++;
+ if(p == NULL || *p == ' ') continue;
+ next = strchr(p, ' ');
+ if(next == NULL) {
+ next = p + strlen(p);
+ }
+ len = next - p;
+ if(len > (cols-cidx-1)) {
+ /* newline */
+ int i;
+ fprintf(stdout, "\n");
+ for(i = 0; i < indent; i++) {
+ fprintf(stdout, " ");
+ }
+ cidx = indent;
+ } else {
+ printf(" ");
+ cidx++;
+ }
+ }
+ fprintf(stdout, "%c", *p);
+ p++;
+ cidx++;
+ }
+}
+
+/* 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 *strtrim(char *str)
+{
+ char *pch = str;
+ while(isspace(*pch)) {
+ pch++;
+ }
+ if(pch != str) {
+ memmove(str, pch, (strlen(pch) + 1));
+ }
+
+ pch = (char *)(str + (strlen(str) - 1));
+ while(isspace(*pch)) {
+ pch--;
+ }
+ *++pch = '\0';
+
+ return str;
+}
+
+/* presents a prompt and gets a Y/N answer
+ */
+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 whitespace and newlines */
+ char *pch = response;
+ while(isspace(*pch)) {
+ pch++;
+ }
+ if(pch != response) {
+ memmove(response, pch, strlen(pch) + 1);
+ }
+ pch = response + strlen(response) - 1;
+ while(isspace(*pch)) {
+ pch--;
+ }
+ *++pch = 0;
+ strtrim(response);
+
+ if(!strcasecmp(response, "Y") || !strcasecmp(response, "YES") || !strlen(response)) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/util.h b/src/pacman/util.h
new file mode 100644
index 00000000..15ce8043
--- /dev/null
+++ b/src/pacman/util.h
@@ -0,0 +1,42 @@
+/*
+ * util.h
+ *
+ * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef _PM_UTIL_H
+#define _PM_UTIL_H
+
+#define MALLOC(p, b) do { if((b) > 0) { \
+ p = malloc(b); if (!(p)) { \
+ fprintf(stderr, "malloc failure: could not allocate %d bytes\n", b); \
+ exit(1); }} else p = NULL; } while(0)
+
+#define FREE(p) do { if (p) { free(p); (p)= NULL; }} while(0)
+
+long gzopen_frontend(char *pathname, int oflags, int mode);
+int unpack(char *archive, const char *prefix, const char *fn);
+int makepath(char *path);
+int rmrf(char *path);
+void indentprint(char *str, int indent);
+char *strtrim(char *str);
+char *strtoupper(char *str);
+int yesno(char *fmt, ...);
+
+#endif /* _PM_UTIL_H */
+
+/* vim: set ts=2 sw=2 noet: */