diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/Config.in busybox/archival/Config.in --- busybox-1.00/archival/Config.in 2004-03-15 09:28:16.000000000 +0100 +++ busybox/archival/Config.in 2005-03-29 13:45:03.000000000 +0200 @@ -97,6 +97,14 @@ However it saves space as none of the extra dpkg-deb, ar or tar options are needed, they are linked to internally. +config CONFIG_EMERGE + bool "emerge workalike for gentoo packages" + default n + depends on CONFIG_WGET && CONFIG_TBZ2PKG + help + This allows busybox to fetch and install binary packages + from the gentoo portage system. + config CONFIG_GUNZIP bool "gunzip" default n @@ -205,6 +213,13 @@ help Enable use of long options, increases size by about 400 Bytes +config CONFIG_TBZ2PKG + bool "tbz2pkg" + default n + depends on CONFIG_FEATURE_TAR_BZIP2 + help + Install and uninstall gentoo binary packages + config CONFIG_UNCOMPRESS bool "uncompress" default n diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/Makefile.in busybox/archival/Makefile.in --- busybox-1.00/archival/Makefile.in 2004-10-08 09:45:09.000000000 +0200 +++ busybox/archival/Makefile.in 2005-04-02 18:02:27.000000000 +0200 @@ -30,11 +30,13 @@ ARCHIVAL-$(CONFIG_CPIO) += cpio.o ARCHIVAL-$(CONFIG_DPKG) += dpkg.o ARCHIVAL-$(CONFIG_DPKG_DEB) += dpkg_deb.o +ARCHIVAL-$(CONFIG_EMERGE) += emerge.o gentoo_shared.o ARCHIVAL-$(CONFIG_GUNZIP) += gunzip.o ARCHIVAL-$(CONFIG_GZIP) += gzip.o ARCHIVAL-$(CONFIG_RPM2CPIO) += rpm2cpio.o ARCHIVAL-$(CONFIG_RPM) += rpm.o ARCHIVAL-$(CONFIG_TAR) += tar.o +ARCHIVAL-$(CONFIG_TBZ2PKG) += tbz2pkg.o gentoo_shared.o ARCHIVAL-$(CONFIG_UNCOMPRESS) += uncompress.o ARCHIVAL-$(CONFIG_UNZIP) += unzip.o diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/emerge.c busybox/archival/emerge.c --- busybox-1.00/archival/emerge.c 1970-01-01 01:00:00.000000000 +0100 +++ busybox/archival/emerge.c 2005-06-08 20:17:34.000000000 +0200 @@ -0,0 +1,768 @@ +/* +* Distributed under the terms of the GNU General Public License v2 +* $Header: /home/collar_b/programs/cvs/busybox/archival/emerge.c,v 1.27 2005/06/08 18:17:34 collar_b Exp $ +* +* emerge: a gentoo-emerge workalike for embedded systems, integrated +* into the busybox suite +* +* Written by Benjamin Collar +* Copyright (C) 2005, Benjamin Collar +* +******************************************************************** +* 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. +*/ + +/* note about source code organization */ +/* this file is organized like this: + * structs & other declarations + * generally useful stuff + * functions that start with do_ relate directly to an "action", ie a command + * helper functions for the do_ start with the _ + * functions that start with parse_ are used for processing the index file + * other things should hopefully be clear :) + */ + +#include "busybox.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tbz2pkg.h" + +#define file_exists(f) (access(f, R_OK) == 0) +#define dir_exists(f) (access(f, R_OK) == 0) + + +typedef struct pkg_info_s { + llist_t *packages; + package_t** searchable_packages; /* sorted pointers to package names; use bsearch to find them */ + int num_packages; /* length of searchable_packages array */ +} pkg_info_t; + +extern int wget_main(int argc, char **argv); +configurables_t conf; +int (*action) (const package_t*);/* this is the action we'll take, after handling cmdline, etc */ +static pkg_info_t pkg_info; + +/* some useful functions */ +static int +mkalldirs(char *dir) +{ + return bb_make_directory(dir, -1, FILEUTILS_RECUR); +} + +static package_t* +find_in_index (package_t* pkg) +{ + void* result; + result = bsearch((void*) pkg, (const void*) pkg_info.searchable_packages, + pkg_info.num_packages, sizeof(package_t*), bfind_compare); + if (result) { + return (*(package_t**)result); + } + return 0; +} + +static int +fetch_package(package_t* package) +{ + /* using the package in package-name (which is category/package), figure + * out the actual package name and URL. Then fetch it. return 0 when it works. + */ + /* append the category/package to the binhost to create the url */ + + static char fullurl[GEM_PATH_MAX]; + static char target[GEM_PATH_MAX]; + static char pkgver[GEM_NAME_LEN*2+2]; + sprintf(pkgver, "%s-%s.tbz2", package->name, package->version); + if (!conf.pretend) { + sprintf(msg, "Fetching package %s/%s", package->category, pkgver); + einfo(msg); + } else { + sprintf(msg, "Would fetch %s/%s", package->category, pkgver); + einfo(msg); + return 0; + } + sprintf(target, "%s/All/", conf.pkgdir); + /* make sure the package download directory exists */ + if ( mkalldirs(target) != 0 ) + edie("Failed to create package download directory"); + /* check if package already exists; if so, return ok */ + strcat(target, pkgver); + if ( file_exists(target) ) { + sprintf(msg, "Package already fetched: %s", target); + einfo(msg); + /* further processing expects msg to contain path to downloaded file */ + /* REFACTOR THIS */ + strcpy(msg, target); + return 0; + } + sprintf(target, "%s/All/", conf.pkgdir); + /* is it correct that the package file will be in http://binhost/category/abc-1.2.3.tbz2 ? */ + sprintf(fullurl, "%s/%s/%s", conf.binhost, package->category, pkgver); + { + char *cmdline[] = { "wget", "-c", "-P", target, fullurl, 0 }; + pid_t child=fork(); + if ( child == 0 ) { + execvp("/proc/self/exe", cmdline); + } else { + int status, wgrval; + wait(&status); + if (WIFEXITED(status)) { + wgrval = WEXITSTATUS(status); + if (wgrval != 0) { + edie("Failed to fetch package"); + } else { + strcat(target, pkgver); + sprintf(msg, "Package saved to %s", target); + einfo(msg); + } + sprintf(msg, "%s/%s/%s", conf.pkgdir, package->category, pkgver); + if (symlink(target, msg) != 0 && errno != EEXIST) { + edie("Failed to symlink package into correct subdirectory"); + } + /* save targetdir into msg; another function will use it */ + strcpy(msg, target); + return 0; + } + } + } + return 1; +} + +static int +do_fetch(const package_t * user_pkg) +{ + llist_t* list; + package_t *dep_pkg, *index_pkg; + int rval=0; + index_pkg = user_pkg->index_pkg; + if (!index_pkg) { + eerror("Package not found in index, cannot fetch!"); + return 1; + } + list = index_pkg->depends; + while (list) { + dep_pkg = decode_namespec((char*) list->data); + dep_pkg->index_pkg = find_in_index(dep_pkg); + if (!dep_pkg->index_pkg) { + sprintf(msg, "Dependency %s for %s not found in index", + dep_pkg->name, user_pkg->name); + eerror(msg); + rval = 1; + } else { + rval = rval ? rval : do_fetch(dep_pkg); + } + free(dep_pkg); + list = list->link; + } + if (conf.pretend) { + sprintf(msg, "Would fetch package %s\n", user_pkg->name); + einfo(msg); + return 0; + } + return rval ? rval : fetch_package(index_pkg); +} + +static void +do_info(void) +{ + bb_printf("PKGDIR=%s\n", (conf.pkgdir == NULL) ? "" : conf.pkgdir); + bb_printf("PKG_DBDIR=%s\n", (conf.pkg_dbdir == NULL) ? "" : conf.pkg_dbdir); + bb_printf("PORTAGE_BINHOST=%s\n", + (conf.binhost == NULL) ? "" : conf.binhost); + bb_printf("PORTAGE_TMPDIR=%s\n", + (conf.tmpdir == NULL) ? "" : conf.tmpdir); + bb_printf("ROOT=%s\n", (conf.root == NULL) ? "" : conf.root); + bb_printf("INDEX_FILE=%s\n", (conf.index == NULL) ? "" : conf.index); +} + +static int do_merge(const package_t* p); + +static int +merge_should_install(package_t* p) +{ + static package_t tmppkg; + int rval=0; + llist_t *installed, *t; + memcpy(&tmppkg, p, sizeof(package_t)); + tmppkg.modifier[0]='>'; + tmppkg.modifier[1]='='; + installed = find_package_on_disk(&tmppkg); + rval = installed == 0; + while (installed) { + t = installed->link; + free(installed); + installed = t; + } + return rval; +} + +static int +merge_deps(package_t* p) +{ + llist_t* list = p->depends; + package_t* dep_pkg=0; + int rval = 0; + sprintf(msg, "Merging dependencies of %s", p->name); + einfo(msg); + if (conf.debug) { print_pkg(p); } + while (list) { + dep_pkg = decode_namespec((char*) list->data); + if (strcmp(dep_pkg->category, "virtual") == 0) { + rval = rval ? 1 : 0; + sprintf(msg, "Can't do virtual dependencies yet. Please make sure %s/%s is installed", + dep_pkg->category, dep_pkg->name); + eerror(msg); + } else { + dep_pkg->index_pkg = find_in_index(dep_pkg); + if (!dep_pkg->index_pkg) { + rval = 1; + } else { + rval = rval ? 1 : do_merge(dep_pkg); + } + } + free(dep_pkg); + list = list->link; + } + einfo("Done merging dependencies"); + return rval; +} + +static int +do_merge(const package_t* user_pkg) +{ + package_t* index_pkg = user_pkg->index_pkg; + sprintf(msg, "Merging package %s", user_pkg->name); + einfo(msg); + if (!index_pkg) { + eerror("Package not found in index"); + return 1; + } + if (!merge_should_install(index_pkg)) { + sprintf(msg, "Package %s already up-to-date", user_pkg->name); + einfo(msg); + return 0; + } + if (merge_deps(index_pkg) != 0) { eerror("Failed to merge dependencies"); return 1; } + if (fetch_package(index_pkg) != 0) { eerror("Failed to fetch package"); return 1; } + + /* fetch puts the filename into msg, so give that to tbz2 */ + if (conf.pretend) { + sprintf(msg, "Would merge %s", user_pkg->name); + einfo(msg); + return 0; + } + if (tbz2_install_file(msg, &conf) <= 0) { eerror("Failed to install package"); return 1; } + sprintf(msg, "Package %s merged successfully", user_pkg->name); + einfo(msg); + return 0; +} + +static int +do_unmerge(const package_t* user_pkg) +{ + package_t* index_pkg = user_pkg->index_pkg, *installed_pkg=0; + int rval=0, should_free=0; + llist_t* installed_pkgs, *l; + if (!index_pkg) { + index_pkg = xmalloc(sizeof(package_t)); + memcpy(index_pkg, user_pkg, sizeof(package_t)); + sprintf(msg, "Unable to find package %s in index, continuing...", user_pkg->name); + should_free=1; + } + if (conf.pretend) { + sprintf(msg, "Would unmerge %s", user_pkg->name); + einfo(msg); + rval = 0; + } + installed_pkgs = find_package_on_disk(index_pkg); + if (!installed_pkgs) { + einfo("Nothing to unmerge"); + } + while (installed_pkgs) { + installed_pkg = (package_t*) installed_pkgs->data; + if (conf.pretend) { + sprintf(msg, "Would unmerge %s-%s", installed_pkg->name, + installed_pkg->version); + einfo(msg); + } else { + if (tbz2_unmerge(installed_pkg) <= 0) rval = 1; + if (rval==0) { + einfo("Unmerge successful"); + } else { + eerror("Unmerge failed!"); + } + } + + l = installed_pkgs->link; + free(installed_pkgs->data); + installed_pkgs = l; + } + if (should_free) + free(index_pkg); + return rval; +} + +static int +do_query(const package_t* user_pkg) +{ + einfo("Package query result\nQuery--"); + print_pkg(user_pkg); + if (!user_pkg->index_pkg) { + sprintf(msg, "Package not found in index: %s, continuing", user_pkg->name); + einfo(msg); + } else { + einfo("Found package in index"); + print_pkg(user_pkg->index_pkg); + } + { + llist_t *r, *l; + einfo("Checking for installed version"); + r = find_package_on_disk(user_pkg); + if (!r) { einfo("Not found"); } + while(r) { + einfo("Found installed package"); + print_pkg((package_t*)r->data); + l = r->link; + free(r->data); + r = l; + } + } + return 0; +} + +static int +do_sync(const package_t* user_pkg) +{ + eerror("Unable to sync"); + return 1; +} + +static int +do_list(const package_t* user_pkg) +{ + llist_t* r = find_package_on_disk(user_pkg), *l; + while (r) { + tbz2_list((package_t*)r->data); + l = r->link; + free(r->data); + r = l; + } + return 0; +} + +static int +do_update(const package_t* user_pkg) +{ + eerror("Unable to update"); + return 1; +} + +static int +do_clean(const package_t* user_pkg) +{ + eerror("Unable to clean"); + return 1; +} + + +/* parseargs and similar ilk */ +/* busybox has their own funny getopt structure, which depends on bits... */ +#define EMERGE_HELP (1<<0) +#define EMERGE_DEBUG (1<<1) +#define EMERGE_PRETEND (1<<2) +#define EMERGE_VERBOSE (1<<3) +#define EMERGE_FETCH (1<<4) +#define EMERGE_USEPKGONLY (1<<5) +#define EMERGE_UNMERGE (1<<6) +#define EMERGE_QUERY (1<<7) +#define EMERGE_INFO (1<<8) +#define EMERGE_SYNC (1<<9) +#define EMERGE_LIST (1<<10) +#define EMERGE_UPDATE (1<<11) +#define EMERGE_CLEAN (1<<12) + +static struct option const emerge_long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 0, 0, 'd'}, + {"pretend", 0, 0, 'p'}, + {"verbose", 0, 0, 'v'}, + {"fetch", 0, 0, 'f'}, + {"usepkgonly", 0, 0, 'K'}, + {"unmerge", 0, 0, 'C'}, + {"query", 0, 0, 'q'}, + {"info", 0, 0, 'i'}, + {"sync", 0, 0, 's'}, + {"list", 0, 0, 'l'}, + {"update", 0, 0, 'u'}, + {"clean", 0, 0, 'c'}, + {"root", 1, 0, 'R'}, + {"index", 1, 0, 'I'}, + {"pkgdir", 1, 0, 'P'}, + {"tmpdir", 1, 0, 'T'}, + {"binhost", 1, 0, 'B'}, + {NULL, 0, NULL, 0} +}; + +#define OPTIONS_FLAGS "hdpvfKCqislucR:I:P:T:B:" +static void +parseargs(int argc, char *argv[]) +{ + int optend; + unsigned long opt; + char *root=0, *pkgdir=0, *tmpdir=0, *binhost=0, *cindex=0; + package_t* tmppkg; + opterr = 0; + /* this complementaly only works if a conflict is specified in both directions, + thus f~q:q~f will block f & q from being specified together. that seems + pretty wrong to me */ + bb_opt_complementaly = "f~Cqisluc:i~pfKCqsluc:q~f"; + bb_applet_long_options = emerge_long_options; + + /* need to allocate these flags, not let bb do it! */ + /* need to allocate!! also, conf.index processing seems broken */ + root = pkgdir = tmpdir = binhost = 0; + opt = bb_getopt_ulflags(argc, argv, OPTIONS_FLAGS, + &root, &cindex, &pkgdir, &tmpdir, &binhost); + if (root != NULL) { + conf.root = xmalloc(strnlen(root, GEM_PATH_MAX)); + strncpy(conf.root, root, GEM_PATH_MAX); + } + if (pkgdir != NULL) { + conf.pkgdir = xmalloc(strnlen(pkgdir, GEM_PATH_MAX)); + strncpy(conf.pkgdir, pkgdir, GEM_PATH_MAX); + } + if (tmpdir != NULL) { + conf.tmpdir = xmalloc(strnlen(tmpdir, GEM_PATH_MAX)); + strncpy(conf.tmpdir, tmpdir, GEM_PATH_MAX); + } + if (binhost != NULL) { + conf.binhost = xmalloc(strnlen(binhost, GEM_NAME_LEN)); + strncpy(conf.binhost, binhost, GEM_NAME_LEN); + } + if (cindex != NULL) { + conf.index = xmalloc(strnlen(cindex, GEM_PATH_MAX)); + strncpy(conf.index, cindex, GEM_PATH_MAX); + } + + /* Check one and only one context option was given */ + /* this doesn't work... */ + if (opt & 0x80000000UL) { + bb_printf("conflict!\n"); + bb_show_usage(); + } + + if (opt & EMERGE_HELP) + bb_show_usage(); + + /* runtime-configuration */ + if (opt & EMERGE_DEBUG) + conf.debug = 1; + if (opt & EMERGE_PRETEND) + conf.pretend = 1; + if (opt & EMERGE_VERBOSE) + conf.verbose = 1; + if (opt & EMERGE_USEPKGONLY) + conf.usepkgonly = 1; + + /* actions */ + if (opt & EMERGE_INFO + || (argv[optind] && strncmp(argv[optind], "info", 4) == 0)) { + /* info: perform info, then exit */ + do_info(); + exit(0); + } else { + if (opt & EMERGE_FETCH) + action = do_fetch; + else if (opt & EMERGE_UNMERGE) + action = do_unmerge; + else if (opt & EMERGE_QUERY) + action = do_query; + else if (opt & EMERGE_SYNC) + action = do_sync; + else if (opt & EMERGE_LIST) + action = do_list; + else if (opt & EMERGE_UPDATE) + action = do_update; + else if (opt & EMERGE_CLEAN) + action = do_clean; + else + action = do_merge; + } + + /* this adds the remainder of the arguments (i.e. the packages) reversed, so they + are processed in the order specified (which may be important) */ + optend = argc - 1; + + while (optend >= optind) { + if (strnlen(argv[optend], GEM_NAME_LEN) == GEM_NAME_LEN) { + snprintf(msg, GEM_NAME_LEN, "Name too long: %s", argv[optend]); + edie(msg); + } + tmppkg = decode_namespec(argv[optend]); + if (!tmppkg) { + snprintf(msg, GEM_NAME_LEN, "Unable to decode name %s", argv[optend]); + edie(msg); + } + tmppkg->index_pkg=0; + conf.packages = llist_add_to(conf.packages, (char *)tmppkg); + optend--; + ++conf.num_packages; + } +} + +/* all the functions that start with parse_ are for parsing the Index file */ +static void +parse_pkgname_version(package_t* pkg) +{ + int i = 0; char *c; + while (msg[i] != ' ' && i < GEM_MLEN) ++i; + ++i; + if (i >= GEM_MLEN) edie("overflow!"); + c = &msg[i]; +keep_looking: + while (msg[i] != '-' && i < GEM_MLEN) ++i; + ++i; + if (i >= GEM_MLEN) edie("overflow looking for version"); + if (!isdigit(msg[i])) goto keep_looking; + strncpy(pkg->name, c, i-5); + c = &msg[i]; + strncpy(pkg->version, c, GEM_NAME_LEN); + i = strnlen(c, GEM_NAME_LEN); + pkg->version[i-1]=0; +} + +static void +parse_category(package_t* pkg) +{ + /* CATEGORY: sys-kernel */ + int i = 0; char *c; + while (msg[i] != ' ') ++i; + ++i; + c = &msg[i]; + i = strnlen(c, GEM_NAME_LEN); + c[i-1]=0; + strncpy(pkg->category, c, i-1); +} + +static void +parse_depend_line(package_t* pkg, char* m) +{ + /* just strchr our way through it--> use m, not msg! */ + char *c, *d, *i=m; + do { + c = strchr(i, ' '); + if (c != NULL) { + d = xmalloc(c-i+1); + if (d == NULL) edie("failed to alloc space for depend"); + strncpy(d, i, c-i); + pkg->depends = llist_add_to(pkg->depends, d); + ++c; i = c; + } + } while (c != NULL); + /* do the last one too */ + d = xmalloc(strlen(i)+1); + strcpy(d, i); + pkg->depends = llist_add_to(pkg->depends, d); + +} + +/* this is out in static-land because both parse_depends and parse_line need to use it */ +static FILE *index_file; +static void +parse_depends(package_t* pkg) +{ + int saw_end_of_line = 0, len; + char *m, *l; + static char tmp[GEM_NAME_LEN]; + /* skip over the RDEPEND: part */ + if ((m = strchr(msg, ':')) == NULL) edie("No RDEPEND?"); + ++m; ++m; + while (!saw_end_of_line) { + len = strnlen(msg, GEM_MLEN); + /* it's ok to use msg here ... */ + if (msg[len-1] == '\n') { + saw_end_of_line = 1; + msg[len-1]=0; /* cut that end of line off */ + /* but parsing the line parses on m */ + parse_depend_line(pkg, m); + } else { + /* also ok here */ + l = rindex(msg, ' '); /* space before the last word */ + ++l; + strncpy(tmp, l, GEM_NAME_LEN); /* len - l ? */ + --l; *l=0; /* now the line parser can't see the partial word */ + parse_depend_line(pkg, m); + strncpy(msg, tmp, GEM_NAME_LEN); /* now msg has the partial word */ + len = strnlen(msg, GEM_NAME_LEN); + l = &msg[len]; + if (fgets(l, GEM_MLEN-len, index_file) == NULL) edie("unexpect end of file (deps"); + /* msg doesn't have RDEPEND anymore, so parse at beginning of msg */ + m = &msg[0]; + } + } +} + +static int +parse_line(package_t* pkg) +{ /* must return 1 when the whole package is ready, otherwise 0 (or edie()) */ + /* fortunately, the lines in each section have totally distinct beginnings! */ + if (msg[0] == '\n') return 0; + if (msg[0] == 'P') parse_pkgname_version(pkg); + else if (msg[0] == 'C' && msg[1] == 'A') parse_category(pkg); + else if (msg[0] == 'C' && msg[1] == 'H') return 0; /* parse_chost(pkg); */ + else if (msg[0] == 'L') return 0; /* parse_license(pkg); */ + else if (msg[0] == 'C' && msg[1] == 'F') return 0; /* parse_cflags(pkg); */ + else if (msg[0] == 'R') parse_depends(pkg); + else if (msg[0] == 'U') return 0; /* parse_use(pkg); */ + /* size is the very last entry, so it will return 1 */ + else if (msg[0] == 'S') return 1; /* parse_size(pkg); */ + else { edie("unexpected data in index file"); } + return 0; +} + +static int +process_index(void) +{ + package_t *pkg = 0; + + if ((index_file = bb_xfopen(conf.index, "r")) == 0) + edie("Failed to open index file"); + if (conf.debug) einfo("*** *** Processing index file"); + + while (feof(index_file) != 1) { + /* can we get a line? */ + if (fgets(msg, GEM_MLEN, index_file) != NULL) { + /* do we have a package? */ + if (!pkg) { + pkg = (package_t*) malloc(sizeof(package_t)); + memset(pkg, 0, sizeof(package_t)); + } + /* is the pkg already complete? */ + if (parse_line(pkg)==1) { + /* finish package! */ + if (conf.verbose) + print_pkg(pkg); + pkg_info.packages = llist_add_to(pkg_info.packages, (char *) pkg); + pkg = 0; + ++pkg_info.num_packages; + } + } + } + if (ferror(index_file) != 0) { + edie("encountered error while processing index file"); + } + if (bb_fclose_nonstdin(index_file) != 0) return 1; + if (conf.debug) { einfo("*** *** Done processing index file"); } + return 0; +} + +int +emerge_main(int argc, char **argv) +{ + char tmpp[GEM_PATH_MAX]; + unsigned int len; int i; + llist_t *tp; + package_t *package = 0; + + if (argc < 2) + bb_show_usage(); + + conf.verbose = 0; + conf.debug = 0; + conf.pretend = 0; + conf.usepkgonly = 0; + conf.packages = 0; + conf.num_packages = 0; + conf.index = 0; + + /* command line overrides env vars. */ + conf.pkgdir = getenv("PKGDIR"); + conf.binhost = getenv("PORTAGE_BINHOST"); + conf.tmpdir = getenv("PORTAGE_TMPDIR"); + conf.root = getenv("ROOT"); + conf.index = getenv("INDEX_FILE"); + conf.pkg_dbdir = getenv("PKG_DBDIR"); + + parseargs(argc, argv); + + /* now configurations may have some items, and some not. function may exit */ +#undef FOO +#define FOO "Try adding it to your /etc/make.conf, exporting it as an environment variable, or specifying it on the command line" + /* verify that environment variables exist before going on, fix them on disk if necessary */ + if (conf.pkgdir == 0) + edie("PKGDIR is not set. " FOO); + if (conf.binhost == 0) + edie("PORTAGE_BINHOST is not set. " FOO); + if (conf.tmpdir == 0) + edie("PORTAGE_TMPDIR is not set. " FOO); + if (conf.root == 0) + edie("ROOT is not set. " FOO); + if (conf.index == 0) + edie("INDEX is not set. " FOO); +#undef FOO + (void) tbz2pkg_init(&conf); + + + /* make sure PKGDIR exists on disk */ + len = strnlen(conf.pkgdir, GEM_PATH_MAX); + memset(tmpp, 0, sizeof(tmpp)); + strncpy(tmpp, conf.pkgdir, len); + strncat(tmpp, "/All", GEM_PATH_MAX - 1 - len); + if (!dir_exists(tmpp)) /* dir_exists checks R_OK, not W_OK...fix that later! */ + if (mkalldirs(tmpp) != 0) + edie("FAILED to make PKGDIR/All"); + + /* that's all for configuration; let's parse out the index */ + pkg_info.searchable_packages=0; + pkg_info.num_packages=0; + process_index(); + + pkg_info.searchable_packages = + (package_t**) xmalloc(pkg_info.num_packages*sizeof(package_t*)); + tp = pkg_info.packages; + for (i = 0; i < pkg_info.num_packages; ++i) { + pkg_info.searchable_packages[i] = (package_t*) tp->data; + tp = tp->link; + } + + /* now we are all set up. perform the action */ + tp = conf.packages; + while (tp != 0) { + package = (package_t*)tp->data; + if (conf.debug) { + bb_printf("handling package:\n"); + print_pkg(package); + } + package->index_pkg=find_in_index(package); + if (action(package) != 0) { + sprintf(msg, "Failed on package %s", package->name); + edie(msg); + } + package = 0; + tp = tp->link; + } + + exit(EXIT_SUCCESS); +} diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/gentoo_shared.c busybox/archival/gentoo_shared.c --- busybox-1.00/archival/gentoo_shared.c 1970-01-01 01:00:00.000000000 +0100 +++ busybox/archival/gentoo_shared.c 2005-06-08 19:46:32.000000000 +0200 @@ -0,0 +1,297 @@ +/* +* Distributed under the terms of the GNU General Public License v2 +* $Header: /home/collar_b/programs/cvs/busybox/archival/gentoo_shared.c,v 1.10 2005/06/08 17:46:32 collar_b Exp $ +* +* gentoo_shared: functions that are used by both emerge.c and tbz2pkg.c +* reason: emerge.c can depend (Config.on) on tbz2pkg, but not the other +* direction too. +* +* Written by Benjamin Collar & Natanael Copa +* Copyright (C) 2005, Benjamin Collar, Natanael Copa +* +******************************************************************** +* 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 "gentoo_shared.h" +#include + +char msg[GEM_MLEN]; /* useful for einfo, etc--just a buffer */ + +void +einfo(char *str) +{ + bb_fprintf(stdout, ">>> %s\n", str); +} + +void +eerror(char *str) +{ + bb_fprintf(stderr, "!!! %s\n", str); +} + +void +edie(char *str) +{ + eerror(str); + bb_error_msg_and_die("Quitting..."); +} + +extern configurables_t conf; + +void +print_pkg(const package_t* pkg) +{ + llist_t* d=pkg->depends; + bb_printf("Name: %s\n", pkg->name); + bb_printf("Version: %s\n", pkg->version); + bb_printf("Category: %s\n", pkg->category); + if (conf.debug) { + bb_printf("Modifiers: %s\n", pkg->modifier); + } + bb_printf("Depends: \n"); + while (d) { + bb_printf(" %s\n", d->data); + d = d->link; + } + bb_printf("\n"); +} + +int +bfind_compare(const void* key, const void* try) +{ + const package_t* query = (const package_t*) key; + const package_t **potential = (const package_t**) try; + int match=0; + match = strcmp(query->name, (*potential)->name); + if (match == 0) { + if (conf.debug) { bb_printf("got a name match...\n"); } + /* if the names match, check that we're in the same cateogry */ + if (query->category[0]) { + match = strcmp(query->category, (*potential)->category); + if (match != 0) { + /* category doesn't match -- need resolution from user*/ + sprintf(msg, "Found package name in multiple categories: (given) %s, (found)" + " %s Please resolve manually with emerge ... category/package\n", + query->category, (*potential)->category); + edie(msg); + } + if (conf.debug) { bb_printf("got a category match\n"); } + /* if category does match, check version */ + } + if (query->version[0]) { + match = strcmp(query->version, (*potential)->version); + if (query->modifier[0]) { + if (conf.debug) + bb_printf("modifier processing engaged!\n"); + if (query->modifier[1] == '=' && match == 0) goto stop; + if (query->modifier[0] == '=' && match == 0) goto stop; + if (query->modifier[0] == '>' && match == -1) { match = 0; goto stop; } + if (query->modifier[0] == '<' && match == 1) { match = 0; goto stop; } + } + if (conf.debug && match == 0) bb_printf("got version match\n"); + } + } +stop: + if (conf.debug) { + bb_printf("compare result %d for\n", match); + print_pkg(query); + print_pkg(*potential); + } + return match; +} + +typedef struct pkgsearch { + llist_t* results; + const package_t* package; +} pkgsearch_t; + +int +findPkgVerAction(const char* fileName, struct stat* statbuf, void* userData) +{ + pkgsearch_t* sch = (pkgsearch_t*) userData; + package_t* result; + char *basec, *bname, *begin, *dirc, *dname; + int retval=0, cmp; + static char version[GEM_NAME_LEN]; + if (conf.debug) bb_printf("checking for pkg/version, got %s\n", fileName); + version[0]=0; + basec = strdup(fileName); + bname = basename(basec); + if (strstr(bname, sch->package->name) != bname) { + free(basec); + return 1; + } + /* get version from fileName */ + begin = bname; + while (*begin != 0) { + if (*begin == '-') { + ++begin; + if (isdigit(*begin)) { + strcpy(version, begin); + break; + } + } else { + ++begin; + } + } + cmp = strcmp(version, sch->package->version); + if ( + (sch->package->modifier[1] == '=' && cmp == 0) || + (sch->package->modifier[0] == '<' && cmp == -1) || + (sch->package->modifier[0] == '>' && cmp == 1) || + (sch->package->modifier[0] == '>' && cmp == 1) || + (!sch->package->modifier[0]) /* && cmp == 0) */ + ) + { + result = xmalloc(sizeof(package_t)); + memset(result, 0, sizeof(package_t)); + /* we can copy the given package, as long as we clear the modifiers */ + memcpy(result, sch->package, sizeof(package_t)); + result->modifier[0]=0; result->modifier[1]=0; + strcpy(result->version, version); + strcpy(result->disk_location, fileName); + if (conf.debug) { bb_printf("found disk entry %s", result->disk_location); } + /* get the directory name == category */ + dirc = strdup(fileName); + dname = dirname(dirc); + free(basec); + basec = strdup(dname); + bname = basename(basec); + strcpy(result->category, bname); + if (conf.debug) { bb_printf("found disk package \n"); print_pkg(result); } + free(dirc); + sch->results = llist_add_to(sch->results, (char*) result); + retval = 0; + } else { + retval = 1; + } + + free(basec); + return retval; + +} + +/* the result* list for this function is a (single) directory name--the one that matches! */ +int +findCatAction(const char* fileName, struct stat* statbuf, void* userData) +{ + pkgsearch_t* sch = (pkgsearch_t*) userData; + char *basec, *bname; + int retval; + if (conf.debug) bb_printf("checking for category, got %s\n", fileName); + basec = strdup(fileName); + bname = basename(basec); + if (strcmp(bname, sch->package->category) == 0) { + /* this is our man */ + /* recur from here into pkgs directory, look for right name (only 1) */ + recursive_action(fileName, 1, 0, 0, 0, findPkgVerAction, userData); + retval=0; + } else { + retval=1; + } + free(basec); + return retval; +} + +llist_t* +find_package_on_disk(const package_t* package) +{ +/* int recursive_action(const char *fileName, int recurse, + int followLinks, int depthFirst, + int (*fileAction) (const char *fileName, struct stat* statbuf, void* userData), + int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData), + void* userData); +*/ + static pkgsearch_t ps; ps.results=0; ps.package=package; + if (!*package->category) + recursive_action(conf.pkg_dbdir, 1, 0, 0, 0, findPkgVerAction, (void*) &ps); + else + recursive_action(conf.pkg_dbdir, 1, 0, 0, 0, findCatAction, (void*) &ps); + return ps.results; +} + +package_t* +decode_namespec(char* namespec) +{ + /* on the command line or in the Package-depends, the form of a specification is + * emerge abc + * cat/abc + * >=cat/abc-1.3.4.5 + * this function has to split all that up and return a new package_t (in char form) + */ + package_t* ret = xmalloc(sizeof(package_t)); + char *slash, *begin, first = namespec[0]; + int provided_modifiers=0, provided_category=0; + memset(ret, 0, sizeof(package_t)); + if (conf.debug) { sprintf(msg, "decoding namespec %s\n", namespec); einfo(msg); } + switch (first) { + case '>': case '<': case '=': case '!': + { + /* handle modifiers */ + provided_modifiers=1; + ret->modifier[0]=first; + first = namespec[1]; + if (first == '>' || first == '<' || first == '=' || first == '!') { + ret->modifier[1] = first; + begin = &namespec[2]; + } else { + begin = &namespec[1]; + } + break; + } + default: + begin = &namespec[0]; + } + /* is there a slash (and thus a category & name?) */ + /* process category */ + if ((slash = strchr(begin, '/')) != 0) { + /* we have a slash, so there must be a category */ + provided_category=1; + *slash = 0; + strcpy(ret->category, begin); + ++slash; + begin = slash; + } else { + slash = begin; + } + /* process name & version */ + while (*begin != 0) { + if (*begin == '-') { + ++begin; + if (isdigit(*begin)) { + strcpy(ret->version, begin); + --begin; + *begin = 0; + break; + } else { + continue; + } + } + ++begin; + } + strcpy(ret->name, slash); + if (conf.debug) { + bb_printf("Decoded namespec to \n"); + print_pkg(ret); + } + if (provided_modifiers && !provided_category) { + sprintf(msg, "If you use =!<>, you must specify category/app. error: %s", namespec); + edie(msg); + } + + return ret; +} diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/gentoo_shared.h busybox/archival/gentoo_shared.h --- busybox-1.00/archival/gentoo_shared.h 1970-01-01 01:00:00.000000000 +0100 +++ busybox/archival/gentoo_shared.h 2005-06-08 20:16:26.000000000 +0200 @@ -0,0 +1,83 @@ +/* +* Distributed under the terms of the GNU General Public License v2 +* $Header: /home/collar_b/programs/cvs/busybox/archival/gentoo_shared.h,v 1.8 2005/06/08 17:46:32 collar_b Exp $ +* +* stuff that is shared between emerge.c and tbz2pkg.c +* +* Written by Benjamin Collar +* Copyright (C) 2005, Benjamin Collar +* +******************************************************************** +* 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 GENTOO_SHARED_H +#define GENTOO_SHARED_H + +#include "busybox.h" +#include +#include + +#define GEM_PATH_MAX (PATH_MAX) +#define GEM_NAME_LEN (32) +#define GEM_MLEN (GEM_PATH_MAX+64) + +typedef struct configurables_s { + char *root; + char *index; + char *tmpdir; + char *binhost; + char *pkgdir; + char *pkg_dbdir; /* used by tbz2pkg only */ + char *install_mask; /* used by tbz2pkg only */ + int verbose; + int debug; + int pretend; + int usepkgonly; + llist_t *packages; /* the package-names given on the command line */ + int num_packages; /* the number of package-names given */ +} configurables_t; + + +typedef struct package_s { + llist_t *depends; + struct package_s* index_pkg; + char modifier[3]; /* things like >=, =, !, ~, etc that portage uses */ + char version[GEM_NAME_LEN]; + char category[GEM_NAME_LEN]; + char name[GEM_NAME_LEN]; + char disk_location[GEM_PATH_MAX]; +} package_t; + + +extern char msg[GEM_MLEN]; /* useful for einfo, etc--just a buffer */ +void einfo(char* s); /* write some info, emerge style */ +void eerror(char* s); /* write an error, emerge style */ +void edie(char* s); /* write the error, then exit */ + +/* use this function to bsearch the pkg_info.packages array (all packages in index) + * or to just compare two package_t's + * key is a package_t* you are searching for, try is package_t** -- a possible match + * this function is used in bsearch, but may also be used to compare two package_t's; + * it returns -1,0,1 as one would expect */ +int bfind_compare(const void* key, const void* try); +void print_pkg(const package_t* pkg); +package_t* decode_namespec(char* namespec); +/* finds an installed package (or multiple, if modifiers are used) */ +llist_t* find_package_on_disk(const package_t* package); + + +#endif diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/tbz2pkg.c busybox/archival/tbz2pkg.c --- busybox-1.00/archival/tbz2pkg.c 1970-01-01 01:00:00.000000000 +0100 +++ busybox/archival/tbz2pkg.c 2005-06-08 20:17:34.000000000 +0200 @@ -0,0 +1,945 @@ +/* + * tbz2pkg, an installer for gentoo binary packages (tbz2) + * $Header: /home/collar_b/programs/cvs/busybox/archival/tbz2pkg.c,v 1.13 2005/06/08 18:17:34 collar_b Exp $ + * + * Written by Natanael Copa + * Copyright (C) 2005 by Natanael Copa + * Extended by Benjamin Collar + * Parts Copyright (C) 2005 by Benjamin Collar + * + * Distributed under the terms of the GNU General Public License v2 + * + * 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 "busybox.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tbz2pkg.h" + + +#define BUFSIZE 32768 +#define PREBUFSIZE 7 + +/* if BUFSIZE is smaller than XPACKSIZE, strange things could happen */ +#if (XPAKSIZE >= BUFSIZE) +#error BUFSIZE must be bigger than XPAKSIZE +#endif + +#define INDEX_LEN_OFFSET 8 +#define DATA_LEN_OFFSET (INDEX_LEN_OFFSET + 4) +#define INDEX_OFFSET (DATA_LEN_OFFSET + 4) + +/* if the XPAK is bigger than 8MiB, something is wrong */ +#define FAR_TOO_BIG_XPAK (8192 * 1024) + +#define CHECKSUM_ALGO HASH_SHA1 + +typedef struct xpak_s { + char *buffer; /* the raw xpak buffer */ + int size; + int index_len; + int data_len; + int data_offset; +} xpak_t; + +static configurables_t *conf; +static char prebuf[PREBUFSIZE]; +static int presize=0; /* how much is in the prebuf? */ +static char root_dbdir[256]; +static char root_dbdir_pkg[256]; +static char root_dbdir_pkg_contents[256]; + +static struct option const tbz2pkg_long_options[] = { + {"debug", 0, 0, 'd'}, + {"help", 0, 0, 'h'}, + {"install", 0, 0, 'i'}, + {"install-mask", 1, 0, 'M'}, + {"list", 0, 0, 'L'}, + {"pretend", 0, 0, 'p'}, + {"purge", 0, 0, 'P'}, + {"root", 1, 0, 'R'}, + {"verbose", 0, 0, 'v'}, + {NULL, 0, NULL, 0} +}; + +#define TBZ2PKG_DEBUG (1<<0) +#define TBZ2PKG_HELP (1<<1) +#define TBZ2PKG_INSTALL (1<<2) +#define TBZ2PKG_INSTALL_MASK (1<<3) +#define TBZ2PKG_LIST (1<<4) +#define TBZ2PKG_PRETEND (1<<5) +#define TBZ2PKG_PURGE (1<<6) +#define TBZ2PKG_ROOT (1<<7) +#define TBZ2PKG_VERBOSE (1<<8) + + +#define OPTIONS_FLAGS "dhiM:LpPR:v" + + +configurables_t *tbz2pkg_init(configurables_t *myconf) { + snprintf(root_dbdir, sizeof(root_dbdir), "%s", myconf->pkg_dbdir); + return conf = myconf; +} + +char *set_dbdir_pkg_char(const char *pkg) { + snprintf(root_dbdir_pkg, sizeof(root_dbdir_pkg), "%s/%s", root_dbdir, pkg); + strncpy(root_dbdir_pkg_contents, root_dbdir_pkg, + sizeof(root_dbdir_pkg_contents)); + strncat(root_dbdir_pkg_contents, "/CONTENTS", + sizeof(root_dbdir_pkg_contents)); + return root_dbdir_pkg; +} + +char *set_dbdir_pkg(package_t *pkg) { + snprintf(root_dbdir_pkg, sizeof(root_dbdir_pkg), "%s", pkg->disk_location); + strncpy(root_dbdir_pkg_contents, root_dbdir_pkg, + sizeof(root_dbdir_pkg_contents)); + strncat(root_dbdir_pkg_contents, "/CONTENTS", + sizeof(root_dbdir_pkg_contents)); + return root_dbdir_pkg; +} + +/* + convert a 4 byte big endian to int +*/ +static int decodeint(const unsigned char *ptr) { + return (((int)ptr[0]) << 24) + (((int)ptr[1]) << 16) + + (((int)ptr[2]) << 8) + ((int)ptr[3]); +} + + + +/* + return a string with the absolute path. +*/ +char *mk_absolute_path(const char *path) { + static char fullpath[256]; /* maximun length of path */ + if (strncmp(path, "./", 2) == 0) + snprintf(fullpath, sizeof(fullpath), "%s%s", conf->root, path+1); + else + snprintf(fullpath, sizeof(fullpath), "%s%s%s", + conf->root, (path[0] != '/') ? "/" : "", path); + return fullpath; +} + + +/* Creates a full path from a dbfile in the pkgdb */ +char *mk_dbdir_path(const char *package, const char *dbfile) { + static char path[256]; + snprintf(path, sizeof(path), "%s/%s/%s", + mk_absolute_path(conf->pkg_dbdir), package, dbfile); + return path; +} + +/* + finds the specified index and returns the value in a allocated buffer +*/ +static char *xpak_get_data(const xpak_t *xpak, const char *key) { + int len; + int offset=INDEX_OFFSET; + char *pathname; + while (offset < xpak->index_len) { + if (offset + 4 > xpak->size) + bb_error_msg_and_die("xpak_get_data: problems with XPAK"); + len = decodeint(xpak->buffer + offset); + + /* pathname_len + pathname + data_offset + data_len */ + if (offset + 4 + len + 4 + 4 > xpak->size) + return NULL; + pathname = bb_xstrndup(xpak->buffer + offset + 4, len); + if (strcmp(pathname, key) == 0) { + int data_offset = decodeint(xpak->buffer + offset + 4 + len); + int data_len = decodeint(xpak->buffer + offset + 8 + len); + char *p = xmalloc(data_len+1); + if (data_offset >= 0 && ((data_offset + data_len) < xpak->size)) { + memcpy(p, xpak->buffer + xpak->data_offset + data_offset, + data_len); + p[data_len] = '\0'; + free(pathname); + return(p); + } else { + bb_error_msg_and_die("xpak_get_data: data_offset=%i, " + "data_offset+data_len=%i, xpak->size=%i", + data_offset, data_offset + data_len, + xpak->size); + } + } + free(pathname); + offset += 12 + len; + } + /* not found */ + return NULL; +} + +/* + allocate space for xpak->buffer and get the rest of the data stream + memory is allocated and pointer is stored in global variable xpak. + + returns the size of xpak or -1 on error. +*/ +int get_xpak(int src_fd, xpak_t *xpak, const char *restbuf, size_t restsize) { + static char readbuf[BUFSIZE]; + int total = restsize; + int numread; + + xpak->buffer = xmalloc(restsize); + memcpy(xpak->buffer, restbuf, restsize); + + /* dump the rest of the data */ + while((numread = read(src_fd, readbuf, BUFSIZE))) { + char *p; + p = xrealloc(xpak->buffer, total + numread); + xpak->buffer = p; + memcpy(xpak->buffer + total, readbuf, numread); + total += numread; + /* check that the XPAK is not unreasonable big */ + if (total > FAR_TOO_BIG_XPAK) { + bb_error_msg_and_die("XPAK is unreasonable big. Giving up."); + } + } + xpak->size = total - 8; /* we skip the trailing [4 byte offset]STOP */ + if (xpak->size < (INDEX_OFFSET + 8)) { /* the +8 is for the "XPAKSTOP" */ + /* something is definitively wrong */ + free(xpak->buffer); + xpak->buffer = NULL; + return -1; + } + + xpak->index_len = decodeint(xpak->buffer + INDEX_LEN_OFFSET); + xpak->data_len = decodeint(xpak->buffer + DATA_LEN_OFFSET); + xpak->data_offset = INDEX_OFFSET + xpak->index_len; + if (xpak->data_offset + xpak->data_len + 8 > xpak->size) { + /* we are out of buffer */ + free(xpak->buffer); + xpak->buffer=NULL; + return -1; + } + return xpak->size; +} + +/* + flush buffer to file +*/ +int flush_buf(const char *buf, int size, const char *filename) { + FILE *outfile; + int written; + outfile = bb_xfopen(filename, "w"); + written = fwrite(buf, 1, size, outfile); + bb_fclose_nonstdin(outfile); + return written; +} + +/* + read data from src_fd until we find a "XPAKPACK" string + returns numbers of byte available in buffer +*/ +int read_to_xpak(int src_fd, char *buf, size_t size, xpak_t *xpak) { + int n; + int count, rest, avail; + char *p=buf; + char *found = NULL; + + memcpy(buf, prebuf, presize); + n = size - presize; /* this is what we want to read */ + assert(n>0); + if ((count = bb_xread(src_fd, buf+presize, n) + presize) == 0) { + /* we are done reading from file. no more data */ + return 0; + } + presize=0; + avail=count; + rest=count; + while( ((p=memchr(p, 'X', rest)) != NULL) && (found == NULL) && rest) { + avail = p - buf; /* data available in buffer that is guaranteed to + not contain "XPAKPACK" */ + rest = count - avail; /* the amount of data we need to examine */ + if ( rest < PREBUFSIZE ) { + /* + we found 'X' but we don't have enough data left to check + the entire string. We save the data in prebuf for next read + */ + memcpy(prebuf, p, rest); + presize=rest; + return avail; + } + + if (strncmp(p, "XPAKPACK", rest)==0) { + /* ok guy's, we found the XPAKPACK */ + found=p; + if (get_xpak(src_fd, xpak, p, rest)<0) + bb_error_msg_and_die("Problems with XPAK\n"); + if (conf->debug) flush_buf(xpak->buffer, xpak->size, "/tmp/xpak"); + return avail; + } + + /* nothing here to see folks, move on (no XPAKPACK found)*/ + p++; + rest--; + } + if (avail == 0 && found == NULL) { + bb_error_msg_and_die("No XPACK was found. sorry...\n"); + } + return count; +} + + +/* + Extrackt the package from xpak. (CATEGORY/PF) +*/ +char* xpak_get_package(xpak_t *xpak, char *package, int packagemax) { + char *cat, *pf, *eol; + + if ((cat = xpak_get_data(xpak, "CATEGORY")) == NULL) + bb_error_msg_and_die("CATEGORY not found in XPAK"); + + if ((pf = xpak_get_data(xpak, "PF")) == NULL) + bb_error_msg_and_die("PF not found in XPAK"); + + if ((eol = strchr(cat, '\n')) != NULL) *eol = '\0'; + if ((eol = strchr(pf, '\n')) != NULL) *eol = '\0'; + snprintf(package, packagemax, "%s/%s", cat, pf); + free(cat); + free(pf); + return package; +} + +/* + unpack the xpak to path +*/ +int xpak_unpack(xpak_t *xpak, char *package, int package_bufsize) { + int len; + int offset=INDEX_OFFSET; + char *pathname; + int data_offset, data_len; + + xpak_get_package(xpak, package, package_bufsize); + set_dbdir_pkg_char(package); + bb_make_directory(root_dbdir_pkg, -1, FILEUTILS_RECUR); + + while (offset < xpak->index_len) { + char *key; + if (offset + 4 > xpak->size) return -1; + len = decodeint(xpak->buffer + offset); + + /* pathname_len + pathname + data_offset + data_len */ + if (offset + 4 + len + 4 + 4 > xpak->size) return -1; + if ((key = xmalloc(len + 1))==NULL) return -1; + memcpy(key, xpak->buffer + offset + 4, len); + key[len] = '\0'; + pathname = mk_dbdir_path(package, key); + free(key); + + data_offset = decodeint(xpak->buffer + offset + 4 + len); + data_len = decodeint(xpak->buffer + offset + 8 + len); + + if ((data_offset >= 0) && ((data_offset + data_len) < xpak->size)) { + int i; + if (conf->debug) { + /* unpack the xpak if we are in debug mode */ + if (conf->pretend) + i = xpak->size; + else + i = flush_buf(xpak->buffer + + xpak->data_offset + + data_offset, + data_len, pathname); + if (conf->verbose) + bb_printf("Writing %s: %i bytes\n", pathname, i); + } + } else { + bb_error_msg_and_die("Invalid XPAK."); + } + offset += 12 + len; + } + return 0; +} + +/* + create a rejectlist for tar from install_mask +*/ +llist_t *create_reject_list(const char *install_mask) { + char *tok, *mask; + llist_t *list = NULL; + + mask = bb_xstrdup(install_mask); + /* according to man 3 strtok: "Never use these functions." + any alternative how this can easily be solved? */ + tok=strtok(mask, " "); + while(tok != NULL) { + list = llist_add_to(list, bb_xstrdup(tok)); + if (conf->debug) bb_printf("create_reject_list: added %s\n", tok); + tok = strtok(NULL, " "); + } + free(mask); + return list; +} + +/* + for debugging stuff +*/ +void dump_llist(llist_t *list) { + llist_t *item; + item = list; + while (item != NULL) { + bb_printf("dump_llist: %s\n", item->data); + item = item->link; + } +} + +/* + this func unpacks (tar -xj) whatever comes from src_fd, + return the filelist to ret_fd and exit. + the XPAK is supposed to be stripped off from src_fd +*/ +void tbz2_unpack_files(int src_fd, int ret_fd, const char *root_dir, + const char *install_mask) { + archive_handle_t *tar_handle = init_handle(); + llist_t *item; + + tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL; + + if (install_mask != NULL) { + if (conf->debug) + bb_printf("creating reject list from '%s'\n", install_mask); + tar_handle->reject = create_reject_list(install_mask); + tar_handle->filter = filter_accept_reject_list; + if (conf->debug) + dump_llist(tar_handle->reject); + } + tar_handle->src_fd = src_fd; + tar_handle->seek = seek_by_char; + if (! conf->pretend) tar_handle->action_data = data_extract_all; + + if (root_dir != NULL) { + if (chdir(root_dir)) + bb_perror_msg_and_die("Couldnt chdir to %s", root_dir); + } else { + if (chdir("/")) + bb_perror_msg_and_die("Couldnt chdir to /"); + } + + while (get_header_tar_bz2(tar_handle) == EXIT_SUCCESS); + + /* return the filelist */ + for (item = tar_handle->passed; item != NULL; item = item->link) { + if (item->data != NULL) { + write(ret_fd, item->data, strlen(item->data)); + write(ret_fd, "\0", 1); + } + } +} + + +/* those funcs are copied from coreutils/md5_sha1_sum.c */ +/* This might be useful elsewhere */ +static unsigned char *hash_bin_to_hex(unsigned char *hash_value, + unsigned char hash_length) +{ + int x, len, max; + unsigned char *hex_value; + + max = (hash_length * 2) + 2; + hex_value = xmalloc(max); + for (x = len = 0; x < hash_length; x++) { + len += snprintf(hex_value + len, max - len, "%02x", hash_value[x]); + } + return (hex_value); +} + +static uint8_t *hash_file(const char *filename, uint8_t hash_algo) +{ + static uint8_t hash_value_bin[22]; + uint8_t *hash_value = NULL; + uint8_t hash_length; + int src_fd; + + src_fd = open(filename, O_RDONLY); + + if (hash_algo == HASH_MD5) { + hash_length = 16; + } else { + hash_length = 20; + } + + /* hash_value_bin = xmalloc(hash_length); */ + + if ((src_fd != -1) && (hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2)) { + hash_value = hash_bin_to_hex(hash_value_bin, hash_length); + } else { + bb_perror_msg("%s", filename); + } + close(src_fd); + return(hash_value); +} + + +/* + write a line to CONTENTS file + returns size of file +*/ +int write_line_to_contents(FILE *outf, const char *line) { + struct stat info; + char *fullpath = mk_absolute_path(line); + + /* append to CONTENTS */ + if (!conf->pretend) { + /* first we check if it is a link */ + if (lstat(fullpath, &info) < 0) { + bb_perror_msg(fullpath); + return 0; + } + if (S_ISLNK(info.st_mode)) { + static char linkbuf[256]; + readlink(fullpath, linkbuf, sizeof(linkbuf)); + bb_fprintf(outf, "sym %s -> %s %i\n", line, linkbuf, + (int)info.st_mtime); + } else if (S_ISDIR(info.st_mode)) { + bb_fprintf(outf, "dir %s\n", line); + } else if (S_ISREG(info.st_mode)) { +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + char *hash = hash_file(fullpath, CHECKSUM_ALGO); + bb_fprintf(outf, "obj %s %s %i\n", line, hash, (int)info.st_mtime); + free(hash); +#else + bb_fprintf(outf, "obj %s %i %i\n", line, (int)info.st_size, + (int)info.st_mtime); +#endif + } else { + bb_fprintf(outf, "??? %s - %i\n", line, + (int)info.st_mtime); + } + } + if (conf->verbose) bb_printf(">>> %s\n", line); + return info.st_size; +} + +/* + generate a CONTENTS file + returns numbers of lines written. +*/ +int create_contents(int child_fd, const char *package) { + static char line[1024]; /* read buf */ + char c; + int i = 0, line_count = 0, total_size = 0; + FILE *outf; + + set_dbdir_pkg_char(package); + bb_make_directory(root_dbdir_pkg, -1, FILEUTILS_RECUR); + + if (conf->root != NULL) chdir(conf->root); else chdir("/"); + + /* open the CONTENTS file */ + outf = bb_xfopen(root_dbdir_pkg_contents, "w"); + + /* now we can read the file list from child */ + /* we reuse the buffer */ + while (read(child_fd, &c, 1)) { + if (i < (sizeof(line)-1)) /* buffer size checking... */ + line[i++] = c; + else + line[i] = '\0'; + if (c == '\0') { + total_size += write_line_to_contents(outf, line); + line_count++; + i = 0; + } + } + if (!conf->pretend) fclose(outf); + return total_size; +} + +/* insert a string to llist, sorted by length. longest first */ +/* this code has to be buggy.... */ +llist_t *llist_insert_length_sorted(llist_t *list, char *data) { + llist_t *prev=NULL, *cur = list; + int data_len = strlen(data); + + /* if this is the first element, create a new list */ + if (list == NULL) return llist_add_to(list, data); + + while ((cur != NULL) && (strlen(cur->data) > data_len)) { + prev = cur; + cur = cur->link; + } + if (cur == NULL) { /* this was the shortest, append to the end */ + prev->link = llist_add_to(NULL, data); + } else { + if (prev != NULL) + prev->link = llist_add_to(cur, data); + else + return llist_add_to(cur, data); + } + return list; +} + +/* + check if path is a link that points to dest. + returns nonzero if the dest correspond to the link. +*/ +int check_link(const char *path, const char *dest) { + static char buf[256]; + if (readlink(path, buf, sizeof(buf)) < 0) return 0; + return (strcmp(buf, dest) == 0); +} + +/* + umerge a symlink. + modifies the passed "string". (replaces all spaces with \0 t be exact) +*/ +int unmerge_sym(char *string) { + char *type = strtok(string, " "); + char *path = strtok(NULL, " "); + char *dest; + time_t mtime; + char *fullpath = mk_absolute_path(path); + static struct stat info; + + strtok(NULL, " "); /* get the '->' */ + dest = strtok(NULL, " "); + mtime = atoi(strtok(NULL, " ")); + if (type == NULL || path == NULL) return 0; + + lstat(fullpath, &info); + if (check_link(fullpath, dest) && (info.st_mtime == mtime)) { + /* remove it */ + if (conf->verbose) bb_printf("<<< %s\n", path); + if (!conf->pretend && (unlink(fullpath) < 0)) + bb_perror_msg("%s", fullpath); + else + return info.st_size; + } else { + if (conf->verbose) bb_printf("--- %s\n", path); + } + + return 0; +} + +/* + unmerge a file + modifies line. (replaces all spaces with \0 t be exact) + returns size of the file removed. +*/ +int unmerge_obj(char *line) { + char *type = strtok(line, " "); + char *path = strtok(NULL, " "); + char *chksum = strtok(NULL, " "); + time_t mtime = atoi(strtok(NULL, " ")); + static char fullpath[256]; + static struct stat info; + + if (type == NULL || path == NULL || chksum == NULL) return 0; + + snprintf(fullpath, sizeof(fullpath), "%s%s%s", + conf->root, (path[0] != '/') ? "/" : "", path); + + lstat(fullpath, &info); + /* + bb_printf("%s:%s contents(%i:%i) - stat(%i,%i)\n\n", + type, path, atoi(chksum), (int)mtime, + (int)info.st_size, (int)info.st_mtime); + */ + /* currently the chksum is just the file size... */ + if ((mtime == info.st_mtime) && +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + (strcmp(chksum, hash_file(fullpath, CHECKSUM_ALGO)) == 0) + /* we should free the allocated mem from hash_file here... */ +#else + (atoi(chksum) == info.st_size) +#endif + ) { + if (conf->verbose) bb_printf("<<< %s\n", path); + if (!conf->pretend && unlink(fullpath) < 0) + bb_perror_msg("%s", fullpath); + else { + return info.st_size; + } + } else { + if(conf->verbose) bb_printf("--- %s\n", path); + } + return 0; +} + +/* + change dir where the CONTENTS are stored + return 0 on succes. +*/ /* +int ch_dbdir(const char *package) { + char path[256]; + snprintf(path, sizeof(path), "%s/%s/%s", + conf->root, conf->pkg_dbdir, package); + return (chdir(mk_as_path(package))); +} +*/ +/* + Search unlink all files liste in list and destroy the list. + Data in the nodes is free'ed too. + returns number of destroyed items. +*/ +int llist_rmdir_and_destroy(llist_t *list) { + llist_t *item = list; + int count = 0; + while (item != NULL) { + llist_t *tmp; + if (rmdir(mk_absolute_path(item->data)) == 0) { + count++; + if (conf->verbose) bb_printf("<<< %s\n", item->data); + } else { + if (conf->verbose) bb_printf("--- %s\n", item->data); + } + free(item->data); + tmp = item; + item = item->link; + free(tmp); + } + return count; +} + +int tbz2_unmerge(package_t *package) { + FILE *inf; + static char line[256]; + llist_t *dirs=NULL; + int total_removed = 0; + + if (package == NULL) return -1; + set_dbdir_pkg(package); + /* check if file exist */ + inf = bb_xfopen(root_dbdir_pkg_contents, "r"); + chdir(conf->root); + while (fgets(line, sizeof(line), inf)) { + chomp(line); + if (strncmp(line, "dir", 3) == 0) { + /* we remove the dirs after files and sym's */ + dirs = llist_insert_length_sorted(dirs, bb_xstrdup(line+4)); + } else if (strncmp(line, "sym", 3) == 0) { + unmerge_sym(line); + } else { + unmerge_obj(line); + } + ++total_removed; + } + fclose(inf); + /* remove dirs */ + if (conf->debug) + bb_fprintf(stderr, "removed %i dirs", llist_rmdir_and_destroy(dirs)); + else + llist_rmdir_and_destroy(dirs); + + remove_file(root_dbdir_pkg, FILEUTILS_FORCE|FILEUTILS_RECUR); + return total_removed; +} + +/* list contents of package */ +void tbz2_list(package_t *package) { + FILE *inf; + static char line[256]; + + if (package == NULL) return; + set_dbdir_pkg(package); + inf = bb_xfopen(root_dbdir_pkg_contents, "r"); + while (fgets(line, sizeof(line), inf)) { + char *path; + strtok(line, " "); /* the type */ + path = strtok(NULL, " "); + chomp(path); + bb_printf("%s\n", path); + } + fclose(inf); +} + +/* + reads and unpacks package from src_fd and store the + category/package-version information in buffer package. +*/ +int tbz2_install(int src_fd, char *package, int package_size, + configurables_t *conf_ptr) { + int count, total=0; + static char buffer[BUFSIZE]; + xpak_t xpak; + int pid; + int fd_to_child[2]; + int fd_from_child[2]; /* this is the pipe where we get the file list + in return */ + /* set the global variable conf */ + conf = conf_ptr; + + if ((pipe(fd_to_child) != 0) || (pipe(fd_from_child) != 0)) { + bb_perror_msg_and_die("Can't create pipe"); + } + + pid = fork(); + if (pid == -1) { + bb_perror_msg_and_die("Fork failed"); + } + + if (pid == 0) { + /* child process */ + close(fd_to_child[1]); + close(fd_from_child[0]); + tbz2_unpack_files(fd_to_child[0], fd_from_child[1], + conf->root, conf->install_mask); + close(fd_to_child[0]); + close(fd_from_child[1]); + exit(0); + } + + /* parent process */ + close(fd_to_child[0]); /* This is for writing to child */ + close(fd_from_child[1]); /* and this is for reading */ + + while ( (count = read_to_xpak(src_fd, buffer, BUFSIZE, &xpak))) { + if (bb_full_write(fd_to_child[1], buffer, count) < count) { + bb_error_msg_and_die("unpacking archive failed"); + } + total += count; + } + /* count contains the tar.bz2 size */ + + if (xpak.buffer == NULL) bb_error_msg_and_die("No XPAK found. Sorry.\n"); + if (xpak_get_package(&xpak, package, package_size) == NULL) + bb_error_msg_and_die("Cound not extract package information from XPAK.\n"); + + /* now unmerge previously installed package */ + /* + if (access(mk_dbdir_path(package, "CONTENTS"), R_OK) == 0) + tbz2_unmerge(package, conf); + */ + if (!conf->pretend) xpak_unpack(&xpak, package, package_size); + + count = create_contents(fd_from_child[0], package); + close(fd_from_child[0]);/* close the read pipe */ + free(xpak.buffer); + return(count); +} + +/* + Install from filename. + NULL or '-' means STDIN. +*/ +int tbz2_install_file(char *file, configurables_t *myconf) { + char *display_name; + char package[256]; /* package name found in XPAK */ + int src_fd, size; + + tbz2pkg_init(myconf); + if ((file == NULL) || (strcmp(file, "-") == 0)) { + display_name = "STDIN"; + src_fd = STDIN_FILENO; + } else { + display_name = file; + src_fd = bb_xopen(file, O_RDONLY); + } + + if (myconf->verbose) bb_printf(">>> Unpacking %s\n", display_name); + + size = tbz2_install(src_fd, package, sizeof(package), myconf); + if (size < 0 ) + bb_error_msg_and_die("Package %s was not successfully" + " installed. Sorry.\n", display_name); + else + if (myconf->verbose) + bb_printf(">>> Package %s installed. %i bytes.\n\n", + package, size); + + if (src_fd != STDIN_FILENO) close(src_fd); + return size; +} + +/* + Main +*/ +int tbz2pkg_main(int argc, char *argv[]) { + unsigned long opt; + configurables_t myconf; + + myconf.root = NULL; + myconf.install_mask = NULL; + bb_applet_long_options = tbz2pkg_long_options; + opterr = 0; + opt = bb_getopt_ulflags(argc, argv, OPTIONS_FLAGS, + &myconf.install_mask, + &myconf.root); + + bb_printf("%s\n", myconf.root); + + if (opt & TBZ2PKG_HELP) bb_show_usage(); + + myconf.debug = (opt & TBZ2PKG_DEBUG); + myconf.pretend = (opt & TBZ2PKG_PRETEND); + myconf.verbose = (opt & TBZ2PKG_VERBOSE); + + if (myconf.root == NULL) myconf.root = getenv("ROOT"); + if (myconf.install_mask == NULL) myconf.install_mask = getenv("INSTALL_MASK"); + if ((myconf.pkg_dbdir = getenv("PKG_DBDIR")) == NULL) + /* shouldn't this use myconf.root, not / ? */ + myconf.pkg_dbdir = "/var/db/pkg"; + + if (myconf.debug) + bb_printf("root=%s, install_mask=%s, pkg_dbdir=%s\n", + myconf.root, myconf.install_mask, myconf.pkg_dbdir); + + tbz2pkg_init(&myconf); + if (opt & TBZ2PKG_INSTALL) { + if (optind == argc) { + tbz2_install_file(NULL, &myconf); + } else { + int i, total_size=0; + for (i=optind; i < argc; i++) + total_size += tbz2_install_file(argv[i], &myconf); + if (myconf.verbose) + bb_printf(">>> Totally %i bytes installed.\n\n", total_size); + } + } else { + int i; + llist_t *results=0; + package_t *pkg, *tmppkg; + for (i=optind; i < argc; i++) { + pkg = decode_namespec(argv[i]); + results = find_package_on_disk(pkg); + while (results) { + tmppkg = (package_t*) results->data; + if (opt & TBZ2PKG_LIST) { + bb_printf(">>> Listing %s.\n", tmppkg->name); + tbz2_list(tmppkg); + } else if (opt & TBZ2PKG_PURGE) { + bb_printf(">>> Purging %s.\n", tmppkg->name); + tbz2_unmerge(tmppkg); + /* } else if (opt & TBZ2PKG_CHECK) { + bb_printf(">>> Checking %s.\n", pkg); + tbz2_check(pkg); + */ + } + free(results->data); + results = results->link; + } + pkg = 0; results = 0; + } + } + + return 0; +} + diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/tbz2pkg.h busybox/archival/tbz2pkg.h --- busybox-1.00/archival/tbz2pkg.h 1970-01-01 01:00:00.000000000 +0100 +++ busybox/archival/tbz2pkg.h 2005-06-08 19:46:32.000000000 +0200 @@ -0,0 +1,35 @@ +/* +* Distributed under the terms of the GNU General Public License v2 +* $Header: /home/collar_b/programs/cvs/busybox/archival/tbz2pkg.h,v 1.9 2005/06/08 17:46:32 collar_b Exp $ +* +******************************************************************** +* 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 TBZ2PKG_H +#define TBZ2PKG_H + +#include "gentoo_shared.h" +/* the data & decls here are shared by tbz2pkg and emerge */ + + +/* functions defined in tbz2pkg and used in emerge */ +int tbz2_install_file(char *file, configurables_t *myconf); +int tbz2_unmerge(package_t *package); +void tbz2_list(package_t *package); +configurables_t *tbz2pkg_init(configurables_t *myconf); + +#endif /* TBZ2PKG_H */ diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/include/applets.h busybox/include/applets.h --- busybox-1.00/include/applets.h 2004-08-27 01:01:34.000000000 +0200 +++ busybox/include/applets.h 2005-03-30 15:33:02.000000000 +0200 @@ -178,6 +178,9 @@ #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS) APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif +#if defined(CONFIG_EMERGE) + APPLET(emerge, emerge_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif #ifdef CONFIG_ENV APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) #endif @@ -559,6 +562,9 @@ #ifdef CONFIG_TAR APPLET(tar, tar_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif +#ifdef CONFIG_TBZ2PKG + APPLET(tbz2pkg, tbz2pkg_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif #ifdef CONFIG_TEE APPLET(tee, tee_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) #endif diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/include/usage.h busybox/include/usage.h --- busybox-1.00/include/usage.h 2004-09-14 18:23:56.000000000 +0200 +++ busybox/include/usage.h 2005-06-08 20:16:16.000000000 +0200 @@ -531,6 +531,30 @@ "$ echo "Erik\\nis\\ncool"\n" \ "Erik\\nis\\ncool\n") +#define emerge_trivial_usage \ + "[-hdpvfKCqislucR:I:P:T:B:] package_name" +#define emerge_full_usage \ + "emerge is a utility to install, remove and manage Gentoo packages.\n" \ + "Options:\n" \ + "\t-h|--help : This help\n" \ + "\t-d|--debug : set -x\n" \ + "\t-p|--pretend : pretend only\n" \ + "\t-v|--verbose : set verbose flag\n" \ + "\t-f|--fetch : fetch binary files\n" \ + "\t-K|--usepkgonly : use binary package\n" \ + "\t-C|--unmerge : uninstall a binary package\n" \ + "\t-q|--query : query a package\n" \ + "\t--info|info : display info\n" \ + "\t--sync : perform $opt operation\n" \ + "\t--list : update index and list packages\n" \ + "\t--update : update index file\n" \ + "\t--clean : clean up tmpfiles\n" \ + "\t--root= : define ROOT=\n" \ + "\t--index= : define INDEX_FILE=\n" \ + "\t--pkgdir= : define PKGDIR=\n" \ + "\t--tmpdir= : define PORTAGE_TMPDIR=\n" \ + "\t--binhost= : define PORTAGE_BINHOST=\n" + #define env_trivial_usage \ "[-iu] [-] [name=value]... [command]" #define env_full_usage \ @@ -2422,6 +2446,21 @@ "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" \ "$ tar -cf /tmp/tarball.tar /usr/local\n" +#define tbz2pkg_trivial_usage \ + "[OPTION]... [FILE|PKG_NAME]..." +#define tbz2pkg_full_usage \ + "Manage gentoo-created binary packages.\n\n" \ + "Options:\n" \ + "\t-d\tshow debug output\n" \ + "\t-h\tshow this help information\n" \ + "\t-i\tinstalled given binary packages (FILE)\n" \ + "\t-M\tDon't install files that are masked by given regexp\n" \ + "\t-L\tList contents of installed package(s) (PKG_NAME)\n" \ + "\t-p\tPretend to do action (install, purge)\n" \ + "\t-P\tUninstall package(s) (PKG_NAME)\n" \ + "\t-R\tSet root directory to ROOT\n" \ + "\t-v\tVerbose output\n" + #define tee_trivial_usage \ "[OPTION]... [FILE]..." #define tee_full_usage \