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