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