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