diff options
author | Judd Vinet <judd@archlinux.org> | 2005-03-15 01:51:43 +0000 |
---|---|---|
committer | Judd Vinet <judd@archlinux.org> | 2005-03-15 01:51:43 +0000 |
commit | d04baabafa2ebbad92741d1f87e6ff32999f894a (patch) | |
tree | 5a2280176812b80c28ca77bfa8e0655c16f4db7e /src/pacman |
Initial revision
Diffstat (limited to 'src/pacman')
-rw-r--r-- | src/pacman/Makefile | 39 | ||||
-rw-r--r-- | src/pacman/add.c | 130 | ||||
-rw-r--r-- | src/pacman/add.h | 28 | ||||
-rw-r--r-- | src/pacman/conf.c | 308 | ||||
-rw-r--r-- | src/pacman/conf.h | 28 | ||||
-rw-r--r-- | src/pacman/db.c | 98 | ||||
-rw-r--r-- | src/pacman/db.h | 28 | ||||
-rw-r--r-- | src/pacman/download.c | 467 | ||||
-rw-r--r-- | src/pacman/download.h | 38 | ||||
-rw-r--r-- | src/pacman/list.c | 176 | ||||
-rw-r--r-- | src/pacman/list.h | 45 | ||||
-rw-r--r-- | src/pacman/package.c | 203 | ||||
-rw-r--r-- | src/pacman/package.h | 35 | ||||
-rw-r--r-- | src/pacman/pacman.c | 688 | ||||
-rw-r--r-- | src/pacman/pacman.h | 75 | ||||
-rw-r--r-- | src/pacman/query.c | 247 | ||||
-rw-r--r-- | src/pacman/query.h | 28 | ||||
-rw-r--r-- | src/pacman/remove.c | 137 | ||||
-rw-r--r-- | src/pacman/remove.h | 28 | ||||
-rw-r--r-- | src/pacman/sync.c | 806 | ||||
-rw-r--r-- | src/pacman/sync.h | 35 | ||||
-rw-r--r-- | src/pacman/upgrade.c | 42 | ||||
-rw-r--r-- | src/pacman/upgrade.h | 28 | ||||
-rw-r--r-- | src/pacman/util.c | 297 | ||||
-rw-r--r-- | src/pacman/util.h | 42 |
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: */ |