initial import
[patches.git] / emerge-svn.diff
1 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/Config.in busybox/archival/Config.in
2 --- busybox-1.00/archival/Config.in 2004-03-15 09:28:16.000000000 +0100
3 +++ busybox/archival/Config.in 2005-03-29 13:45:03.000000000 +0200
4 @@ -97,6 +97,14 @@
5 However it saves space as none of the extra dpkg-deb, ar or tar options are
6 needed, they are linked to internally.
7
8 +config CONFIG_EMERGE
9 + bool "emerge workalike for gentoo packages"
10 + default n
11 + depends on CONFIG_WGET && CONFIG_TBZ2PKG
12 + help
13 + This allows busybox to fetch and install binary packages
14 + from the gentoo portage system.
15 +
16 config CONFIG_GUNZIP
17 bool "gunzip"
18 default n
19 @@ -205,6 +213,13 @@
20 help
21 Enable use of long options, increases size by about 400 Bytes
22
23 +config CONFIG_TBZ2PKG
24 + bool "tbz2pkg"
25 + default n
26 + depends on CONFIG_FEATURE_TAR_BZIP2
27 + help
28 + Install and uninstall gentoo binary packages
29 +
30 config CONFIG_UNCOMPRESS
31 bool "uncompress"
32 default n
33 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/Makefile.in busybox/archival/Makefile.in
34 --- busybox-1.00/archival/Makefile.in 2004-10-08 09:45:09.000000000 +0200
35 +++ busybox/archival/Makefile.in 2005-04-02 18:02:27.000000000 +0200
36 @@ -30,11 +30,13 @@
37 ARCHIVAL-$(CONFIG_CPIO) += cpio.o
38 ARCHIVAL-$(CONFIG_DPKG) += dpkg.o
39 ARCHIVAL-$(CONFIG_DPKG_DEB) += dpkg_deb.o
40 +ARCHIVAL-$(CONFIG_EMERGE) += emerge.o gentoo_shared.o
41 ARCHIVAL-$(CONFIG_GUNZIP) += gunzip.o
42 ARCHIVAL-$(CONFIG_GZIP) += gzip.o
43 ARCHIVAL-$(CONFIG_RPM2CPIO) += rpm2cpio.o
44 ARCHIVAL-$(CONFIG_RPM) += rpm.o
45 ARCHIVAL-$(CONFIG_TAR) += tar.o
46 +ARCHIVAL-$(CONFIG_TBZ2PKG) += tbz2pkg.o gentoo_shared.o
47 ARCHIVAL-$(CONFIG_UNCOMPRESS) += uncompress.o
48 ARCHIVAL-$(CONFIG_UNZIP) += unzip.o
49
50 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/emerge.c busybox/archival/emerge.c
51 --- busybox-1.00/archival/emerge.c 1970-01-01 01:00:00.000000000 +0100
52 +++ busybox/archival/emerge.c 2005-06-08 20:17:34.000000000 +0200
53 @@ -0,0 +1,768 @@
54 +/*
55 +* Distributed under the terms of the GNU General Public License v2
56 +* $Header: /home/collar_b/programs/cvs/busybox/archival/emerge.c,v 1.27 2005/06/08 18:17:34 collar_b Exp $
57 +*
58 +* emerge: a gentoo-emerge workalike for embedded systems, integrated
59 +* into the busybox suite
60 +*
61 +* Written by Benjamin Collar
62 +* Copyright (C) 2005, Benjamin Collar
63 +*
64 +********************************************************************
65 +* This program is free software; you can redistribute it and/or
66 +* modify it under the terms of the GNU General Public License as
67 +* published by the Free Software Foundation; either version 2 of the
68 +* License, or (at your option) any later version.
69 +*
70 +* This program is distributed in the hope that it will be useful, but
71 +* WITHOUT ANY WARRANTY; without even the implied warranty of
72 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
73 +* General Public License for more details.
74 +*
75 +* You should have received a copy of the GNU General Public License
76 +* along with this program; if not, write to the Free Software
77 +* Foundation, Inc., 59 Temple Place - Suite 330, Boston,
78 +* MA 02111-1307, USA.
79 +*/
80 +
81 +/* note about source code organization */
82 +/* this file is organized like this:
83 + * structs & other declarations
84 + * generally useful stuff
85 + * functions that start with do_ relate directly to an "action", ie a command
86 + * helper functions for the do_ start with the <command>_
87 + * functions that start with parse_ are used for processing the index file
88 + * other things should hopefully be clear :)
89 + */
90 +
91 +#include "busybox.h"
92 +#include <stdlib.h>
93 +#include <fcntl.h>
94 +#include <unistd.h>
95 +#include <dirent.h>
96 +#include <sys/types.h>
97 +#include <sys/stat.h>
98 +#include <sys/wait.h>
99 +#include <assert.h>
100 +#include <getopt.h>
101 +#include <errno.h>
102 +#include <ctype.h>
103 +#include "tbz2pkg.h"
104 +
105 +#define file_exists(f) (access(f, R_OK) == 0)
106 +#define dir_exists(f) (access(f, R_OK) == 0)
107 +
108 +
109 +typedef struct pkg_info_s {
110 + llist_t *packages;
111 + package_t** searchable_packages; /* sorted pointers to package names; use bsearch to find them */
112 + int num_packages; /* length of searchable_packages array */
113 +} pkg_info_t;
114 +
115 +extern int wget_main(int argc, char **argv);
116 +configurables_t conf;
117 +int (*action) (const package_t*);/* this is the action we'll take, after handling cmdline, etc */
118 +static pkg_info_t pkg_info;
119 +
120 +/* some useful functions */
121 +static int
122 +mkalldirs(char *dir)
123 +{
124 + return bb_make_directory(dir, -1, FILEUTILS_RECUR);
125 +}
126 +
127 +static package_t*
128 +find_in_index (package_t* pkg)
129 +{
130 + void* result;
131 + result = bsearch((void*) pkg, (const void*) pkg_info.searchable_packages,
132 + pkg_info.num_packages, sizeof(package_t*), bfind_compare);
133 + if (result) {
134 + return (*(package_t**)result);
135 + }
136 + return 0;
137 +}
138 +
139 +static int
140 +fetch_package(package_t* package)
141 +{
142 + /* using the package in package-name (which is category/package), figure
143 + * out the actual package name and URL. Then fetch it. return 0 when it works.
144 + */
145 + /* append the category/package to the binhost to create the url */
146 +
147 + static char fullurl[GEM_PATH_MAX];
148 + static char target[GEM_PATH_MAX];
149 + static char pkgver[GEM_NAME_LEN*2+2];
150 + sprintf(pkgver, "%s-%s.tbz2", package->name, package->version);
151 + if (!conf.pretend) {
152 + sprintf(msg, "Fetching package %s/%s", package->category, pkgver);
153 + einfo(msg);
154 + } else {
155 + sprintf(msg, "Would fetch %s/%s", package->category, pkgver);
156 + einfo(msg);
157 + return 0;
158 + }
159 + sprintf(target, "%s/All/", conf.pkgdir);
160 + /* make sure the package download directory exists */
161 + if ( mkalldirs(target) != 0 )
162 + edie("Failed to create package download directory");
163 + /* check if package already exists; if so, return ok */
164 + strcat(target, pkgver);
165 + if ( file_exists(target) ) {
166 + sprintf(msg, "Package already fetched: %s", target);
167 + einfo(msg);
168 + /* further processing expects msg to contain path to downloaded file */
169 + /* REFACTOR THIS */
170 + strcpy(msg, target);
171 + return 0;
172 + }
173 + sprintf(target, "%s/All/", conf.pkgdir);
174 + /* is it correct that the package file will be in http://binhost/category/abc-1.2.3.tbz2 ? */
175 + sprintf(fullurl, "%s/%s/%s", conf.binhost, package->category, pkgver);
176 + {
177 + char *cmdline[] = { "wget", "-c", "-P", target, fullurl, 0 };
178 + pid_t child=fork();
179 + if ( child == 0 ) {
180 + execvp("/proc/self/exe", cmdline);
181 + } else {
182 + int status, wgrval;
183 + wait(&status);
184 + if (WIFEXITED(status)) {
185 + wgrval = WEXITSTATUS(status);
186 + if (wgrval != 0) {
187 + edie("Failed to fetch package");
188 + } else {
189 + strcat(target, pkgver);
190 + sprintf(msg, "Package saved to %s", target);
191 + einfo(msg);
192 + }
193 + sprintf(msg, "%s/%s/%s", conf.pkgdir, package->category, pkgver);
194 + if (symlink(target, msg) != 0 && errno != EEXIST) {
195 + edie("Failed to symlink package into correct subdirectory");
196 + }
197 + /* save targetdir into msg; another function will use it */
198 + strcpy(msg, target);
199 + return 0;
200 + }
201 + }
202 + }
203 + return 1;
204 +}
205 +
206 +static int
207 +do_fetch(const package_t * user_pkg)
208 +{
209 + llist_t* list;
210 + package_t *dep_pkg, *index_pkg;
211 + int rval=0;
212 + index_pkg = user_pkg->index_pkg;
213 + if (!index_pkg) {
214 + eerror("Package not found in index, cannot fetch!");
215 + return 1;
216 + }
217 + list = index_pkg->depends;
218 + while (list) {
219 + dep_pkg = decode_namespec((char*) list->data);
220 + dep_pkg->index_pkg = find_in_index(dep_pkg);
221 + if (!dep_pkg->index_pkg) {
222 + sprintf(msg, "Dependency %s for %s not found in index",
223 + dep_pkg->name, user_pkg->name);
224 + eerror(msg);
225 + rval = 1;
226 + } else {
227 + rval = rval ? rval : do_fetch(dep_pkg);
228 + }
229 + free(dep_pkg);
230 + list = list->link;
231 + }
232 + if (conf.pretend) {
233 + sprintf(msg, "Would fetch package %s\n", user_pkg->name);
234 + einfo(msg);
235 + return 0;
236 + }
237 + return rval ? rval : fetch_package(index_pkg);
238 +}
239 +
240 +static void
241 +do_info(void)
242 +{
243 + bb_printf("PKGDIR=%s\n", (conf.pkgdir == NULL) ? "" : conf.pkgdir);
244 + bb_printf("PKG_DBDIR=%s\n", (conf.pkg_dbdir == NULL) ? "" : conf.pkg_dbdir);
245 + bb_printf("PORTAGE_BINHOST=%s\n",
246 + (conf.binhost == NULL) ? "" : conf.binhost);
247 + bb_printf("PORTAGE_TMPDIR=%s\n",
248 + (conf.tmpdir == NULL) ? "" : conf.tmpdir);
249 + bb_printf("ROOT=%s\n", (conf.root == NULL) ? "" : conf.root);
250 + bb_printf("INDEX_FILE=%s\n", (conf.index == NULL) ? "" : conf.index);
251 +}
252 +
253 +static int do_merge(const package_t* p);
254 +
255 +static int
256 +merge_should_install(package_t* p)
257 +{
258 + static package_t tmppkg;
259 + int rval=0;
260 + llist_t *installed, *t;
261 + memcpy(&tmppkg, p, sizeof(package_t));
262 + tmppkg.modifier[0]='>';
263 + tmppkg.modifier[1]='=';
264 + installed = find_package_on_disk(&tmppkg);
265 + rval = installed == 0;
266 + while (installed) {
267 + t = installed->link;
268 + free(installed);
269 + installed = t;
270 + }
271 + return rval;
272 +}
273 +
274 +static int
275 +merge_deps(package_t* p)
276 +{
277 + llist_t* list = p->depends;
278 + package_t* dep_pkg=0;
279 + int rval = 0;
280 + sprintf(msg, "Merging dependencies of %s", p->name);
281 + einfo(msg);
282 + if (conf.debug) { print_pkg(p); }
283 + while (list) {
284 + dep_pkg = decode_namespec((char*) list->data);
285 + if (strcmp(dep_pkg->category, "virtual") == 0) {
286 + rval = rval ? 1 : 0;
287 + sprintf(msg, "Can't do virtual dependencies yet. Please make sure %s/%s is installed",
288 + dep_pkg->category, dep_pkg->name);
289 + eerror(msg);
290 + } else {
291 + dep_pkg->index_pkg = find_in_index(dep_pkg);
292 + if (!dep_pkg->index_pkg) {
293 + rval = 1;
294 + } else {
295 + rval = rval ? 1 : do_merge(dep_pkg);
296 + }
297 + }
298 + free(dep_pkg);
299 + list = list->link;
300 + }
301 + einfo("Done merging dependencies");
302 + return rval;
303 +}
304 +
305 +static int
306 +do_merge(const package_t* user_pkg)
307 +{
308 + package_t* index_pkg = user_pkg->index_pkg;
309 + sprintf(msg, "Merging package %s", user_pkg->name);
310 + einfo(msg);
311 + if (!index_pkg) {
312 + eerror("Package not found in index");
313 + return 1;
314 + }
315 + if (!merge_should_install(index_pkg)) {
316 + sprintf(msg, "Package %s already up-to-date", user_pkg->name);
317 + einfo(msg);
318 + return 0;
319 + }
320 + if (merge_deps(index_pkg) != 0) { eerror("Failed to merge dependencies"); return 1; }
321 + if (fetch_package(index_pkg) != 0) { eerror("Failed to fetch package"); return 1; }
322 +
323 + /* fetch puts the filename into msg, so give that to tbz2 */
324 + if (conf.pretend) {
325 + sprintf(msg, "Would merge %s", user_pkg->name);
326 + einfo(msg);
327 + return 0;
328 + }
329 + if (tbz2_install_file(msg, &conf) <= 0) { eerror("Failed to install package"); return 1; }
330 + sprintf(msg, "Package %s merged successfully", user_pkg->name);
331 + einfo(msg);
332 + return 0;
333 +}
334 +
335 +static int
336 +do_unmerge(const package_t* user_pkg)
337 +{
338 + package_t* index_pkg = user_pkg->index_pkg, *installed_pkg=0;
339 + int rval=0, should_free=0;
340 + llist_t* installed_pkgs, *l;
341 + if (!index_pkg) {
342 + index_pkg = xmalloc(sizeof(package_t));
343 + memcpy(index_pkg, user_pkg, sizeof(package_t));
344 + sprintf(msg, "Unable to find package %s in index, continuing...", user_pkg->name);
345 + should_free=1;
346 + }
347 + if (conf.pretend) {
348 + sprintf(msg, "Would unmerge %s", user_pkg->name);
349 + einfo(msg);
350 + rval = 0;
351 + }
352 + installed_pkgs = find_package_on_disk(index_pkg);
353 + if (!installed_pkgs) {
354 + einfo("Nothing to unmerge");
355 + }
356 + while (installed_pkgs) {
357 + installed_pkg = (package_t*) installed_pkgs->data;
358 + if (conf.pretend) {
359 + sprintf(msg, "Would unmerge %s-%s", installed_pkg->name,
360 + installed_pkg->version);
361 + einfo(msg);
362 + } else {
363 + if (tbz2_unmerge(installed_pkg) <= 0) rval = 1;
364 + if (rval==0) {
365 + einfo("Unmerge successful");
366 + } else {
367 + eerror("Unmerge failed!");
368 + }
369 + }
370 +
371 + l = installed_pkgs->link;
372 + free(installed_pkgs->data);
373 + installed_pkgs = l;
374 + }
375 + if (should_free)
376 + free(index_pkg);
377 + return rval;
378 +}
379 +
380 +static int
381 +do_query(const package_t* user_pkg)
382 +{
383 + einfo("Package query result\nQuery--");
384 + print_pkg(user_pkg);
385 + if (!user_pkg->index_pkg) {
386 + sprintf(msg, "Package not found in index: %s, continuing", user_pkg->name);
387 + einfo(msg);
388 + } else {
389 + einfo("Found package in index");
390 + print_pkg(user_pkg->index_pkg);
391 + }
392 + {
393 + llist_t *r, *l;
394 + einfo("Checking for installed version");
395 + r = find_package_on_disk(user_pkg);
396 + if (!r) { einfo("Not found"); }
397 + while(r) {
398 + einfo("Found installed package");
399 + print_pkg((package_t*)r->data);
400 + l = r->link;
401 + free(r->data);
402 + r = l;
403 + }
404 + }
405 + return 0;
406 +}
407 +
408 +static int
409 +do_sync(const package_t* user_pkg)
410 +{
411 + eerror("Unable to sync");
412 + return 1;
413 +}
414 +
415 +static int
416 +do_list(const package_t* user_pkg)
417 +{
418 + llist_t* r = find_package_on_disk(user_pkg), *l;
419 + while (r) {
420 + tbz2_list((package_t*)r->data);
421 + l = r->link;
422 + free(r->data);
423 + r = l;
424 + }
425 + return 0;
426 +}
427 +
428 +static int
429 +do_update(const package_t* user_pkg)
430 +{
431 + eerror("Unable to update");
432 + return 1;
433 +}
434 +
435 +static int
436 +do_clean(const package_t* user_pkg)
437 +{
438 + eerror("Unable to clean");
439 + return 1;
440 +}
441 +
442 +
443 +/* parseargs and similar ilk */
444 +/* busybox has their own funny getopt structure, which depends on bits... */
445 +#define EMERGE_HELP (1<<0)
446 +#define EMERGE_DEBUG (1<<1)
447 +#define EMERGE_PRETEND (1<<2)
448 +#define EMERGE_VERBOSE (1<<3)
449 +#define EMERGE_FETCH (1<<4)
450 +#define EMERGE_USEPKGONLY (1<<5)
451 +#define EMERGE_UNMERGE (1<<6)
452 +#define EMERGE_QUERY (1<<7)
453 +#define EMERGE_INFO (1<<8)
454 +#define EMERGE_SYNC (1<<9)
455 +#define EMERGE_LIST (1<<10)
456 +#define EMERGE_UPDATE (1<<11)
457 +#define EMERGE_CLEAN (1<<12)
458 +
459 +static struct option const emerge_long_options[] = {
460 + {"help", 0, 0, 'h'},
461 + {"debug", 0, 0, 'd'},
462 + {"pretend", 0, 0, 'p'},
463 + {"verbose", 0, 0, 'v'},
464 + {"fetch", 0, 0, 'f'},
465 + {"usepkgonly", 0, 0, 'K'},
466 + {"unmerge", 0, 0, 'C'},
467 + {"query", 0, 0, 'q'},
468 + {"info", 0, 0, 'i'},
469 + {"sync", 0, 0, 's'},
470 + {"list", 0, 0, 'l'},
471 + {"update", 0, 0, 'u'},
472 + {"clean", 0, 0, 'c'},
473 + {"root", 1, 0, 'R'},
474 + {"index", 1, 0, 'I'},
475 + {"pkgdir", 1, 0, 'P'},
476 + {"tmpdir", 1, 0, 'T'},
477 + {"binhost", 1, 0, 'B'},
478 + {NULL, 0, NULL, 0}
479 +};
480 +
481 +#define OPTIONS_FLAGS "hdpvfKCqislucR:I:P:T:B:"
482 +static void
483 +parseargs(int argc, char *argv[])
484 +{
485 + int optend;
486 + unsigned long opt;
487 + char *root=0, *pkgdir=0, *tmpdir=0, *binhost=0, *cindex=0;
488 + package_t* tmppkg;
489 + opterr = 0;
490 + /* this complementaly only works if a conflict is specified in both directions,
491 + thus f~q:q~f will block f & q from being specified together. that seems
492 + pretty wrong to me */
493 + bb_opt_complementaly = "f~Cqisluc:i~pfKCqsluc:q~f";
494 + bb_applet_long_options = emerge_long_options;
495 +
496 + /* need to allocate these flags, not let bb do it! */
497 + /* need to allocate!! also, conf.index processing seems broken */
498 + root = pkgdir = tmpdir = binhost = 0;
499 + opt = bb_getopt_ulflags(argc, argv, OPTIONS_FLAGS,
500 + &root, &cindex, &pkgdir, &tmpdir, &binhost);
501 + if (root != NULL) {
502 + conf.root = xmalloc(strnlen(root, GEM_PATH_MAX));
503 + strncpy(conf.root, root, GEM_PATH_MAX);
504 + }
505 + if (pkgdir != NULL) {
506 + conf.pkgdir = xmalloc(strnlen(pkgdir, GEM_PATH_MAX));
507 + strncpy(conf.pkgdir, pkgdir, GEM_PATH_MAX);
508 + }
509 + if (tmpdir != NULL) {
510 + conf.tmpdir = xmalloc(strnlen(tmpdir, GEM_PATH_MAX));
511 + strncpy(conf.tmpdir, tmpdir, GEM_PATH_MAX);
512 + }
513 + if (binhost != NULL) {
514 + conf.binhost = xmalloc(strnlen(binhost, GEM_NAME_LEN));
515 + strncpy(conf.binhost, binhost, GEM_NAME_LEN);
516 + }
517 + if (cindex != NULL) {
518 + conf.index = xmalloc(strnlen(cindex, GEM_PATH_MAX));
519 + strncpy(conf.index, cindex, GEM_PATH_MAX);
520 + }
521 +
522 + /* Check one and only one context option was given */
523 + /* this doesn't work... */
524 + if (opt & 0x80000000UL) {
525 + bb_printf("conflict!\n");
526 + bb_show_usage();
527 + }
528 +
529 + if (opt & EMERGE_HELP)
530 + bb_show_usage();
531 +
532 + /* runtime-configuration */
533 + if (opt & EMERGE_DEBUG)
534 + conf.debug = 1;
535 + if (opt & EMERGE_PRETEND)
536 + conf.pretend = 1;
537 + if (opt & EMERGE_VERBOSE)
538 + conf.verbose = 1;
539 + if (opt & EMERGE_USEPKGONLY)
540 + conf.usepkgonly = 1;
541 +
542 + /* actions */
543 + if (opt & EMERGE_INFO
544 + || (argv[optind] && strncmp(argv[optind], "info", 4) == 0)) {
545 + /* info: perform info, then exit */
546 + do_info();
547 + exit(0);
548 + } else {
549 + if (opt & EMERGE_FETCH)
550 + action = do_fetch;
551 + else if (opt & EMERGE_UNMERGE)
552 + action = do_unmerge;
553 + else if (opt & EMERGE_QUERY)
554 + action = do_query;
555 + else if (opt & EMERGE_SYNC)
556 + action = do_sync;
557 + else if (opt & EMERGE_LIST)
558 + action = do_list;
559 + else if (opt & EMERGE_UPDATE)
560 + action = do_update;
561 + else if (opt & EMERGE_CLEAN)
562 + action = do_clean;
563 + else
564 + action = do_merge;
565 + }
566 +
567 + /* this adds the remainder of the arguments (i.e. the packages) reversed, so they
568 + are processed in the order specified (which may be important) */
569 + optend = argc - 1;
570 +
571 + while (optend >= optind) {
572 + if (strnlen(argv[optend], GEM_NAME_LEN) == GEM_NAME_LEN) {
573 + snprintf(msg, GEM_NAME_LEN, "Name too long: %s", argv[optend]);
574 + edie(msg);
575 + }
576 + tmppkg = decode_namespec(argv[optend]);
577 + if (!tmppkg) {
578 + snprintf(msg, GEM_NAME_LEN, "Unable to decode name %s", argv[optend]);
579 + edie(msg);
580 + }
581 + tmppkg->index_pkg=0;
582 + conf.packages = llist_add_to(conf.packages, (char *)tmppkg);
583 + optend--;
584 + ++conf.num_packages;
585 + }
586 +}
587 +
588 +/* all the functions that start with parse_ are for parsing the Index file */
589 +static void
590 +parse_pkgname_version(package_t* pkg)
591 +{
592 + int i = 0; char *c;
593 + while (msg[i] != ' ' && i < GEM_MLEN) ++i;
594 + ++i;
595 + if (i >= GEM_MLEN) edie("overflow!");
596 + c = &msg[i];
597 +keep_looking:
598 + while (msg[i] != '-' && i < GEM_MLEN) ++i;
599 + ++i;
600 + if (i >= GEM_MLEN) edie("overflow looking for version");
601 + if (!isdigit(msg[i])) goto keep_looking;
602 + strncpy(pkg->name, c, i-5);
603 + c = &msg[i];
604 + strncpy(pkg->version, c, GEM_NAME_LEN);
605 + i = strnlen(c, GEM_NAME_LEN);
606 + pkg->version[i-1]=0;
607 +}
608 +
609 +static void
610 +parse_category(package_t* pkg)
611 +{
612 + /* CATEGORY: sys-kernel */
613 + int i = 0; char *c;
614 + while (msg[i] != ' ') ++i;
615 + ++i;
616 + c = &msg[i];
617 + i = strnlen(c, GEM_NAME_LEN);
618 + c[i-1]=0;
619 + strncpy(pkg->category, c, i-1);
620 +}
621 +
622 +static void
623 +parse_depend_line(package_t* pkg, char* m)
624 +{
625 + /* just strchr our way through it--> use m, not msg! */
626 + char *c, *d, *i=m;
627 + do {
628 + c = strchr(i, ' ');
629 + if (c != NULL) {
630 + d = xmalloc(c-i+1);
631 + if (d == NULL) edie("failed to alloc space for depend");
632 + strncpy(d, i, c-i);
633 + pkg->depends = llist_add_to(pkg->depends, d);
634 + ++c; i = c;
635 + }
636 + } while (c != NULL);
637 + /* do the last one too */
638 + d = xmalloc(strlen(i)+1);
639 + strcpy(d, i);
640 + pkg->depends = llist_add_to(pkg->depends, d);
641 +
642 +}
643 +
644 +/* this is out in static-land because both parse_depends and parse_line need to use it */
645 +static FILE *index_file;
646 +static void
647 +parse_depends(package_t* pkg)
648 +{
649 + int saw_end_of_line = 0, len;
650 + char *m, *l;
651 + static char tmp[GEM_NAME_LEN];
652 + /* skip over the RDEPEND: part */
653 + if ((m = strchr(msg, ':')) == NULL) edie("No RDEPEND?");
654 + ++m; ++m;
655 + while (!saw_end_of_line) {
656 + len = strnlen(msg, GEM_MLEN);
657 + /* it's ok to use msg here ... */
658 + if (msg[len-1] == '\n') {
659 + saw_end_of_line = 1;
660 + msg[len-1]=0; /* cut that end of line off */
661 + /* but parsing the line parses on m */
662 + parse_depend_line(pkg, m);
663 + } else {
664 + /* also ok here */
665 + l = rindex(msg, ' '); /* space before the last word */
666 + ++l;
667 + strncpy(tmp, l, GEM_NAME_LEN); /* len - l ? */
668 + --l; *l=0; /* now the line parser can't see the partial word */
669 + parse_depend_line(pkg, m);
670 + strncpy(msg, tmp, GEM_NAME_LEN); /* now msg has the partial word */
671 + len = strnlen(msg, GEM_NAME_LEN);
672 + l = &msg[len];
673 + if (fgets(l, GEM_MLEN-len, index_file) == NULL) edie("unexpect end of file (deps");
674 + /* msg doesn't have RDEPEND anymore, so parse at beginning of msg */
675 + m = &msg[0];
676 + }
677 + }
678 +}
679 +
680 +static int
681 +parse_line(package_t* pkg)
682 +{ /* must return 1 when the whole package is ready, otherwise 0 (or edie()) */
683 + /* fortunately, the lines in each section have totally distinct beginnings! */
684 + if (msg[0] == '\n') return 0;
685 + if (msg[0] == 'P') parse_pkgname_version(pkg);
686 + else if (msg[0] == 'C' && msg[1] == 'A') parse_category(pkg);
687 + else if (msg[0] == 'C' && msg[1] == 'H') return 0; /* parse_chost(pkg); */
688 + else if (msg[0] == 'L') return 0; /* parse_license(pkg); */
689 + else if (msg[0] == 'C' && msg[1] == 'F') return 0; /* parse_cflags(pkg); */
690 + else if (msg[0] == 'R') parse_depends(pkg);
691 + else if (msg[0] == 'U') return 0; /* parse_use(pkg); */
692 + /* size is the very last entry, so it will return 1 */
693 + else if (msg[0] == 'S') return 1; /* parse_size(pkg); */
694 + else { edie("unexpected data in index file"); }
695 + return 0;
696 +}
697 +
698 +static int
699 +process_index(void)
700 +{
701 + package_t *pkg = 0;
702 +
703 + if ((index_file = bb_xfopen(conf.index, "r")) == 0)
704 + edie("Failed to open index file");
705 + if (conf.debug) einfo("*** *** Processing index file");
706 +
707 + while (feof(index_file) != 1) {
708 + /* can we get a line? */
709 + if (fgets(msg, GEM_MLEN, index_file) != NULL) {
710 + /* do we have a package? */
711 + if (!pkg) {
712 + pkg = (package_t*) malloc(sizeof(package_t));
713 + memset(pkg, 0, sizeof(package_t));
714 + }
715 + /* is the pkg already complete? */
716 + if (parse_line(pkg)==1) {
717 + /* finish package! */
718 + if (conf.verbose)
719 + print_pkg(pkg);
720 + pkg_info.packages = llist_add_to(pkg_info.packages, (char *) pkg);
721 + pkg = 0;
722 + ++pkg_info.num_packages;
723 + }
724 + }
725 + }
726 + if (ferror(index_file) != 0) {
727 + edie("encountered error while processing index file");
728 + }
729 + if (bb_fclose_nonstdin(index_file) != 0) return 1;
730 + if (conf.debug) { einfo("*** *** Done processing index file"); }
731 + return 0;
732 +}
733 +
734 +int
735 +emerge_main(int argc, char **argv)
736 +{
737 + char tmpp[GEM_PATH_MAX];
738 + unsigned int len; int i;
739 + llist_t *tp;
740 + package_t *package = 0;
741 +
742 + if (argc < 2)
743 + bb_show_usage();
744 +
745 + conf.verbose = 0;
746 + conf.debug = 0;
747 + conf.pretend = 0;
748 + conf.usepkgonly = 0;
749 + conf.packages = 0;
750 + conf.num_packages = 0;
751 + conf.index = 0;
752 +
753 + /* command line overrides env vars. */
754 + conf.pkgdir = getenv("PKGDIR");
755 + conf.binhost = getenv("PORTAGE_BINHOST");
756 + conf.tmpdir = getenv("PORTAGE_TMPDIR");
757 + conf.root = getenv("ROOT");
758 + conf.index = getenv("INDEX_FILE");
759 + conf.pkg_dbdir = getenv("PKG_DBDIR");
760 +
761 + parseargs(argc, argv);
762 +
763 + /* now configurations may have some items, and some not. function may exit */
764 +#undef FOO
765 +#define FOO "Try adding it to your /etc/make.conf, exporting it as an environment variable, or specifying it on the command line"
766 + /* verify that environment variables exist before going on, fix them on disk if necessary */
767 + if (conf.pkgdir == 0)
768 + edie("PKGDIR is not set. " FOO);
769 + if (conf.binhost == 0)
770 + edie("PORTAGE_BINHOST is not set. " FOO);
771 + if (conf.tmpdir == 0)
772 + edie("PORTAGE_TMPDIR is not set. " FOO);
773 + if (conf.root == 0)
774 + edie("ROOT is not set. " FOO);
775 + if (conf.index == 0)
776 + edie("INDEX is not set. " FOO);
777 +#undef FOO
778 + (void) tbz2pkg_init(&conf);
779 +
780 +
781 + /* make sure PKGDIR exists on disk */
782 + len = strnlen(conf.pkgdir, GEM_PATH_MAX);
783 + memset(tmpp, 0, sizeof(tmpp));
784 + strncpy(tmpp, conf.pkgdir, len);
785 + strncat(tmpp, "/All", GEM_PATH_MAX - 1 - len);
786 + if (!dir_exists(tmpp)) /* dir_exists checks R_OK, not W_OK...fix that later! */
787 + if (mkalldirs(tmpp) != 0)
788 + edie("FAILED to make PKGDIR/All");
789 +
790 + /* that's all for configuration; let's parse out the index */
791 + pkg_info.searchable_packages=0;
792 + pkg_info.num_packages=0;
793 + process_index();
794 +
795 + pkg_info.searchable_packages =
796 + (package_t**) xmalloc(pkg_info.num_packages*sizeof(package_t*));
797 + tp = pkg_info.packages;
798 + for (i = 0; i < pkg_info.num_packages; ++i) {
799 + pkg_info.searchable_packages[i] = (package_t*) tp->data;
800 + tp = tp->link;
801 + }
802 +
803 + /* now we are all set up. perform the action */
804 + tp = conf.packages;
805 + while (tp != 0) {
806 + package = (package_t*)tp->data;
807 + if (conf.debug) {
808 + bb_printf("handling package:\n");
809 + print_pkg(package);
810 + }
811 + package->index_pkg=find_in_index(package);
812 + if (action(package) != 0) {
813 + sprintf(msg, "Failed on package %s", package->name);
814 + edie(msg);
815 + }
816 + package = 0;
817 + tp = tp->link;
818 + }
819 +
820 + exit(EXIT_SUCCESS);
821 +}
822 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/gentoo_shared.c busybox/archival/gentoo_shared.c
823 --- busybox-1.00/archival/gentoo_shared.c 1970-01-01 01:00:00.000000000 +0100
824 +++ busybox/archival/gentoo_shared.c 2005-06-08 19:46:32.000000000 +0200
825 @@ -0,0 +1,297 @@
826 +/*
827 +* Distributed under the terms of the GNU General Public License v2
828 +* $Header: /home/collar_b/programs/cvs/busybox/archival/gentoo_shared.c,v 1.10 2005/06/08 17:46:32 collar_b Exp $
829 +*
830 +* gentoo_shared: functions that are used by both emerge.c and tbz2pkg.c
831 +* reason: emerge.c can depend (Config.on) on tbz2pkg, but not the other
832 +* direction too.
833 +*
834 +* Written by Benjamin Collar & Natanael Copa
835 +* Copyright (C) 2005, Benjamin Collar, Natanael Copa
836 +*
837 +********************************************************************
838 +* This program is free software; you can redistribute it and/or
839 +* modify it under the terms of the GNU General Public License as
840 +* published by the Free Software Foundation; either version 2 of the
841 +* License, or (at your option) any later version.
842 +*
843 +* This program is distributed in the hope that it will be useful, but
844 +* WITHOUT ANY WARRANTY; without even the implied warranty of
845 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
846 +* General Public License for more details.
847 +*
848 +* You should have received a copy of the GNU General Public License
849 +* along with this program; if not, write to the Free Software
850 +* Foundation, Inc., 59 Temple Place - Suite 330, Boston,
851 +* MA 02111-1307, USA.
852 +*/
853 +
854 +#include "gentoo_shared.h"
855 +#include <ctype.h>
856 +
857 +char msg[GEM_MLEN]; /* useful for einfo, etc--just a buffer */
858 +
859 +void
860 +einfo(char *str)
861 +{
862 + bb_fprintf(stdout, ">>> %s\n", str);
863 +}
864 +
865 +void
866 +eerror(char *str)
867 +{
868 + bb_fprintf(stderr, "!!! %s\n", str);
869 +}
870 +
871 +void
872 +edie(char *str)
873 +{
874 + eerror(str);
875 + bb_error_msg_and_die("Quitting...");
876 +}
877 +
878 +extern configurables_t conf;
879 +
880 +void
881 +print_pkg(const package_t* pkg)
882 +{
883 + llist_t* d=pkg->depends;
884 + bb_printf("Name: %s\n", pkg->name);
885 + bb_printf("Version: %s\n", pkg->version);
886 + bb_printf("Category: %s\n", pkg->category);
887 + if (conf.debug) {
888 + bb_printf("Modifiers: %s\n", pkg->modifier);
889 + }
890 + bb_printf("Depends: \n");
891 + while (d) {
892 + bb_printf(" %s\n", d->data);
893 + d = d->link;
894 + }
895 + bb_printf("\n");
896 +}
897 +
898 +int
899 +bfind_compare(const void* key, const void* try)
900 +{
901 + const package_t* query = (const package_t*) key;
902 + const package_t **potential = (const package_t**) try;
903 + int match=0;
904 + match = strcmp(query->name, (*potential)->name);
905 + if (match == 0) {
906 + if (conf.debug) { bb_printf("got a name match...\n"); }
907 + /* if the names match, check that we're in the same cateogry */
908 + if (query->category[0]) {
909 + match = strcmp(query->category, (*potential)->category);
910 + if (match != 0) {
911 + /* category doesn't match -- need resolution from user*/
912 + sprintf(msg, "Found package name in multiple categories: (given) %s, (found)"
913 + " %s Please resolve manually with emerge ... category/package\n",
914 + query->category, (*potential)->category);
915 + edie(msg);
916 + }
917 + if (conf.debug) { bb_printf("got a category match\n"); }
918 + /* if category does match, check version */
919 + }
920 + if (query->version[0]) {
921 + match = strcmp(query->version, (*potential)->version);
922 + if (query->modifier[0]) {
923 + if (conf.debug)
924 + bb_printf("modifier processing engaged!\n");
925 + if (query->modifier[1] == '=' && match == 0) goto stop;
926 + if (query->modifier[0] == '=' && match == 0) goto stop;
927 + if (query->modifier[0] == '>' && match == -1) { match = 0; goto stop; }
928 + if (query->modifier[0] == '<' && match == 1) { match = 0; goto stop; }
929 + }
930 + if (conf.debug && match == 0) bb_printf("got version match\n");
931 + }
932 + }
933 +stop:
934 + if (conf.debug) {
935 + bb_printf("compare result %d for\n", match);
936 + print_pkg(query);
937 + print_pkg(*potential);
938 + }
939 + return match;
940 +}
941 +
942 +typedef struct pkgsearch {
943 + llist_t* results;
944 + const package_t* package;
945 +} pkgsearch_t;
946 +
947 +int
948 +findPkgVerAction(const char* fileName, struct stat* statbuf, void* userData)
949 +{
950 + pkgsearch_t* sch = (pkgsearch_t*) userData;
951 + package_t* result;
952 + char *basec, *bname, *begin, *dirc, *dname;
953 + int retval=0, cmp;
954 + static char version[GEM_NAME_LEN];
955 + if (conf.debug) bb_printf("checking for pkg/version, got %s\n", fileName);
956 + version[0]=0;
957 + basec = strdup(fileName);
958 + bname = basename(basec);
959 + if (strstr(bname, sch->package->name) != bname) {
960 + free(basec);
961 + return 1;
962 + }
963 + /* get version from fileName */
964 + begin = bname;
965 + while (*begin != 0) {
966 + if (*begin == '-') {
967 + ++begin;
968 + if (isdigit(*begin)) {
969 + strcpy(version, begin);
970 + break;
971 + }
972 + } else {
973 + ++begin;
974 + }
975 + }
976 + cmp = strcmp(version, sch->package->version);
977 + if (
978 + (sch->package->modifier[1] == '=' && cmp == 0) ||
979 + (sch->package->modifier[0] == '<' && cmp == -1) ||
980 + (sch->package->modifier[0] == '>' && cmp == 1) ||
981 + (sch->package->modifier[0] == '>' && cmp == 1) ||
982 + (!sch->package->modifier[0]) /* && cmp == 0) */
983 + )
984 + {
985 + result = xmalloc(sizeof(package_t));
986 + memset(result, 0, sizeof(package_t));
987 + /* we can copy the given package, as long as we clear the modifiers */
988 + memcpy(result, sch->package, sizeof(package_t));
989 + result->modifier[0]=0; result->modifier[1]=0;
990 + strcpy(result->version, version);
991 + strcpy(result->disk_location, fileName);
992 + if (conf.debug) { bb_printf("found disk entry %s", result->disk_location); }
993 + /* get the directory name == category */
994 + dirc = strdup(fileName);
995 + dname = dirname(dirc);
996 + free(basec);
997 + basec = strdup(dname);
998 + bname = basename(basec);
999 + strcpy(result->category, bname);
1000 + if (conf.debug) { bb_printf("found disk package \n"); print_pkg(result); }
1001 + free(dirc);
1002 + sch->results = llist_add_to(sch->results, (char*) result);
1003 + retval = 0;
1004 + } else {
1005 + retval = 1;
1006 + }
1007 +
1008 + free(basec);
1009 + return retval;
1010 +
1011 +}
1012 +
1013 +/* the result* list for this function is a (single) directory name--the one that matches! */
1014 +int
1015 +findCatAction(const char* fileName, struct stat* statbuf, void* userData)
1016 +{
1017 + pkgsearch_t* sch = (pkgsearch_t*) userData;
1018 + char *basec, *bname;
1019 + int retval;
1020 + if (conf.debug) bb_printf("checking for category, got %s\n", fileName);
1021 + basec = strdup(fileName);
1022 + bname = basename(basec);
1023 + if (strcmp(bname, sch->package->category) == 0) {
1024 + /* this is our man */
1025 + /* recur from here into pkgs directory, look for right name (only 1) */
1026 + recursive_action(fileName, 1, 0, 0, 0, findPkgVerAction, userData);
1027 + retval=0;
1028 + } else {
1029 + retval=1;
1030 + }
1031 + free(basec);
1032 + return retval;
1033 +}
1034 +
1035 +llist_t*
1036 +find_package_on_disk(const package_t* package)
1037 +{
1038 +/* int recursive_action(const char *fileName, int recurse,
1039 + int followLinks, int depthFirst,
1040 + int (*fileAction) (const char *fileName, struct stat* statbuf, void* userData),
1041 + int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData),
1042 + void* userData);
1043 +*/
1044 + static pkgsearch_t ps; ps.results=0; ps.package=package;
1045 + if (!*package->category)
1046 + recursive_action(conf.pkg_dbdir, 1, 0, 0, 0, findPkgVerAction, (void*) &ps);
1047 + else
1048 + recursive_action(conf.pkg_dbdir, 1, 0, 0, 0, findCatAction, (void*) &ps);
1049 + return ps.results;
1050 +}
1051 +
1052 +package_t*
1053 +decode_namespec(char* namespec)
1054 +{
1055 + /* on the command line or in the Package-depends, the form of a specification is
1056 + * emerge abc
1057 + * cat/abc
1058 + * >=cat/abc-1.3.4.5
1059 + * this function has to split all that up and return a new package_t (in char form)
1060 + */
1061 + package_t* ret = xmalloc(sizeof(package_t));
1062 + char *slash, *begin, first = namespec[0];
1063 + int provided_modifiers=0, provided_category=0;
1064 + memset(ret, 0, sizeof(package_t));
1065 + if (conf.debug) { sprintf(msg, "decoding namespec %s\n", namespec); einfo(msg); }
1066 + switch (first) {
1067 + case '>': case '<': case '=': case '!':
1068 + {
1069 + /* handle modifiers */
1070 + provided_modifiers=1;
1071 + ret->modifier[0]=first;
1072 + first = namespec[1];
1073 + if (first == '>' || first == '<' || first == '=' || first == '!') {
1074 + ret->modifier[1] = first;
1075 + begin = &namespec[2];
1076 + } else {
1077 + begin = &namespec[1];
1078 + }
1079 + break;
1080 + }
1081 + default:
1082 + begin = &namespec[0];
1083 + }
1084 + /* is there a slash (and thus a category & name?) */
1085 + /* process category */
1086 + if ((slash = strchr(begin, '/')) != 0) {
1087 + /* we have a slash, so there must be a category */
1088 + provided_category=1;
1089 + *slash = 0;
1090 + strcpy(ret->category, begin);
1091 + ++slash;
1092 + begin = slash;
1093 + } else {
1094 + slash = begin;
1095 + }
1096 + /* process name & version */
1097 + while (*begin != 0) {
1098 + if (*begin == '-') {
1099 + ++begin;
1100 + if (isdigit(*begin)) {
1101 + strcpy(ret->version, begin);
1102 + --begin;
1103 + *begin = 0;
1104 + break;
1105 + } else {
1106 + continue;
1107 + }
1108 + }
1109 + ++begin;
1110 + }
1111 + strcpy(ret->name, slash);
1112 + if (conf.debug) {
1113 + bb_printf("Decoded namespec to \n");
1114 + print_pkg(ret);
1115 + }
1116 + if (provided_modifiers && !provided_category) {
1117 + sprintf(msg, "If you use =!<>, you must specify category/app. error: %s", namespec);
1118 + edie(msg);
1119 + }
1120 +
1121 + return ret;
1122 +}
1123 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/gentoo_shared.h busybox/archival/gentoo_shared.h
1124 --- busybox-1.00/archival/gentoo_shared.h 1970-01-01 01:00:00.000000000 +0100
1125 +++ busybox/archival/gentoo_shared.h 2005-06-08 20:16:26.000000000 +0200
1126 @@ -0,0 +1,83 @@
1127 +/*
1128 +* Distributed under the terms of the GNU General Public License v2
1129 +* $Header: /home/collar_b/programs/cvs/busybox/archival/gentoo_shared.h,v 1.8 2005/06/08 17:46:32 collar_b Exp $
1130 +*
1131 +* stuff that is shared between emerge.c and tbz2pkg.c
1132 +*
1133 +* Written by Benjamin Collar
1134 +* Copyright (C) 2005, Benjamin Collar
1135 +*
1136 +********************************************************************
1137 +* This program is free software; you can redistribute it and/or
1138 +* modify it under the terms of the GNU General Public License as
1139 +* published by the Free Software Foundation; either version 2 of the
1140 +* License, or (at your option) any later version.
1141 +*
1142 +* This program is distributed in the hope that it will be useful, but
1143 +* WITHOUT ANY WARRANTY; without even the implied warranty of
1144 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1145 +* General Public License for more details.
1146 +*
1147 +* You should have received a copy of the GNU General Public License
1148 +* along with this program; if not, write to the Free Software
1149 +* Foundation, Inc., 59 Temple Place - Suite 330, Boston,
1150 +* MA 02111-1307, USA.
1151 +*/
1152 +
1153 +#ifndef GENTOO_SHARED_H
1154 +#define GENTOO_SHARED_H
1155 +
1156 +#include "busybox.h"
1157 +#include <string.h>
1158 +#include <stdio.h>
1159 +
1160 +#define GEM_PATH_MAX (PATH_MAX)
1161 +#define GEM_NAME_LEN (32)
1162 +#define GEM_MLEN (GEM_PATH_MAX+64)
1163 +
1164 +typedef struct configurables_s {
1165 + char *root;
1166 + char *index;
1167 + char *tmpdir;
1168 + char *binhost;
1169 + char *pkgdir;
1170 + char *pkg_dbdir; /* used by tbz2pkg only */
1171 + char *install_mask; /* used by tbz2pkg only */
1172 + int verbose;
1173 + int debug;
1174 + int pretend;
1175 + int usepkgonly;
1176 + llist_t *packages; /* the package-names given on the command line */
1177 + int num_packages; /* the number of package-names given */
1178 +} configurables_t;
1179 +
1180 +
1181 +typedef struct package_s {
1182 + llist_t *depends;
1183 + struct package_s* index_pkg;
1184 + char modifier[3]; /* things like >=, =, !, ~, etc that portage uses */
1185 + char version[GEM_NAME_LEN];
1186 + char category[GEM_NAME_LEN];
1187 + char name[GEM_NAME_LEN];
1188 + char disk_location[GEM_PATH_MAX];
1189 +} package_t;
1190 +
1191 +
1192 +extern char msg[GEM_MLEN]; /* useful for einfo, etc--just a buffer */
1193 +void einfo(char* s); /* write some info, emerge style */
1194 +void eerror(char* s); /* write an error, emerge style */
1195 +void edie(char* s); /* write the error, then exit */
1196 +
1197 +/* use this function to bsearch the pkg_info.packages array (all packages in index)
1198 + * or to just compare two package_t's
1199 + * key is a package_t* you are searching for, try is package_t** -- a possible match
1200 + * this function is used in bsearch, but may also be used to compare two package_t's;
1201 + * it returns -1,0,1 as one would expect */
1202 +int bfind_compare(const void* key, const void* try);
1203 +void print_pkg(const package_t* pkg);
1204 +package_t* decode_namespec(char* namespec);
1205 +/* finds an installed package (or multiple, if modifiers are used) */
1206 +llist_t* find_package_on_disk(const package_t* package);
1207 +
1208 +
1209 +#endif
1210 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/tbz2pkg.c busybox/archival/tbz2pkg.c
1211 --- busybox-1.00/archival/tbz2pkg.c 1970-01-01 01:00:00.000000000 +0100
1212 +++ busybox/archival/tbz2pkg.c 2005-06-08 20:17:34.000000000 +0200
1213 @@ -0,0 +1,945 @@
1214 +/*
1215 + * tbz2pkg, an installer for gentoo binary packages (tbz2)
1216 + * $Header: /home/collar_b/programs/cvs/busybox/archival/tbz2pkg.c,v 1.13 2005/06/08 18:17:34 collar_b Exp $
1217 + *
1218 + * Written by Natanael Copa
1219 + * Copyright (C) 2005 by Natanael Copa
1220 + * Extended by Benjamin Collar
1221 + * Parts Copyright (C) 2005 by Benjamin Collar
1222 + *
1223 + * Distributed under the terms of the GNU General Public License v2
1224 + *
1225 + * This program is free software; you can redistribute it and/or
1226 + * modify it under the terms of the GNU General Public License as
1227 + * published by the Free Software Foundation; either version 2 of the
1228 + * License, or (at your option) any later version.
1229 + *
1230 + * This program is distributed in the hope that it will be useful, but
1231 + * WITHOUT ANY WARRANTY; without even the implied warranty of
1232 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1233 + * General Public License for more details.
1234 + *
1235 + * You should have received a copy of the GNU General Public License
1236 + * along with this program; if not, write to the Free Software
1237 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
1238 + * MA 02111-1307, USA.
1239 + */
1240 +
1241 +#include "busybox.h"
1242 +#include <stdlib.h>
1243 +#include <stdio.h>
1244 +#include <string.h>
1245 +#include <assert.h>
1246 +#include <unistd.h>
1247 +#include <sys/stat.h>
1248 +#include <sys/types.h>
1249 +#include <fcntl.h>
1250 +
1251 +#include <unarchive.h>
1252 +#include <getopt.h>
1253 +#include <errno.h>
1254 +
1255 +#include "tbz2pkg.h"
1256 +
1257 +
1258 +#define BUFSIZE 32768
1259 +#define PREBUFSIZE 7
1260 +
1261 +/* if BUFSIZE is smaller than XPACKSIZE, strange things could happen */
1262 +#if (XPAKSIZE >= BUFSIZE)
1263 +#error BUFSIZE must be bigger than XPAKSIZE
1264 +#endif
1265 +
1266 +#define INDEX_LEN_OFFSET 8
1267 +#define DATA_LEN_OFFSET (INDEX_LEN_OFFSET + 4)
1268 +#define INDEX_OFFSET (DATA_LEN_OFFSET + 4)
1269 +
1270 +/* if the XPAK is bigger than 8MiB, something is wrong */
1271 +#define FAR_TOO_BIG_XPAK (8192 * 1024)
1272 +
1273 +#define CHECKSUM_ALGO HASH_SHA1
1274 +
1275 +typedef struct xpak_s {
1276 + char *buffer; /* the raw xpak buffer */
1277 + int size;
1278 + int index_len;
1279 + int data_len;
1280 + int data_offset;
1281 +} xpak_t;
1282 +
1283 +static configurables_t *conf;
1284 +static char prebuf[PREBUFSIZE];
1285 +static int presize=0; /* how much is in the prebuf? */
1286 +static char root_dbdir[256];
1287 +static char root_dbdir_pkg[256];
1288 +static char root_dbdir_pkg_contents[256];
1289 +
1290 +static struct option const tbz2pkg_long_options[] = {
1291 + {"debug", 0, 0, 'd'},
1292 + {"help", 0, 0, 'h'},
1293 + {"install", 0, 0, 'i'},
1294 + {"install-mask", 1, 0, 'M'},
1295 + {"list", 0, 0, 'L'},
1296 + {"pretend", 0, 0, 'p'},
1297 + {"purge", 0, 0, 'P'},
1298 + {"root", 1, 0, 'R'},
1299 + {"verbose", 0, 0, 'v'},
1300 + {NULL, 0, NULL, 0}
1301 +};
1302 +
1303 +#define TBZ2PKG_DEBUG (1<<0)
1304 +#define TBZ2PKG_HELP (1<<1)
1305 +#define TBZ2PKG_INSTALL (1<<2)
1306 +#define TBZ2PKG_INSTALL_MASK (1<<3)
1307 +#define TBZ2PKG_LIST (1<<4)
1308 +#define TBZ2PKG_PRETEND (1<<5)
1309 +#define TBZ2PKG_PURGE (1<<6)
1310 +#define TBZ2PKG_ROOT (1<<7)
1311 +#define TBZ2PKG_VERBOSE (1<<8)
1312 +
1313 +
1314 +#define OPTIONS_FLAGS "dhiM:LpPR:v"
1315 +
1316 +
1317 +configurables_t *tbz2pkg_init(configurables_t *myconf) {
1318 + snprintf(root_dbdir, sizeof(root_dbdir), "%s", myconf->pkg_dbdir);
1319 + return conf = myconf;
1320 +}
1321 +
1322 +char *set_dbdir_pkg_char(const char *pkg) {
1323 + snprintf(root_dbdir_pkg, sizeof(root_dbdir_pkg), "%s/%s", root_dbdir, pkg);
1324 + strncpy(root_dbdir_pkg_contents, root_dbdir_pkg,
1325 + sizeof(root_dbdir_pkg_contents));
1326 + strncat(root_dbdir_pkg_contents, "/CONTENTS",
1327 + sizeof(root_dbdir_pkg_contents));
1328 + return root_dbdir_pkg;
1329 +}
1330 +
1331 +char *set_dbdir_pkg(package_t *pkg) {
1332 + snprintf(root_dbdir_pkg, sizeof(root_dbdir_pkg), "%s", pkg->disk_location);
1333 + strncpy(root_dbdir_pkg_contents, root_dbdir_pkg,
1334 + sizeof(root_dbdir_pkg_contents));
1335 + strncat(root_dbdir_pkg_contents, "/CONTENTS",
1336 + sizeof(root_dbdir_pkg_contents));
1337 + return root_dbdir_pkg;
1338 +}
1339 +
1340 +/*
1341 + convert a 4 byte big endian to int
1342 +*/
1343 +static int decodeint(const unsigned char *ptr) {
1344 + return (((int)ptr[0]) << 24) + (((int)ptr[1]) << 16)
1345 + + (((int)ptr[2]) << 8) + ((int)ptr[3]);
1346 +}
1347 +
1348 +
1349 +
1350 +/*
1351 + return a string with the absolute path.
1352 +*/
1353 +char *mk_absolute_path(const char *path) {
1354 + static char fullpath[256]; /* maximun length of path */
1355 + if (strncmp(path, "./", 2) == 0)
1356 + snprintf(fullpath, sizeof(fullpath), "%s%s", conf->root, path+1);
1357 + else
1358 + snprintf(fullpath, sizeof(fullpath), "%s%s%s",
1359 + conf->root, (path[0] != '/') ? "/" : "", path);
1360 + return fullpath;
1361 +}
1362 +
1363 +
1364 +/* Creates a full path from a dbfile in the pkgdb */
1365 +char *mk_dbdir_path(const char *package, const char *dbfile) {
1366 + static char path[256];
1367 + snprintf(path, sizeof(path), "%s/%s/%s",
1368 + mk_absolute_path(conf->pkg_dbdir), package, dbfile);
1369 + return path;
1370 +}
1371 +
1372 +/*
1373 + finds the specified index and returns the value in a allocated buffer
1374 +*/
1375 +static char *xpak_get_data(const xpak_t *xpak, const char *key) {
1376 + int len;
1377 + int offset=INDEX_OFFSET;
1378 + char *pathname;
1379 + while (offset < xpak->index_len) {
1380 + if (offset + 4 > xpak->size)
1381 + bb_error_msg_and_die("xpak_get_data: problems with XPAK");
1382 + len = decodeint(xpak->buffer + offset);
1383 +
1384 + /* pathname_len + pathname + data_offset + data_len */
1385 + if (offset + 4 + len + 4 + 4 > xpak->size)
1386 + return NULL;
1387 + pathname = bb_xstrndup(xpak->buffer + offset + 4, len);
1388 + if (strcmp(pathname, key) == 0) {
1389 + int data_offset = decodeint(xpak->buffer + offset + 4 + len);
1390 + int data_len = decodeint(xpak->buffer + offset + 8 + len);
1391 + char *p = xmalloc(data_len+1);
1392 + if (data_offset >= 0 && ((data_offset + data_len) < xpak->size)) {
1393 + memcpy(p, xpak->buffer + xpak->data_offset + data_offset,
1394 + data_len);
1395 + p[data_len] = '\0';
1396 + free(pathname);
1397 + return(p);
1398 + } else {
1399 + bb_error_msg_and_die("xpak_get_data: data_offset=%i, "
1400 + "data_offset+data_len=%i, xpak->size=%i",
1401 + data_offset, data_offset + data_len,
1402 + xpak->size);
1403 + }
1404 + }
1405 + free(pathname);
1406 + offset += 12 + len;
1407 + }
1408 + /* not found */
1409 + return NULL;
1410 +}
1411 +
1412 +/*
1413 + allocate space for xpak->buffer and get the rest of the data stream
1414 + memory is allocated and pointer is stored in global variable xpak.
1415 +
1416 + returns the size of xpak or -1 on error.
1417 +*/
1418 +int get_xpak(int src_fd, xpak_t *xpak, const char *restbuf, size_t restsize) {
1419 + static char readbuf[BUFSIZE];
1420 + int total = restsize;
1421 + int numread;
1422 +
1423 + xpak->buffer = xmalloc(restsize);
1424 + memcpy(xpak->buffer, restbuf, restsize);
1425 +
1426 + /* dump the rest of the data */
1427 + while((numread = read(src_fd, readbuf, BUFSIZE))) {
1428 + char *p;
1429 + p = xrealloc(xpak->buffer, total + numread);
1430 + xpak->buffer = p;
1431 + memcpy(xpak->buffer + total, readbuf, numread);
1432 + total += numread;
1433 + /* check that the XPAK is not unreasonable big */
1434 + if (total > FAR_TOO_BIG_XPAK) {
1435 + bb_error_msg_and_die("XPAK is unreasonable big. Giving up.");
1436 + }
1437 + }
1438 + xpak->size = total - 8; /* we skip the trailing [4 byte offset]STOP */
1439 + if (xpak->size < (INDEX_OFFSET + 8)) { /* the +8 is for the "XPAKSTOP" */
1440 + /* something is definitively wrong */
1441 + free(xpak->buffer);
1442 + xpak->buffer = NULL;
1443 + return -1;
1444 + }
1445 +
1446 + xpak->index_len = decodeint(xpak->buffer + INDEX_LEN_OFFSET);
1447 + xpak->data_len = decodeint(xpak->buffer + DATA_LEN_OFFSET);
1448 + xpak->data_offset = INDEX_OFFSET + xpak->index_len;
1449 + if (xpak->data_offset + xpak->data_len + 8 > xpak->size) {
1450 + /* we are out of buffer */
1451 + free(xpak->buffer);
1452 + xpak->buffer=NULL;
1453 + return -1;
1454 + }
1455 + return xpak->size;
1456 +}
1457 +
1458 +/*
1459 + flush buffer to file
1460 +*/
1461 +int flush_buf(const char *buf, int size, const char *filename) {
1462 + FILE *outfile;
1463 + int written;
1464 + outfile = bb_xfopen(filename, "w");
1465 + written = fwrite(buf, 1, size, outfile);
1466 + bb_fclose_nonstdin(outfile);
1467 + return written;
1468 +}
1469 +
1470 +/*
1471 + read data from src_fd until we find a "XPAKPACK" string
1472 + returns numbers of byte available in buffer
1473 +*/
1474 +int read_to_xpak(int src_fd, char *buf, size_t size, xpak_t *xpak) {
1475 + int n;
1476 + int count, rest, avail;
1477 + char *p=buf;
1478 + char *found = NULL;
1479 +
1480 + memcpy(buf, prebuf, presize);
1481 + n = size - presize; /* this is what we want to read */
1482 + assert(n>0);
1483 + if ((count = bb_xread(src_fd, buf+presize, n) + presize) == 0) {
1484 + /* we are done reading from file. no more data */
1485 + return 0;
1486 + }
1487 + presize=0;
1488 + avail=count;
1489 + rest=count;
1490 + while( ((p=memchr(p, 'X', rest)) != NULL) && (found == NULL) && rest) {
1491 + avail = p - buf; /* data available in buffer that is guaranteed to
1492 + not contain "XPAKPACK" */
1493 + rest = count - avail; /* the amount of data we need to examine */
1494 + if ( rest < PREBUFSIZE ) {
1495 + /*
1496 + we found 'X' but we don't have enough data left to check
1497 + the entire string. We save the data in prebuf for next read
1498 + */
1499 + memcpy(prebuf, p, rest);
1500 + presize=rest;
1501 + return avail;
1502 + }
1503 +
1504 + if (strncmp(p, "XPAKPACK", rest)==0) {
1505 + /* ok guy's, we found the XPAKPACK */
1506 + found=p;
1507 + if (get_xpak(src_fd, xpak, p, rest)<0)
1508 + bb_error_msg_and_die("Problems with XPAK\n");
1509 + if (conf->debug) flush_buf(xpak->buffer, xpak->size, "/tmp/xpak");
1510 + return avail;
1511 + }
1512 +
1513 + /* nothing here to see folks, move on (no XPAKPACK found)*/
1514 + p++;
1515 + rest--;
1516 + }
1517 + if (avail == 0 && found == NULL) {
1518 + bb_error_msg_and_die("No XPACK was found. sorry...\n");
1519 + }
1520 + return count;
1521 +}
1522 +
1523 +
1524 +/*
1525 + Extrackt the package from xpak. (CATEGORY/PF)
1526 +*/
1527 +char* xpak_get_package(xpak_t *xpak, char *package, int packagemax) {
1528 + char *cat, *pf, *eol;
1529 +
1530 + if ((cat = xpak_get_data(xpak, "CATEGORY")) == NULL)
1531 + bb_error_msg_and_die("CATEGORY not found in XPAK");
1532 +
1533 + if ((pf = xpak_get_data(xpak, "PF")) == NULL)
1534 + bb_error_msg_and_die("PF not found in XPAK");
1535 +
1536 + if ((eol = strchr(cat, '\n')) != NULL) *eol = '\0';
1537 + if ((eol = strchr(pf, '\n')) != NULL) *eol = '\0';
1538 + snprintf(package, packagemax, "%s/%s", cat, pf);
1539 + free(cat);
1540 + free(pf);
1541 + return package;
1542 +}
1543 +
1544 +/*
1545 + unpack the xpak to path
1546 +*/
1547 +int xpak_unpack(xpak_t *xpak, char *package, int package_bufsize) {
1548 + int len;
1549 + int offset=INDEX_OFFSET;
1550 + char *pathname;
1551 + int data_offset, data_len;
1552 +
1553 + xpak_get_package(xpak, package, package_bufsize);
1554 + set_dbdir_pkg_char(package);
1555 + bb_make_directory(root_dbdir_pkg, -1, FILEUTILS_RECUR);
1556 +
1557 + while (offset < xpak->index_len) {
1558 + char *key;
1559 + if (offset + 4 > xpak->size) return -1;
1560 + len = decodeint(xpak->buffer + offset);
1561 +
1562 + /* pathname_len + pathname + data_offset + data_len */
1563 + if (offset + 4 + len + 4 + 4 > xpak->size) return -1;
1564 + if ((key = xmalloc(len + 1))==NULL) return -1;
1565 + memcpy(key, xpak->buffer + offset + 4, len);
1566 + key[len] = '\0';
1567 + pathname = mk_dbdir_path(package, key);
1568 + free(key);
1569 +
1570 + data_offset = decodeint(xpak->buffer + offset + 4 + len);
1571 + data_len = decodeint(xpak->buffer + offset + 8 + len);
1572 +
1573 + if ((data_offset >= 0) && ((data_offset + data_len) < xpak->size)) {
1574 + int i;
1575 + if (conf->debug) {
1576 + /* unpack the xpak if we are in debug mode */
1577 + if (conf->pretend)
1578 + i = xpak->size;
1579 + else
1580 + i = flush_buf(xpak->buffer
1581 + + xpak->data_offset
1582 + + data_offset,
1583 + data_len, pathname);
1584 + if (conf->verbose)
1585 + bb_printf("Writing %s: %i bytes\n", pathname, i);
1586 + }
1587 + } else {
1588 + bb_error_msg_and_die("Invalid XPAK.");
1589 + }
1590 + offset += 12 + len;
1591 + }
1592 + return 0;
1593 +}
1594 +
1595 +/*
1596 + create a rejectlist for tar from install_mask
1597 +*/
1598 +llist_t *create_reject_list(const char *install_mask) {
1599 + char *tok, *mask;
1600 + llist_t *list = NULL;
1601 +
1602 + mask = bb_xstrdup(install_mask);
1603 + /* according to man 3 strtok: "Never use these functions."
1604 + any alternative how this can easily be solved? */
1605 + tok=strtok(mask, " ");
1606 + while(tok != NULL) {
1607 + list = llist_add_to(list, bb_xstrdup(tok));
1608 + if (conf->debug) bb_printf("create_reject_list: added %s\n", tok);
1609 + tok = strtok(NULL, " ");
1610 + }
1611 + free(mask);
1612 + return list;
1613 +}
1614 +
1615 +/*
1616 + for debugging stuff
1617 +*/
1618 +void dump_llist(llist_t *list) {
1619 + llist_t *item;
1620 + item = list;
1621 + while (item != NULL) {
1622 + bb_printf("dump_llist: %s\n", item->data);
1623 + item = item->link;
1624 + }
1625 +}
1626 +
1627 +/*
1628 + this func unpacks (tar -xj) whatever comes from src_fd,
1629 + return the filelist to ret_fd and exit.
1630 + the XPAK is supposed to be stripped off from src_fd
1631 +*/
1632 +void tbz2_unpack_files(int src_fd, int ret_fd, const char *root_dir,
1633 + const char *install_mask) {
1634 + archive_handle_t *tar_handle = init_handle();
1635 + llist_t *item;
1636 +
1637 + tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL;
1638 +
1639 + if (install_mask != NULL) {
1640 + if (conf->debug)
1641 + bb_printf("creating reject list from '%s'\n", install_mask);
1642 + tar_handle->reject = create_reject_list(install_mask);
1643 + tar_handle->filter = filter_accept_reject_list;
1644 + if (conf->debug)
1645 + dump_llist(tar_handle->reject);
1646 + }
1647 + tar_handle->src_fd = src_fd;
1648 + tar_handle->seek = seek_by_char;
1649 + if (! conf->pretend) tar_handle->action_data = data_extract_all;
1650 +
1651 + if (root_dir != NULL) {
1652 + if (chdir(root_dir))
1653 + bb_perror_msg_and_die("Couldnt chdir to %s", root_dir);
1654 + } else {
1655 + if (chdir("/"))
1656 + bb_perror_msg_and_die("Couldnt chdir to /");
1657 + }
1658 +
1659 + while (get_header_tar_bz2(tar_handle) == EXIT_SUCCESS);
1660 +
1661 + /* return the filelist */
1662 + for (item = tar_handle->passed; item != NULL; item = item->link) {
1663 + if (item->data != NULL) {
1664 + write(ret_fd, item->data, strlen(item->data));
1665 + write(ret_fd, "\0", 1);
1666 + }
1667 + }
1668 +}
1669 +
1670 +
1671 +/* those funcs are copied from coreutils/md5_sha1_sum.c */
1672 +/* This might be useful elsewhere */
1673 +static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
1674 + unsigned char hash_length)
1675 +{
1676 + int x, len, max;
1677 + unsigned char *hex_value;
1678 +
1679 + max = (hash_length * 2) + 2;
1680 + hex_value = xmalloc(max);
1681 + for (x = len = 0; x < hash_length; x++) {
1682 + len += snprintf(hex_value + len, max - len, "%02x", hash_value[x]);
1683 + }
1684 + return (hex_value);
1685 +}
1686 +
1687 +static uint8_t *hash_file(const char *filename, uint8_t hash_algo)
1688 +{
1689 + static uint8_t hash_value_bin[22];
1690 + uint8_t *hash_value = NULL;
1691 + uint8_t hash_length;
1692 + int src_fd;
1693 +
1694 + src_fd = open(filename, O_RDONLY);
1695 +
1696 + if (hash_algo == HASH_MD5) {
1697 + hash_length = 16;
1698 + } else {
1699 + hash_length = 20;
1700 + }
1701 +
1702 + /* hash_value_bin = xmalloc(hash_length); */
1703 +
1704 + if ((src_fd != -1) && (hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2)) {
1705 + hash_value = hash_bin_to_hex(hash_value_bin, hash_length);
1706 + } else {
1707 + bb_perror_msg("%s", filename);
1708 + }
1709 + close(src_fd);
1710 + return(hash_value);
1711 +}
1712 +
1713 +
1714 +/*
1715 + write a line to CONTENTS file
1716 + returns size of file
1717 +*/
1718 +int write_line_to_contents(FILE *outf, const char *line) {
1719 + struct stat info;
1720 + char *fullpath = mk_absolute_path(line);
1721 +
1722 + /* append to CONTENTS */
1723 + if (!conf->pretend) {
1724 + /* first we check if it is a link */
1725 + if (lstat(fullpath, &info) < 0) {
1726 + bb_perror_msg(fullpath);
1727 + return 0;
1728 + }
1729 + if (S_ISLNK(info.st_mode)) {
1730 + static char linkbuf[256];
1731 + readlink(fullpath, linkbuf, sizeof(linkbuf));
1732 + bb_fprintf(outf, "sym %s -> %s %i\n", line, linkbuf,
1733 + (int)info.st_mtime);
1734 + } else if (S_ISDIR(info.st_mode)) {
1735 + bb_fprintf(outf, "dir %s\n", line);
1736 + } else if (S_ISREG(info.st_mode)) {
1737 +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
1738 + char *hash = hash_file(fullpath, CHECKSUM_ALGO);
1739 + bb_fprintf(outf, "obj %s %s %i\n", line, hash, (int)info.st_mtime);
1740 + free(hash);
1741 +#else
1742 + bb_fprintf(outf, "obj %s %i %i\n", line, (int)info.st_size,
1743 + (int)info.st_mtime);
1744 +#endif
1745 + } else {
1746 + bb_fprintf(outf, "??? %s - %i\n", line,
1747 + (int)info.st_mtime);
1748 + }
1749 + }
1750 + if (conf->verbose) bb_printf(">>> %s\n", line);
1751 + return info.st_size;
1752 +}
1753 +
1754 +/*
1755 + generate a CONTENTS file
1756 + returns numbers of lines written.
1757 +*/
1758 +int create_contents(int child_fd, const char *package) {
1759 + static char line[1024]; /* read buf */
1760 + char c;
1761 + int i = 0, line_count = 0, total_size = 0;
1762 + FILE *outf;
1763 +
1764 + set_dbdir_pkg_char(package);
1765 + bb_make_directory(root_dbdir_pkg, -1, FILEUTILS_RECUR);
1766 +
1767 + if (conf->root != NULL) chdir(conf->root); else chdir("/");
1768 +
1769 + /* open the CONTENTS file */
1770 + outf = bb_xfopen(root_dbdir_pkg_contents, "w");
1771 +
1772 + /* now we can read the file list from child */
1773 + /* we reuse the buffer */
1774 + while (read(child_fd, &c, 1)) {
1775 + if (i < (sizeof(line)-1)) /* buffer size checking... */
1776 + line[i++] = c;
1777 + else
1778 + line[i] = '\0';
1779 + if (c == '\0') {
1780 + total_size += write_line_to_contents(outf, line);
1781 + line_count++;
1782 + i = 0;
1783 + }
1784 + }
1785 + if (!conf->pretend) fclose(outf);
1786 + return total_size;
1787 +}
1788 +
1789 +/* insert a string to llist, sorted by length. longest first */
1790 +/* this code has to be buggy.... */
1791 +llist_t *llist_insert_length_sorted(llist_t *list, char *data) {
1792 + llist_t *prev=NULL, *cur = list;
1793 + int data_len = strlen(data);
1794 +
1795 + /* if this is the first element, create a new list */
1796 + if (list == NULL) return llist_add_to(list, data);
1797 +
1798 + while ((cur != NULL) && (strlen(cur->data) > data_len)) {
1799 + prev = cur;
1800 + cur = cur->link;
1801 + }
1802 + if (cur == NULL) { /* this was the shortest, append to the end */
1803 + prev->link = llist_add_to(NULL, data);
1804 + } else {
1805 + if (prev != NULL)
1806 + prev->link = llist_add_to(cur, data);
1807 + else
1808 + return llist_add_to(cur, data);
1809 + }
1810 + return list;
1811 +}
1812 +
1813 +/*
1814 + check if path is a link that points to dest.
1815 + returns nonzero if the dest correspond to the link.
1816 +*/
1817 +int check_link(const char *path, const char *dest) {
1818 + static char buf[256];
1819 + if (readlink(path, buf, sizeof(buf)) < 0) return 0;
1820 + return (strcmp(buf, dest) == 0);
1821 +}
1822 +
1823 +/*
1824 + umerge a symlink.
1825 + modifies the passed "string". (replaces all spaces with \0 t be exact)
1826 +*/
1827 +int unmerge_sym(char *string) {
1828 + char *type = strtok(string, " ");
1829 + char *path = strtok(NULL, " ");
1830 + char *dest;
1831 + time_t mtime;
1832 + char *fullpath = mk_absolute_path(path);
1833 + static struct stat info;
1834 +
1835 + strtok(NULL, " "); /* get the '->' */
1836 + dest = strtok(NULL, " ");
1837 + mtime = atoi(strtok(NULL, " "));
1838 + if (type == NULL || path == NULL) return 0;
1839 +
1840 + lstat(fullpath, &info);
1841 + if (check_link(fullpath, dest) && (info.st_mtime == mtime)) {
1842 + /* remove it */
1843 + if (conf->verbose) bb_printf("<<< %s\n", path);
1844 + if (!conf->pretend && (unlink(fullpath) < 0))
1845 + bb_perror_msg("%s", fullpath);
1846 + else
1847 + return info.st_size;
1848 + } else {
1849 + if (conf->verbose) bb_printf("--- %s\n", path);
1850 + }
1851 +
1852 + return 0;
1853 +}
1854 +
1855 +/*
1856 + unmerge a file
1857 + modifies line. (replaces all spaces with \0 t be exact)
1858 + returns size of the file removed.
1859 +*/
1860 +int unmerge_obj(char *line) {
1861 + char *type = strtok(line, " ");
1862 + char *path = strtok(NULL, " ");
1863 + char *chksum = strtok(NULL, " ");
1864 + time_t mtime = atoi(strtok(NULL, " "));
1865 + static char fullpath[256];
1866 + static struct stat info;
1867 +
1868 + if (type == NULL || path == NULL || chksum == NULL) return 0;
1869 +
1870 + snprintf(fullpath, sizeof(fullpath), "%s%s%s",
1871 + conf->root, (path[0] != '/') ? "/" : "", path);
1872 +
1873 + lstat(fullpath, &info);
1874 + /*
1875 + bb_printf("%s:%s contents(%i:%i) - stat(%i,%i)\n\n",
1876 + type, path, atoi(chksum), (int)mtime,
1877 + (int)info.st_size, (int)info.st_mtime);
1878 + */
1879 + /* currently the chksum is just the file size... */
1880 + if ((mtime == info.st_mtime) &&
1881 +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
1882 + (strcmp(chksum, hash_file(fullpath, CHECKSUM_ALGO)) == 0)
1883 + /* we should free the allocated mem from hash_file here... */
1884 +#else
1885 + (atoi(chksum) == info.st_size)
1886 +#endif
1887 + ) {
1888 + if (conf->verbose) bb_printf("<<< %s\n", path);
1889 + if (!conf->pretend && unlink(fullpath) < 0)
1890 + bb_perror_msg("%s", fullpath);
1891 + else {
1892 + return info.st_size;
1893 + }
1894 + } else {
1895 + if(conf->verbose) bb_printf("--- %s\n", path);
1896 + }
1897 + return 0;
1898 +}
1899 +
1900 +/*
1901 + change dir where the CONTENTS are stored
1902 + return 0 on succes.
1903 +*/ /*
1904 +int ch_dbdir(const char *package) {
1905 + char path[256];
1906 + snprintf(path, sizeof(path), "%s/%s/%s",
1907 + conf->root, conf->pkg_dbdir, package);
1908 + return (chdir(mk_as_path(package)));
1909 +}
1910 +*/
1911 +/*
1912 + Search unlink all files liste in list and destroy the list.
1913 + Data in the nodes is free'ed too.
1914 + returns number of destroyed items.
1915 +*/
1916 +int llist_rmdir_and_destroy(llist_t *list) {
1917 + llist_t *item = list;
1918 + int count = 0;
1919 + while (item != NULL) {
1920 + llist_t *tmp;
1921 + if (rmdir(mk_absolute_path(item->data)) == 0) {
1922 + count++;
1923 + if (conf->verbose) bb_printf("<<< %s\n", item->data);
1924 + } else {
1925 + if (conf->verbose) bb_printf("--- %s\n", item->data);
1926 + }
1927 + free(item->data);
1928 + tmp = item;
1929 + item = item->link;
1930 + free(tmp);
1931 + }
1932 + return count;
1933 +}
1934 +
1935 +int tbz2_unmerge(package_t *package) {
1936 + FILE *inf;
1937 + static char line[256];
1938 + llist_t *dirs=NULL;
1939 + int total_removed = 0;
1940 +
1941 + if (package == NULL) return -1;
1942 + set_dbdir_pkg(package);
1943 + /* check if file exist */
1944 + inf = bb_xfopen(root_dbdir_pkg_contents, "r");
1945 + chdir(conf->root);
1946 + while (fgets(line, sizeof(line), inf)) {
1947 + chomp(line);
1948 + if (strncmp(line, "dir", 3) == 0) {
1949 + /* we remove the dirs after files and sym's */
1950 + dirs = llist_insert_length_sorted(dirs, bb_xstrdup(line+4));
1951 + } else if (strncmp(line, "sym", 3) == 0) {
1952 + unmerge_sym(line);
1953 + } else {
1954 + unmerge_obj(line);
1955 + }
1956 + ++total_removed;
1957 + }
1958 + fclose(inf);
1959 + /* remove dirs */
1960 + if (conf->debug)
1961 + bb_fprintf(stderr, "removed %i dirs", llist_rmdir_and_destroy(dirs));
1962 + else
1963 + llist_rmdir_and_destroy(dirs);
1964 +
1965 + remove_file(root_dbdir_pkg, FILEUTILS_FORCE|FILEUTILS_RECUR);
1966 + return total_removed;
1967 +}
1968 +
1969 +/* list contents of package */
1970 +void tbz2_list(package_t *package) {
1971 + FILE *inf;
1972 + static char line[256];
1973 +
1974 + if (package == NULL) return;
1975 + set_dbdir_pkg(package);
1976 + inf = bb_xfopen(root_dbdir_pkg_contents, "r");
1977 + while (fgets(line, sizeof(line), inf)) {
1978 + char *path;
1979 + strtok(line, " "); /* the type */
1980 + path = strtok(NULL, " ");
1981 + chomp(path);
1982 + bb_printf("%s\n", path);
1983 + }
1984 + fclose(inf);
1985 +}
1986 +
1987 +/*
1988 + reads and unpacks package from src_fd and store the
1989 + category/package-version information in buffer package.
1990 +*/
1991 +int tbz2_install(int src_fd, char *package, int package_size,
1992 + configurables_t *conf_ptr) {
1993 + int count, total=0;
1994 + static char buffer[BUFSIZE];
1995 + xpak_t xpak;
1996 + int pid;
1997 + int fd_to_child[2];
1998 + int fd_from_child[2]; /* this is the pipe where we get the file list
1999 + in return */
2000 + /* set the global variable conf */
2001 + conf = conf_ptr;
2002 +
2003 + if ((pipe(fd_to_child) != 0) || (pipe(fd_from_child) != 0)) {
2004 + bb_perror_msg_and_die("Can't create pipe");
2005 + }
2006 +
2007 + pid = fork();
2008 + if (pid == -1) {
2009 + bb_perror_msg_and_die("Fork failed");
2010 + }
2011 +
2012 + if (pid == 0) {
2013 + /* child process */
2014 + close(fd_to_child[1]);
2015 + close(fd_from_child[0]);
2016 + tbz2_unpack_files(fd_to_child[0], fd_from_child[1],
2017 + conf->root, conf->install_mask);
2018 + close(fd_to_child[0]);
2019 + close(fd_from_child[1]);
2020 + exit(0);
2021 + }
2022 +
2023 + /* parent process */
2024 + close(fd_to_child[0]); /* This is for writing to child */
2025 + close(fd_from_child[1]); /* and this is for reading */
2026 +
2027 + while ( (count = read_to_xpak(src_fd, buffer, BUFSIZE, &xpak))) {
2028 + if (bb_full_write(fd_to_child[1], buffer, count) < count) {
2029 + bb_error_msg_and_die("unpacking archive failed");
2030 + }
2031 + total += count;
2032 + }
2033 + /* count contains the tar.bz2 size */
2034 +
2035 + if (xpak.buffer == NULL) bb_error_msg_and_die("No XPAK found. Sorry.\n");
2036 + if (xpak_get_package(&xpak, package, package_size) == NULL)
2037 + bb_error_msg_and_die("Cound not extract package information from XPAK.\n");
2038 +
2039 + /* now unmerge previously installed package */
2040 + /*
2041 + if (access(mk_dbdir_path(package, "CONTENTS"), R_OK) == 0)
2042 + tbz2_unmerge(package, conf);
2043 + */
2044 + if (!conf->pretend) xpak_unpack(&xpak, package, package_size);
2045 +
2046 + count = create_contents(fd_from_child[0], package);
2047 + close(fd_from_child[0]);/* close the read pipe */
2048 + free(xpak.buffer);
2049 + return(count);
2050 +}
2051 +
2052 +/*
2053 + Install from filename.
2054 + NULL or '-' means STDIN.
2055 +*/
2056 +int tbz2_install_file(char *file, configurables_t *myconf) {
2057 + char *display_name;
2058 + char package[256]; /* package name found in XPAK */
2059 + int src_fd, size;
2060 +
2061 + tbz2pkg_init(myconf);
2062 + if ((file == NULL) || (strcmp(file, "-") == 0)) {
2063 + display_name = "STDIN";
2064 + src_fd = STDIN_FILENO;
2065 + } else {
2066 + display_name = file;
2067 + src_fd = bb_xopen(file, O_RDONLY);
2068 + }
2069 +
2070 + if (myconf->verbose) bb_printf(">>> Unpacking %s\n", display_name);
2071 +
2072 + size = tbz2_install(src_fd, package, sizeof(package), myconf);
2073 + if (size < 0 )
2074 + bb_error_msg_and_die("Package %s was not successfully"
2075 + " installed. Sorry.\n", display_name);
2076 + else
2077 + if (myconf->verbose)
2078 + bb_printf(">>> Package %s installed. %i bytes.\n\n",
2079 + package, size);
2080 +
2081 + if (src_fd != STDIN_FILENO) close(src_fd);
2082 + return size;
2083 +}
2084 +
2085 +/*
2086 + Main
2087 +*/
2088 +int tbz2pkg_main(int argc, char *argv[]) {
2089 + unsigned long opt;
2090 + configurables_t myconf;
2091 +
2092 + myconf.root = NULL;
2093 + myconf.install_mask = NULL;
2094 + bb_applet_long_options = tbz2pkg_long_options;
2095 + opterr = 0;
2096 + opt = bb_getopt_ulflags(argc, argv, OPTIONS_FLAGS,
2097 + &myconf.install_mask,
2098 + &myconf.root);
2099 +
2100 + bb_printf("%s\n", myconf.root);
2101 +
2102 + if (opt & TBZ2PKG_HELP) bb_show_usage();
2103 +
2104 + myconf.debug = (opt & TBZ2PKG_DEBUG);
2105 + myconf.pretend = (opt & TBZ2PKG_PRETEND);
2106 + myconf.verbose = (opt & TBZ2PKG_VERBOSE);
2107 +
2108 + if (myconf.root == NULL) myconf.root = getenv("ROOT");
2109 + if (myconf.install_mask == NULL) myconf.install_mask = getenv("INSTALL_MASK");
2110 + if ((myconf.pkg_dbdir = getenv("PKG_DBDIR")) == NULL)
2111 + /* shouldn't this use myconf.root, not / ? */
2112 + myconf.pkg_dbdir = "/var/db/pkg";
2113 +
2114 + if (myconf.debug)
2115 + bb_printf("root=%s, install_mask=%s, pkg_dbdir=%s\n",
2116 + myconf.root, myconf.install_mask, myconf.pkg_dbdir);
2117 +
2118 + tbz2pkg_init(&myconf);
2119 + if (opt & TBZ2PKG_INSTALL) {
2120 + if (optind == argc) {
2121 + tbz2_install_file(NULL, &myconf);
2122 + } else {
2123 + int i, total_size=0;
2124 + for (i=optind; i < argc; i++)
2125 + total_size += tbz2_install_file(argv[i], &myconf);
2126 + if (myconf.verbose)
2127 + bb_printf(">>> Totally %i bytes installed.\n\n", total_size);
2128 + }
2129 + } else {
2130 + int i;
2131 + llist_t *results=0;
2132 + package_t *pkg, *tmppkg;
2133 + for (i=optind; i < argc; i++) {
2134 + pkg = decode_namespec(argv[i]);
2135 + results = find_package_on_disk(pkg);
2136 + while (results) {
2137 + tmppkg = (package_t*) results->data;
2138 + if (opt & TBZ2PKG_LIST) {
2139 + bb_printf(">>> Listing %s.\n", tmppkg->name);
2140 + tbz2_list(tmppkg);
2141 + } else if (opt & TBZ2PKG_PURGE) {
2142 + bb_printf(">>> Purging %s.\n", tmppkg->name);
2143 + tbz2_unmerge(tmppkg);
2144 + /* } else if (opt & TBZ2PKG_CHECK) {
2145 + bb_printf(">>> Checking %s.\n", pkg);
2146 + tbz2_check(pkg);
2147 + */
2148 + }
2149 + free(results->data);
2150 + results = results->link;
2151 + }
2152 + pkg = 0; results = 0;
2153 + }
2154 + }
2155 +
2156 + return 0;
2157 +}
2158 +
2159 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/archival/tbz2pkg.h busybox/archival/tbz2pkg.h
2160 --- busybox-1.00/archival/tbz2pkg.h 1970-01-01 01:00:00.000000000 +0100
2161 +++ busybox/archival/tbz2pkg.h 2005-06-08 19:46:32.000000000 +0200
2162 @@ -0,0 +1,35 @@
2163 +/*
2164 +* Distributed under the terms of the GNU General Public License v2
2165 +* $Header: /home/collar_b/programs/cvs/busybox/archival/tbz2pkg.h,v 1.9 2005/06/08 17:46:32 collar_b Exp $
2166 +*
2167 +********************************************************************
2168 +* This program is free software; you can redistribute it and/or
2169 +* modify it under the terms of the GNU General Public License as
2170 +* published by the Free Software Foundation; either version 2 of the
2171 +* License, or (at your option) any later version.
2172 +*
2173 +* This program is distributed in the hope that it will be useful, but
2174 +* WITHOUT ANY WARRANTY; without even the implied warranty of
2175 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2176 +* General Public License for more details.
2177 +*
2178 +* You should have received a copy of the GNU General Public License
2179 +* along with this program; if not, write to the Free Software
2180 +* Foundation, Inc., 59 Temple Place - Suite 330, Boston,
2181 +* MA 02111-1307, USA.
2182 +*/
2183 +
2184 +#ifndef TBZ2PKG_H
2185 +#define TBZ2PKG_H
2186 +
2187 +#include "gentoo_shared.h"
2188 +/* the data & decls here are shared by tbz2pkg and emerge */
2189 +
2190 +
2191 +/* functions defined in tbz2pkg and used in emerge */
2192 +int tbz2_install_file(char *file, configurables_t *myconf);
2193 +int tbz2_unmerge(package_t *package);
2194 +void tbz2_list(package_t *package);
2195 +configurables_t *tbz2pkg_init(configurables_t *myconf);
2196 +
2197 +#endif /* TBZ2PKG_H */
2198 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/include/applets.h busybox/include/applets.h
2199 --- busybox-1.00/include/applets.h 2004-08-27 01:01:34.000000000 +0200
2200 +++ busybox/include/applets.h 2005-03-30 15:33:02.000000000 +0200
2201 @@ -178,6 +178,9 @@
2202 #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
2203 APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
2204 #endif
2205 +#if defined(CONFIG_EMERGE)
2206 + APPLET(emerge, emerge_main, _BB_DIR_BIN, _BB_SUID_NEVER)
2207 +#endif
2208 #ifdef CONFIG_ENV
2209 APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
2210 #endif
2211 @@ -559,6 +562,9 @@
2212 #ifdef CONFIG_TAR
2213 APPLET(tar, tar_main, _BB_DIR_BIN, _BB_SUID_NEVER)
2214 #endif
2215 +#ifdef CONFIG_TBZ2PKG
2216 + APPLET(tbz2pkg, tbz2pkg_main, _BB_DIR_BIN, _BB_SUID_NEVER)
2217 +#endif
2218 #ifdef CONFIG_TEE
2219 APPLET(tee, tee_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
2220 #endif
2221 diff -Nru --exclude=CVS --exclude=env.sh busybox-1.00/include/usage.h busybox/include/usage.h
2222 --- busybox-1.00/include/usage.h 2004-09-14 18:23:56.000000000 +0200
2223 +++ busybox/include/usage.h 2005-06-08 20:16:16.000000000 +0200
2224 @@ -531,6 +531,30 @@
2225 "$ echo "Erik\\nis\\ncool"\n" \
2226 "Erik\\nis\\ncool\n")
2227
2228 +#define emerge_trivial_usage \
2229 + "[-hdpvfKCqislucR:I:P:T:B:] package_name"
2230 +#define emerge_full_usage \
2231 + "emerge is a utility to install, remove and manage Gentoo packages.\n" \
2232 + "Options:\n" \
2233 + "\t-h|--help : This help\n" \
2234 + "\t-d|--debug : set -x\n" \
2235 + "\t-p|--pretend : pretend only\n" \
2236 + "\t-v|--verbose : set verbose flag\n" \
2237 + "\t-f|--fetch : fetch binary files\n" \
2238 + "\t-K|--usepkgonly : use binary package\n" \
2239 + "\t-C|--unmerge : uninstall a binary package\n" \
2240 + "\t-q|--query : query a package\n" \
2241 + "\t--info|info : display info\n" \
2242 + "\t--sync : perform $opt operation\n" \
2243 + "\t--list : update index and list packages\n" \
2244 + "\t--update : update index file\n" \
2245 + "\t--clean : clean up tmpfiles\n" \
2246 + "\t--root=<dir> : define ROOT=\n" \
2247 + "\t--index=<file> : define INDEX_FILE=\n" \
2248 + "\t--pkgdir=<dir> : define PKGDIR=\n" \
2249 + "\t--tmpdir=<dir> : define PORTAGE_TMPDIR=\n" \
2250 + "\t--binhost=<url> : define PORTAGE_BINHOST=\n"
2251 +
2252 #define env_trivial_usage \
2253 "[-iu] [-] [name=value]... [command]"
2254 #define env_full_usage \
2255 @@ -2422,6 +2446,21 @@
2256 "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" \
2257 "$ tar -cf /tmp/tarball.tar /usr/local\n"
2258
2259 +#define tbz2pkg_trivial_usage \
2260 + "[OPTION]... [FILE|PKG_NAME]..."
2261 +#define tbz2pkg_full_usage \
2262 + "Manage gentoo-created binary packages.\n\n" \
2263 + "Options:\n" \
2264 + "\t-d\tshow debug output\n" \
2265 + "\t-h\tshow this help information\n" \
2266 + "\t-i\tinstalled given binary packages (FILE)\n" \
2267 + "\t-M\tDon't install files that are masked by given regexp\n" \
2268 + "\t-L\tList contents of installed package(s) (PKG_NAME)\n" \
2269 + "\t-p\tPretend to do action (install, purge)\n" \
2270 + "\t-P\tUninstall package(s) (PKG_NAME)\n" \
2271 + "\t-R\tSet root directory to ROOT\n" \
2272 + "\t-v\tVerbose output\n"
2273 +
2274 #define tee_trivial_usage \
2275 "[OPTION]... [FILE]..."
2276 #define tee_full_usage \