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