Move the warning msg after features.h gets pulled in.
[oweals/busybox.git] / dpkg.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <search.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <utime.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11
12 #include "busybox.h"
13
14
15 /* Stupid libc doesn't have a reliable way for use to know 
16  * that libc5 is being used.   Assume this is good enough */ 
17 #if !defined __GLIBC__ && !defined __UCLIBC__
18 #error It looks like you are using libc5, which does not support
19 #error tfind().  tfind() is used by busybox dpkg.
20 #error Please disable BB_DPKG.  Sorry.
21 #endif  
22
23 #define DEPENDSMAX      64      /* maximum number of depends we can handle */
24
25 /* Should we do full dependency checking? */
26 #define DODEPENDS 1
27
28 /* Should we do debugging? */
29 #define DODEBUG 1
30
31 #ifdef DODEBUG
32 #define SYSTEM(x) do_system(x)
33 #define DPRINTF(fmt,args...) fprintf(stderr, fmt, ##args)
34 #else
35 #define SYSTEM(x) system(x)
36 #define DPRINTF(fmt,args...) /* nothing */
37 #endif
38
39 /* from dpkg-deb.c */
40 extern int deb_extract(int optflags, const char *dir_name, const char *deb_filename);
41 static const int dpkg_deb_contents = 1;
42 static const int dpkg_deb_control = 2;
43 //      const int dpkg_deb_info = 4;
44 static const int dpkg_deb_extract = 8;
45 static const int dpkg_deb_verbose_extract = 16;
46 static const int dpkg_deb_list = 32;
47
48 static const char statusfile[] = "/var/lib/dpkg/status.udeb";
49 static const char new_statusfile[] = "/var/lib/dpkg/status.udeb.new";
50 static const char bak_statusfile[] = "/var/lib/dpkg/status.udeb.bak";
51
52 static const char dpkgcidir[] = "/var/lib/dpkg/tmp.ci/";
53
54 static const char infodir[] = "/var/lib/dpkg/info/";
55 static const char udpkg_quiet[] = "UDPKG_QUIET";
56
57 //static const int status_wantstart     = 0;
58 //static const int status_wantunknown   = (1 << 0);
59 static const int status_wantinstall     = (1 << 1);
60 //static const int status_wanthold      = (1 << 2);
61 //static const int status_wantdeinstall = (1 << 3);
62 //static const int status_wantpurge     = (1 << 4);
63 static const int status_wantmask        = 31;
64
65 //static const int status_flagstart     = 5;
66 static const int status_flagok  = (1 << 5);     /* 32 */
67 //static const int status_flagreinstreq = (1 << 6); 
68 //static const int status_flaghold      = (1 << 7);
69 //static const int status_flagholdreinstreq     = (1 << 8);
70 static const int status_flagmask        = 480;
71
72 //static const int status_statusstart   = 9;
73 //static const int status_statusnoninstalled    = (1 << 9); /* 512 */
74 static const int status_statusunpacked  = (1 << 10);
75 static const int status_statushalfconfigured    = (1 << 11); 
76 static const int status_statusinstalled = (1 << 12);
77 static const int status_statushalfinstalled     = (1 << 13);
78 //static const int status_statusconfigfiles     = (1 << 14);
79 //static const int status_statuspostinstfailed  = (1 << 15);
80 //static const int status_statusremovalfailed   = (1 << 16);
81 static const int status_statusmask =  130560; /* i assume status_statusinstalled is supposed to be included */
82
83 static const char *statuswords[][10] = {
84         { (char *) 0, "unknown", "install", "hold", "deinstall", "purge", 0 },
85         { (char *) 5, "ok", "reinstreq", "hold", "hold-reinstreq", 0 },
86         { (char *) 9, "not-installed", "unpacked", "half-configured",
87                 "installed", "half-installed", "config-files",
88                 "post-inst-failed", "removal-failed", 0 }
89 };
90
91 static const int color_white    = 0;
92 static const int color_grey     = 1;
93 static const int color_black    = 2;
94
95 /* data structures */
96 typedef struct package_s {
97         char *file;
98         char *package;
99         char *version;
100         char *depends;
101         char *provides;
102         char *description;
103         int installer_menu_item;
104         unsigned long status;
105         char color; /* for topo-sort */
106         struct package_s *requiredfor[DEPENDSMAX]; 
107         unsigned short requiredcount;
108         struct package_s *next;
109 } package_t;
110
111 #ifdef DODEBUG
112 static int do_system(const char *cmd)
113 {
114         DPRINTF("cmd is %s\n", cmd);
115         return system(cmd);
116 }
117 #else
118 #define do_system(cmd) system(cmd)
119 #endif
120
121 static int package_compare(const void *p1, const void *p2)
122 {
123         return strcmp(((package_t *)p1)->package, 
124                 ((package_t *)p2)->package);
125 }
126
127 static int remove_dpkgcidir()
128 {
129         char *rm_dpkgcidir = NULL;
130
131         rm_dpkgcidir = (char *) xmalloc(strlen(dpkgcidir) + 8);
132         strcpy(rm_dpkgcidir, "rm -rf ");
133         strcat(rm_dpkgcidir, dpkgcidir);
134
135         if (SYSTEM(rm_dpkgcidir) != 0) {
136                 perror("mkdir ");
137                 return EXIT_FAILURE;
138         }
139         return EXIT_SUCCESS;
140 }
141
142 #ifdef DODEPENDS
143 #include <ctype.h>
144
145 static char **depends_split(const char *dependsstr)
146 {
147         static char *dependsvec[DEPENDSMAX];
148         char *p;
149         int i = 0;
150
151         dependsvec[0] = 0;
152         if (dependsstr == 0) {
153                 goto end;
154         }
155
156         p = strdup(dependsstr);
157         while (*p != 0 && *p != '\n') {
158                 if (*p != ' ') {
159                         if (*p == ',') {
160                                 *p = 0;
161                                 dependsvec[++i] = 0;
162                         } else {
163                                 if (dependsvec[i] == 0) {
164                                         dependsvec[i] = p;
165                                 }
166                         }
167                 } else {
168                         *p = 0; /* eat the space... */
169                 }
170                 p++;
171         }
172         *p = 0;
173
174 end:
175         dependsvec[i+1] = 0;
176         return dependsvec;
177 }
178
179 /* Topological sort algorithm:
180  * ordered is the output list, pkgs is the dependency graph, pkg is 
181  * the current node
182  *
183  * recursively add all the adjacent nodes to the ordered list, marking
184  * each one as visited along the way
185  *
186  * yes, this algorithm looks a bit odd when all the params have the
187  * same type :-)
188  */
189 static void depends_sort_visit(package_t **ordered, package_t *pkgs,
190                 package_t *pkg)
191 {
192         unsigned short i;
193
194         /* mark node as processing */
195         pkg->color = color_grey;
196
197         /* visit each not-yet-visited node */
198         for (i = 0; i < pkg->requiredcount; i++)
199                 if (pkg->requiredfor[i]->color == color_white)
200                         depends_sort_visit(ordered, pkgs, pkg->requiredfor[i]);
201
202 #if 0
203         /* add it to the list */
204         newnode = (struct package_t *)xmalloc(sizeof(struct package_t));
205         /* make a shallow copy */
206         *newnode = *pkg;
207         newnode->next = *ordered;
208         *ordered = newnode;
209 #endif
210
211         pkg->next = *ordered;
212         *ordered = pkg;
213
214         /* mark node as done */
215         pkg->color = color_black;
216 }
217
218 static package_t *depends_sort(package_t *pkgs)
219 {
220         /* TODO: it needs to break cycles in the to-be-installed package 
221          * graph... */
222         package_t *ordered = NULL;
223         package_t *pkg;
224
225         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
226                 pkg->color = color_white;
227         }
228         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
229                 if (pkg->color == color_white) {
230                         depends_sort_visit(&ordered, pkgs, pkg);
231                 }
232         }
233
234         /* Leaks the old list... return the new one... */
235         return ordered;
236 }
237
238
239 /* resolve package dependencies -- 
240  * for each package in the list of packages to be installed, we parse its 
241  * dependency info to determine if the dependent packages are either 
242  * already installed, or are scheduled to be installed. If both tests fail
243  * than bail.
244  *
245  * The algorithm here is O(n^2*m) where n = number of packages to be 
246  * installed and m is the # of dependencies per package. Not a terribly
247  * efficient algorithm, but given that at any one time you are unlikely
248  * to install a very large number of packages it doesn't really matter
249  */
250 static package_t *depends_resolve(package_t *pkgs, void *status)
251 {
252         package_t *pkg, *chk;
253         package_t dependpkg;
254         char **dependsvec;
255         int i;
256         void *found;
257
258         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
259                 dependsvec = depends_split(pkg->depends);
260                 i = 0;
261                 while (dependsvec[i] != 0) {
262                         /* Check for dependencies; first look for installed packages */
263                         dependpkg.package = dependsvec[i];
264                         if ((found = tfind(&dependpkg, &status, package_compare)) == 0 ||
265                             ((chk = *(package_t **)found) &&
266                              (chk->status & (status_flagok | status_statusinstalled)) != 
267                               (status_flagok | status_statusinstalled))) {
268
269                                 /* if it fails, we look through the list of packages we are going to 
270                                  * install */
271                                 for (chk = pkgs; chk != 0; chk = chk->next) {
272                                         if (strcmp(chk->package, dependsvec[i]) == 0 || (chk->provides && 
273                                              strncmp(chk->provides, dependsvec[i], strlen(dependsvec[i])) == 0)) {
274                                                 if (chk->requiredcount >= DEPENDSMAX) {
275                                                         fprintf(stderr, "Too many dependencies for %s\n", chk->package);
276                                                         return 0;
277                                                 }
278                                                 if (chk != pkg) {
279                                                         chk->requiredfor[chk->requiredcount++] = pkg;
280                                                 }
281                                                 break;
282                                         }
283                                 }
284                                 if (chk == 0) {
285                                         fprintf(stderr, "%s depends on %s, but it is not going to be installed\n", pkg->package, dependsvec[i]);
286                                         return 0;
287                                 }
288                         }
289                         i++;
290                 }
291         }
292
293         return depends_sort(pkgs);
294 }
295 #endif
296
297 /* Status file handling routines
298  * 
299  * This is a fairly minimalistic implementation. there are two main functions 
300  * that are supported:
301  * 
302  * 1) reading the entire status file:
303  *    the status file is read into memory as a binary-tree, with just the 
304  *    package and status info preserved
305  *
306  * 2) merging the status file
307  *    control info from (new) packages is merged into the status file, 
308  *    replacing any pre-existing entries. when a merge happens, status info 
309  *    read using the status_read function is written back to the status file
310  */
311 static unsigned long status_parse(const char *line)
312 {
313         char *p;
314         int i, j;
315         unsigned long l = 0;
316
317         for (i = 0; i < 3; i++) {
318                 if ((p = strchr(line, ' ')) != NULL) {
319                         *p = 0;
320                 }
321                 j = 1;
322                 while (statuswords[i][j] != 0) {
323                         if (strcmp(line, statuswords[i][j]) == 0) {
324                                 l |= (1 << ((int)statuswords[i][0] + j - 1));
325                                 break;
326                         }
327                         j++;
328                 }
329                 /* parse error */
330                 if (statuswords[i][j] == 0) {
331                         return 0;
332                 }
333                 line = p+1;
334         }
335
336         return l;
337 }
338
339 static const char *status_print(unsigned long flags)
340 {
341         /* this function returns a static buffer... */
342         static char buf[256];
343         int i, j;
344
345         buf[0] = 0;
346         for (i = 0; i < 3; i++) {
347                 j = 1;
348                 while (statuswords[i][j] != 0) {
349                         if ((flags & (1 << ((int)statuswords[i][0] + j - 1))) != 0)     {
350                                 strcat(buf, statuswords[i][j]);
351                                 if (i < 2) strcat(buf, " ");
352                                 break;
353                         }
354                         j++;
355                 }
356                 if (statuswords[i][j] == 0) {
357                         fprintf(stderr, "corrupted status flag!!\n");
358                         return NULL;
359                 }
360         }
361
362         return buf;
363 }
364
365 /*
366  * Read a control file (or a stanza of a status file) and parse it,
367  * filling parsed fields into the package structure
368  */
369 static int control_read(FILE *file, package_t *p)
370 {
371         char *line;
372
373         while ((line = get_line_from_file(file)) != NULL) {
374                 line[strlen(line)] = 0;
375
376                 if (strlen(line) == 0) {
377                         break;
378                 } else
379                         if (strstr(line, "Package: ") == line) {
380                                 p->package = strdup(line + 9);
381                 } else
382                         if (strstr(line, "Status: ") == line) {
383                                 p->status = status_parse(line + 8);
384                 } else
385                         if (strstr(line, "Depends: ") == line) {
386                                 p->depends = strdup(line + 9);
387                 } else
388                         if (strstr(line, "Provides: ") == line) {
389                                 p->provides = strdup(line + 10);
390                 } else
391                         if (strstr(line, "Description: ") == line) {
392                                 p->description = strdup(line + 13);
393                 /* This is specific to the Debian Installer. Ifdef? */
394                 } else
395                         if (strstr(line, "installer-menu-item: ") == line) {
396                                 p->installer_menu_item = atoi(line + 21);
397                 }
398                 /* TODO: localized descriptions */
399         }
400         free(line);
401         return EXIT_SUCCESS;
402 }
403
404 static void *status_read(void)
405 {
406         FILE *f;
407         void *status = 0;
408         package_t *m = 0, *p = 0, *t = 0;
409
410         if ((f = fopen(statusfile, "r")) == NULL) {
411                 perror(statusfile);
412                 return 0;
413         }
414
415         if (getenv(udpkg_quiet) == NULL) {
416                 printf("(Reading database...)\n");
417         }
418
419         while (!feof(f)) {
420                 m = (package_t *)xmalloc(sizeof(package_t));
421                 memset(m, 0, sizeof(package_t));
422                 control_read(f, m);
423                 if (m->package) {
424                         /*
425                          * If there is an item in the tree by this name,
426                          * it must be a virtual package; insert real
427                          * package in preference.
428                          */
429                         tdelete(m, &status, package_compare);
430                         tsearch(m, &status, package_compare);
431                         if (m->provides) {
432                                 /* 
433                                  * A "Provides" triggers the insertion
434                                  * of a pseudo package into the status
435                                  * binary-tree.
436                                  */
437                                 p = (package_t *)xmalloc(sizeof(package_t));
438                                 memset(p, 0, sizeof(package_t));
439                                 p->package = strdup(m->provides);
440
441                                 t = *(package_t **)tsearch(p, &status, package_compare);
442                                 if (t != p) {
443                                         free(p->package);
444                                         free(p);
445                                 }
446                                 else {
447                                         /*
448                                          * Pseudo package status is the
449                                          * same as the status of the
450                                          * package providing it 
451                                          * FIXME: (not quite right, if 2
452                                          * packages of different statuses
453                                          * provide it).
454                                          */
455                                         t->status = m->status;
456                                 }
457                         }
458                 }
459                 else {
460                         free(m);
461                 }
462         }
463         fclose(f);
464         return status;
465 }
466
467 static int status_merge(void *status, package_t *pkgs)
468 {
469         FILE *fin, *fout;
470         char *line;
471         package_t *pkg = 0, *statpkg = 0;
472         package_t locpkg;
473         int r = 0;
474
475         if ((fin = fopen(statusfile, "r")) == NULL) {
476                 perror(statusfile);
477                 return 0;
478         }
479         if ((fout = fopen(new_statusfile, "w")) == NULL) {
480                 perror(new_statusfile);
481                 return 0;
482         }
483         if (getenv(udpkg_quiet) == NULL) {
484                 printf("(Updating database...)\n");
485         }
486
487         while (((line = get_line_from_file(fin)) != NULL) && !feof(fin)) { 
488                 line[strlen(line)] = 0; /* trim newline */
489                 /* If we see a package header, find out if it's a package
490                  * that we have processed. if so, we skip that block for
491                  * now (write it at the end).
492                  *
493                  * we also look at packages in the status cache and update
494                  * their status fields
495                  */
496                 if (strstr(line, "Package: ") == line) {
497                         for (pkg = pkgs; pkg != 0 && strncmp(line + 9,
498                                         pkg->package, strlen(line) - 9) != 0;
499                              pkg = pkg->next) ;
500
501                         locpkg.package = line + 9;
502                         statpkg = tfind(&locpkg, &status, package_compare);
503                         
504                         /* note: statpkg should be non-zero, unless the status
505                          * file was changed while we are processing (no locking
506                          * is currently done...
507                          */
508                         if (statpkg != 0) {
509                                 statpkg = *(package_t **)statpkg;
510                         }
511                 }
512                 if (pkg != 0) {
513                         continue;
514                 }
515                 if (strstr(line, "Status: ") == line && statpkg != 0) {
516                         snprintf(line, sizeof(line), "Status: %s",
517                                 status_print(statpkg->status));
518                 }
519                 fputs(line, fout);
520                 fputc('\n', fout);
521         }
522         free(line);
523
524         // Print out packages we processed.
525         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
526                 fprintf(fout, "Package: %s\nStatus: %s\n", 
527                         pkg->package, status_print(pkg->status));
528                 if (pkg->depends)
529                         fprintf(fout, "Depends: %s\n", pkg->depends);
530                 if (pkg->provides)
531                         fprintf(fout, "Provides: %s\n", pkg->provides);
532                 if (pkg->installer_menu_item)
533                         fprintf(fout, "installer-menu-item: %i\n", pkg->installer_menu_item);
534                 if (pkg->description)
535                         fprintf(fout, "Description: %s\n", pkg->description);
536                 fputc('\n', fout);
537         }
538         
539         fclose(fin);
540         fclose(fout);
541
542         r = rename(statusfile, bak_statusfile);
543         if (r == 0) {
544                 r = rename(new_statusfile, statusfile);
545         }
546
547         return 0;
548 }
549
550 static int is_file(const char *fn)
551 {
552         struct stat statbuf;
553
554         if (stat(fn, &statbuf) < 0) {
555                 return 0;
556         }
557         return S_ISREG(statbuf.st_mode);
558 }
559
560 static int dpkg_doconfigure(package_t *pkg)
561 {
562         int r;
563         char postinst[1024];
564         char buf[1024];
565
566         DPRINTF("Configuring %s\n", pkg->package);
567         pkg->status &= status_statusmask;
568         snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
569
570         if (is_file(postinst)) {
571                 snprintf(buf, sizeof(buf), "%s configure", postinst);
572                 if ((r = do_system(buf)) != 0) {
573                         fprintf(stderr, "postinst exited with status %d\n", r);
574                         pkg->status |= status_statushalfconfigured;
575                         return 1;
576                 }
577         }
578         pkg->status |= status_statusinstalled;
579         
580         return 0;
581 }
582
583 static int dpkg_dounpack(package_t *pkg)
584 {
585         int r = 0, i;
586         char *cwd;
587         FILE *outfp;
588         char *src_file = NULL;
589         char *dst_file = NULL;
590         char *lst_file = NULL;
591         char *adminscripts[] = { "/prerm", "/postrm", "/preinst", "/postinst",
592                         "/conffiles", "/md5sums", "/shlibs", "/templates" };
593         char buf[1024], buf2[1024];
594
595         DPRINTF("Unpacking %s\n", pkg->package);
596
597         cwd = getcwd(0, 0);
598         chdir("/");
599         deb_extract(dpkg_deb_extract, "/", pkg->file);
600
601         /* Installs the package scripts into the info directory */
602         for (i = 0; i < sizeof(adminscripts) / sizeof(adminscripts[0]); i++) {
603                 /* The full path of the current location of the admin file */
604                 src_file = xrealloc(src_file, strlen(dpkgcidir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
605                 strcpy(src_file, dpkgcidir);
606                 strcat(src_file, pkg->package);
607                 strcat(src_file, adminscripts[i]);
608
609                 /* the full path of where we want the file to be copied to */
610                 dst_file = xrealloc(dst_file, strlen(infodir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
611                 strcpy(dst_file, infodir);
612                 strcat(dst_file, pkg->package);
613                 strcat(dst_file, adminscripts[i]);
614
615                 /* copy admin file to permanent home */
616                 if (copy_file(src_file, dst_file, TRUE, FALSE, FALSE) < 0) {
617                         error_msg_and_die("Cannot copy %s to %s ", buf, buf2);
618                 }
619
620                 /* create the list file */
621                 lst_file = (char *) xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
622                 strcpy(lst_file, infodir);
623                 strcat(lst_file, pkg->package);
624                 strcat(lst_file, ".list");
625                 outfp = freopen(lst_file, "w", stdout);
626                 deb_extract(dpkg_deb_list, NULL, pkg->file);
627                 stdout = freopen(NULL, "w", outfp);
628
629                 printf("done\n");
630                 getchar();
631
632                 fclose(outfp);
633         }
634
635         pkg->status &= status_wantmask;
636         pkg->status |= status_wantinstall;
637         pkg->status &= status_flagmask;
638         pkg->status |= status_flagok;
639         pkg->status &= status_statusmask;
640
641         if (r == 0) {
642                 pkg->status |= status_statusunpacked;
643         } else {
644                 pkg->status |= status_statushalfinstalled;
645         }
646         chdir(cwd);
647         return r;
648 }
649
650 /*
651  * Extract and parse the control.tar.gz from the specified package
652  */
653 static int dpkg_unpackcontrol(package_t *pkg)
654 {
655         char *tmp_name;
656         FILE *file;
657         int length;
658
659         /* clean the temp directory (dpkgcidir) be recreating it */
660         remove_dpkgcidir();
661         if (mkdir(dpkgcidir, S_IRWXU) != 0) {
662                 perror("mkdir");
663                 return EXIT_FAILURE;
664         }
665
666         /*
667          * Get the package name from the file name,
668          * first remove the directories
669          */
670         if ((tmp_name = strrchr(pkg->file, '/')) == NULL) {
671                 tmp_name = pkg->file;
672         } else {
673                 tmp_name++;
674         }
675         /* now remove trailing version numbers etc */
676         length = strcspn(tmp_name, "_.");
677         pkg->package = (char *) xmalloc(length + 1);
678         /* store the package name */
679         strncpy(pkg->package, tmp_name, length);
680
681         /* work out the full extraction path */
682         tmp_name = (char *) xmalloc(strlen(dpkgcidir) + strlen(pkg->package) + 9);
683         memset(tmp_name, 0, strlen(dpkgcidir) + strlen(pkg->package) + 9);
684         strcpy(tmp_name, dpkgcidir);
685         strcat(tmp_name, pkg->package);
686
687         /* extract control.tar.gz to the full extraction path */
688         deb_extract(dpkg_deb_control, tmp_name, pkg->file);
689
690         /* parse the extracted control file */
691         strcat(tmp_name, "/control");
692         if ((file = fopen(tmp_name, "r")) == NULL) {
693                 return EXIT_FAILURE;
694         }
695         if (control_read(file, pkg) == EXIT_FAILURE) {
696                 return EXIT_FAILURE;
697         }
698
699         return EXIT_SUCCESS;
700 }
701
702 static int dpkg_unpack(package_t *pkgs, void *status)
703 {
704         int r = 0;
705         package_t *pkg;
706
707         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
708                 if (dpkg_unpackcontrol(pkg) == EXIT_FAILURE) {
709                         return EXIT_FAILURE;
710                 }
711                 if ((r = dpkg_dounpack(pkg)) != 0 ) {
712                         break;
713                 }
714         }
715         status_merge(status, pkgs);
716         remove_dpkgcidir();
717
718         return r;
719 }
720
721 static int dpkg_configure(package_t *pkgs, void *status)
722 {
723         int r = 0;
724         void *found;
725         package_t *pkg;
726
727         for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next) {
728                 found = tfind(pkg, &status, package_compare);
729
730                 if (found == 0) {
731                         fprintf(stderr, "Trying to configure %s, but it is not installed\n", pkg->package);
732                         r = 1;
733                 } 
734                 /* configure the package listed in the status file;
735                  * not pkg, as we have info only for the latter
736                  */
737                 else {
738                         r = dpkg_doconfigure(*(package_t **)found);
739                 }
740         }
741         status_merge(status, 0);
742
743         return r;
744 }
745
746 static int dpkg_install(package_t *pkgs, void *status)
747 {
748         package_t *p, *ordered = 0;
749
750         /* Stage 1: parse all the control information */
751         for (p = pkgs; p != 0; p = p->next) {
752                 if (dpkg_unpackcontrol(p) == EXIT_FAILURE) {
753                         perror(p->file);
754                         return EXIT_FAILURE;
755                 }
756         }
757
758         /* Stage 2: resolve dependencies */
759 #ifdef DODEPENDS
760         ordered = depends_resolve(pkgs, status);
761 #else
762         ordered = pkgs;
763 #endif
764         
765         /* Stage 3: install */
766         for (p = ordered; p != 0; p = p->next) {
767                 p->status &= status_wantmask;
768                 p->status |= status_wantinstall;
769
770                 /* for now the flag is always set to ok... this is probably
771                  * not what we want
772                  */
773                 p->status &= status_flagmask;
774                 p->status |= status_flagok;
775
776                 DPRINTF("Installing %s\n", p->package);
777                 if (dpkg_dounpack(p) != 0) {
778                         perror(p->file);
779                 }
780                 if (dpkg_doconfigure(p) != 0) {
781                         perror(p->file);
782                 }
783         }
784         
785         if (ordered != 0) {
786                 status_merge(status, pkgs);
787         }
788         remove_dpkgcidir();
789
790         return 0;
791 }
792
793 static int dpkg_remove(package_t *pkgs, void *status)
794 {
795         package_t *p;
796
797         for (p = pkgs; p != 0; p = p->next)
798         {
799         }
800         status_merge(status, 0);
801
802         return 0;
803 }
804
805 extern int dpkg_main(int argc, char **argv)
806 {
807         char opt = 0;
808         char *s;
809         package_t *p, *packages = NULL;
810         char *cwd = getcwd(0, 0);
811         void *status = NULL;
812
813         while (*++argv) {
814                 if (**argv == '-') {
815                         /* Nasty little hack to "parse" long options. */
816                         s = *argv;
817                         while (*s == '-') {
818                                 s++;
819                         }
820                         opt=s[0];
821                 } else {
822                         p = (package_t *)xmalloc(sizeof(package_t));
823                         memset(p, 0, sizeof(package_t));
824
825                         if (**argv == '/') {
826                                 p->file = *argv;
827                         } else
828                                 if (opt != 'c') {
829                                         p->file = xmalloc(strlen(cwd) + strlen(*argv) + 2);
830                                         sprintf(p->file, "%s/%s", cwd, *argv);
831                         } else {
832                                 p->package = strdup(*argv);
833                         }
834
835                         p->next = packages;
836                         packages = p;
837                 }               
838         }
839
840         status = status_read();
841
842         switch (opt) {
843                 case 'i':
844                         return dpkg_install(packages, status);
845                 case 'r':
846                         return dpkg_remove(packages, status);
847                 case 'u':
848                         return dpkg_unpack(packages, status);
849                 case 'c':
850                         return dpkg_configure(packages, status);
851                 default :
852                         show_usage();
853                         return EXIT_FAILURE;
854         }
855 }