Initial import from FreeBSD RELENG_4:
[dragonfly.git] / release / sysinstall / index.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * $FreeBSD: src/release/sysinstall/index.c,v 1.80.2.20 2003/03/03 09:17:43 sobomax Exp $
8  *
9  * Copyright (c) 1995
10  *      Jordan Hubbard.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer,
17  *    verbatim and that no modifications are made prior to this
18  *    point in the file.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <ncurses.h>
42 #include <dialog.h>
43 #include "sysinstall.h"
44
45 /* Macros and magic values */
46 #define MAX_MENU        12
47 #define _MAX_DESC       55
48
49 /* A structure holding the root, top and plist pointer at once */
50 struct ListPtrs
51 {
52     PkgNodePtr root;    /* root of tree */
53     PkgNodePtr top;     /* part of tree we handle */
54     PkgNodePtr plist;   /* list of selected packages */
55 };
56 typedef struct ListPtrs* ListPtrsPtr;
57
58 static void     index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
59
60 /* Shared between index_initialize() and the various clients of it */
61 PkgNode Top, Plist;
62
63 /* Smarter strdup */
64 inline char *
65 _strdup(char *ptr)
66 {
67     return ptr ? strdup(ptr) : NULL;
68 }
69
70 static char *descrs[] = {
71     "Package Selection", "To mark a package, move to it and press SPACE.  If the package is\n"
72     "already marked, it will be unmarked or deleted (if installed).\n"
73     "Items marked with a `D' are dependencies which will be auto-loaded.\n"
74     "To search for a package by name, press ESC.  To select a category,\n"
75     "press RETURN.  NOTE:  The All category selection creates a very large\n"
76     "submenu!  If you select it, please be patient while it comes up.",
77     "Package Targets", "These are the packages you've selected for extraction.\n\n"
78     "If you're sure of these choices, select OK.\n"
79     "If not, select Cancel to go back to the package selection menu.\n",
80     "All", "All available packages in all categories.",
81     "accessibility", "Ports to help disabled users.",
82     "afterstep", "Ports to support the AfterStep window manager.",
83     "applications", "User application software.",
84     "archivers", "Utilities for archiving and unarchiving data.",
85     "astro", "Applications related to astronomy.",
86     "audio", "Audio utilities - most require a supported sound card.",
87     "benchmarks", "Utilities for measuring system performance.",
88     "biology", "Software related to biology.",
89     "cad", "Computer Aided Design utilities.",
90     "chinese", "Ported software for the Chinese market.",
91     "comms", "Communications utilities.",
92     "converters", "Format conversion utilities.",
93     "databases", "Database software.",
94     "deskutils", "Various Desktop utilities.",
95     "devel", "Software development utilities and libraries.",
96     "documentation", "Document preparation utilities.",
97     "editors", "Common text editors.",
98     "elisp", "Things related to Emacs Lisp.",
99     "emulators", "Utilities for emulating other OS types.",
100     "finance", "Monetary, financial and related applications.",
101     "french", "Ported software for French countries.",
102     "ftp", "FTP client and server utilities.",
103     "games", "Various and sundry amusements.",
104     "german", "Ported software for Germanic countries.",
105     "gnome", "Components of the Gnome Desktop environment.",
106     "graphics", "Graphics libraries and utilities.",
107     "haskell", "Software related to the Haskell language.",
108     "hebrew", "Ported software for Hebrew language.",
109     "hungarian", "Ported software for the Hungarian market.",
110     "ipv6", "IPv6 related software.",
111     "irc", "Internet Relay Chat utilities.",
112     "japanese", "Ported software for the Japanese market.",
113     "java", "Java language support.",
114     "kde", "Software for the K Desktop Environment.",
115     "korean", "Ported software for the Korean market.",
116     "lang", "Computer languages.",
117     "languages", "Computer languages.",
118     "libraries", "Software development libraries.",
119     "linux", "Linux programs that can be run under binary compatibility.",
120     "mail", "Electronic mail packages and utilities.",
121     "math", "Mathematical computation software.",
122     "mbone", "Applications and utilities for the MBONE.",
123     "misc", "Miscellaneous utilities.",
124     "multimedia", "Multimedia software.",
125     "net", "Networking utilities.",
126     "news", "USENET News support software.",
127     "numeric", "Mathematical computation software.",
128     "offix", "An office automation suite of sorts.",
129     "orphans", "Packages without a home elsewhere.",
130     "palm", "Software support for the Palm(tm) series.",
131     "parallel", "Applications dealing with parallelism in computing.",
132     "perl5", "Utilities/modules for the PERL5 language.",
133     "picobsd", "Ports to support PicoBSD.",
134     "pilot", "Software support for the 3Com Palm Pilot(tm) series.",
135     "plan9", "Software from the Plan9 operating system.",
136     "portuguese", "Ported software for the Portuguese market.",
137     "print", "Utilities for dealing with printing.",
138     "printing", "Utilities for dealing with printing.",
139     "programming", "Software development utilities and libraries.",
140     "python", "Software related to the Python language.",
141     "ruby", "Software related to the Ruby language.",
142     "russian", "Ported software for the Russian market.",
143     "science", "Scientific software.",
144     "security", "System security software.",
145     "shells", "Various shells (tcsh, bash, etc).",
146     "sysutils", "Various system utilities.",
147     "tcl75", "TCL v7.5 and packages that depend on it.",
148     "tcl76", "TCL v7.6 and packages that depend on it.",
149     "tcl80", "TCL v8.0 and packages that depend on it.",
150     "tcl81", "TCL v8.1 and packages that depend on it.",
151     "tcl82", "TCL v8.2 and packages that depend on it.",
152     "tcl83", "TCL v8.3 and packages that depend on it.",
153     "textproc", "Text processing/search utilities.",
154     "tk41", "Tk4.1 and packages that depend on it.",
155     "tk42", "Tk4.2 and packages that depend on it.",
156     "tk80", "Tk8.0 and packages that depend on it.",
157     "tk81", "Tk8.1 and packages that depend on it.",
158     "tk82", "Tk8.2 and packages that depend on it.",
159     "tk83", "Tk8.3 and packages that depend on it.",
160     "tkstep80", "tkstep wm and packages that depend on it.",
161     "troff", "TROFF text formatting utilities.",
162     "ukrainian", "Ported software for the Ukrainian market.",
163     "vietnamese", "Ported software for the Vietnamese market.",
164     "windowmaker", "Ports to support the WindowMaker window manager.",
165     "www", "WEB utilities (browers, HTTP servers, etc).",
166     "x11", "X Window System based utilities.",
167     "x11-clocks", "X Window System based clocks.",
168     "x11-fm", "X Window System based file managers.",
169     "x11-fonts", "X Window System fonts and font utilities.",
170     "x11-servers", "X Window System servers.",
171     "x11-toolkits", "X Window System based development toolkits.",
172     "x11-wm", "X Window System window managers.",
173     "zope", "Software related to the Zope platform.",
174     NULL, NULL,
175 };
176
177 static char *
178 fetch_desc(char *name)
179 {
180     int i;
181
182     for (i = 0; descrs[i]; i += 2) {
183         if (!strcmp(descrs[i], name))
184             return descrs[i + 1];
185     }
186     return "No description provided";
187 }
188
189 static PkgNodePtr
190 new_pkg_node(char *name, node_type type)
191 {
192     PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
193
194     tmp->name = _strdup(name);
195     tmp->type = type;
196     return tmp;
197 }
198
199 static char *
200 strip(char *buf)
201 {
202     int i;
203
204     for (i = 0; buf[i]; i++)
205         if (buf[i] == '\t' || buf[i] == '\n')
206             buf[i] = ' ';
207     return buf;
208 }
209
210 static IndexEntryPtr
211 new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
212 {
213     IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
214
215     tmp->name =         _strdup(name);
216     tmp->path =         _strdup(pathto);
217     tmp->prefix =       _strdup(prefix);
218     tmp->comment =      _strdup(comment);
219     tmp->descrfile =    strip(_strdup(descr));
220     tmp->maintainer =   _strdup(maint);
221     tmp->deps =         _strdup(deps);
222     tmp->depc =         0;
223     tmp->installed =    package_exists(name);
224     tmp->volume =       volume;
225     return tmp;
226 }
227
228 static void
229 index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
230 {
231     PkgNodePtr p, q;
232
233     for (q = NULL, p = top->kids; p; p = p->next) {
234         if (!strcmp(p->name, where)) {
235             q = p;
236             break;
237         }
238     }
239     if (!p) {
240         /* Add new category */
241         q = new_pkg_node(where, PLACE);
242         q->desc = fetch_desc(where);
243         q->next = top->kids;
244         top->kids = q;
245     }
246     p = new_pkg_node(ptr->name, PACKAGE);
247     p->desc = ptr->comment;
248     p->data = ptr;
249     p->next = q->kids;
250     q->kids = p;
251 }
252
253 static int
254 copy_to_sep(char *to, char *from, int sep)
255 {
256     char *tok;
257
258     tok = strchr(from, sep);
259     if (!tok) {
260         *to = '\0';
261         return 0;
262     }
263     *tok = '\0';
264     strcpy(to, from);
265     return tok + 1 - from;
266 }
267
268 static int
269 readline(FILE *fp, char *buf, int max)
270 {
271     int rv, i = 0;
272     char ch;
273
274     while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
275         buf[i++] = ch;
276     if (i < max)
277         buf[i] = '\0';
278     return rv;
279 }
280
281 /*
282  * XXX - this function should do error checking, and skip corrupted INDEX
283  * lines without a set number of '|' delimited fields.
284  */
285
286 int
287 index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
288 {
289     char line[10240 + 2048 * 7];
290     char junk[2048];
291     char volstr[2048];
292     char *cp;
293     int i;
294
295     i = readline(fp, line, sizeof line);
296     if (i <= 0)
297         return EOF;
298     cp = line;
299     cp += copy_to_sep(name, cp, '|');
300     cp += copy_to_sep(pathto, cp, '|');
301     cp += copy_to_sep(prefix, cp, '|');
302     cp += copy_to_sep(comment, cp, '|');
303     cp += copy_to_sep(descr, cp, '|');
304     cp += copy_to_sep(maint, cp, '|');
305     cp += copy_to_sep(cats, cp, '|');
306     cp += copy_to_sep(junk, cp, '|');   /* build deps - not used */
307     cp += copy_to_sep(rdeps, cp, '|');
308     if (index(cp, '|'))
309         cp += copy_to_sep(junk, cp, '|');       /* url - not used */
310     else {
311         strncpy(junk, cp, 1023);
312         *volume = 0;
313         return 0;
314     }
315     if (index(cp, '|'))
316         cp += copy_to_sep(volstr, cp, '|');     /* media volume */
317     else {
318         strncpy(volstr, cp, 1023);
319     }
320     *volume = atoi(volstr);
321     return 0;
322 }
323
324 int
325 index_read(FILE *fp, PkgNodePtr papa)
326 {
327     char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048 * 8];
328     int volume;
329     PkgNodePtr i;
330
331     while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
332         char *cp, *cp2, tmp[1024];
333         IndexEntryPtr idx;
334
335         idx = new_index(name, pathto, prefix, comment, descr, maint, deps, volume);
336         /* For now, we only add things to menus if they're in categories.  Keywords are ignored */
337         for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
338             *cp2 = '\0';
339             index_register(papa, cp, idx);
340         }
341         index_register(papa, cp, idx);
342
343         /* Add to special "All" category */
344         index_register(papa, "All", idx);
345     }
346
347     /* Adjust dependency counts */
348     for (i = papa->kids; i != NULL; i = i->next)
349         if (strcmp(i->name, "All") == 0)
350             break;
351     for (i = i->kids; i != NULL; i = i->next)
352         if (((IndexEntryPtr)i->data)->installed)
353             index_recorddeps(TRUE, papa, i->data);
354
355     return 0;
356 }
357
358 void
359 index_init(PkgNodePtr top, PkgNodePtr plist)
360 {
361     if (top) {
362         top->next = top->kids = NULL;
363         top->name = "Package Selection";
364         top->type = PLACE;
365         top->desc = fetch_desc(top->name);
366         top->data = NULL;
367     }
368     if (plist) {
369         plist->next = plist->kids = NULL;
370         plist->name = "Package Targets";
371         plist->type = PLACE;
372         plist->desc = fetch_desc(plist->name);
373         plist->data = NULL;
374     }
375 }
376
377 void
378 index_print(PkgNodePtr top, int level)
379 {
380     int i;
381
382     while (top) {
383         for (i = 0; i < level; i++) putchar('\t');
384         printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
385         for (i = 0; i < level; i++) putchar('\t');
386         printf("desc: %s\n", top->desc);
387         if (top->kids)
388             index_print(top->kids, level + 1);
389         top = top->next;
390     }
391 }
392
393 /* Swap one node for another */
394 static void
395 swap_nodes(PkgNodePtr a, PkgNodePtr b)
396 {
397     PkgNode tmp;
398
399     tmp = *a;
400     *a = *b;
401     a->next = tmp.next;
402     tmp.next = b->next;
403     *b = tmp;
404 }
405
406 /* Use a disgustingly simplistic bubble sort to put our lists in order */
407 void
408 index_sort(PkgNodePtr top)
409 {
410     PkgNodePtr p, q;
411
412     /* Sort everything at the top level */
413     for (p = top->kids; p; p = p->next) {
414         for (q = top->kids; q; q = q->next) {
415             if (q->next && strcmp(q->name, q->next->name) > 0)
416                 swap_nodes(q, q->next);
417         }
418     }
419
420     /* Now sub-sort everything n levels down */    
421     for (p = top->kids; p; p = p->next) {
422         if (p->kids)
423             index_sort(p);
424     }
425 }
426
427 /* Delete an entry out of the list it's in (only the plist, at present) */
428 void
429 index_delete(PkgNodePtr n)
430 {
431     if (n->next) {
432         PkgNodePtr p = n->next;
433
434         *n = *(n->next);
435         safe_free(p);
436     }
437     else /* Kludgy end sentinal */
438         n->name = NULL;
439 }
440
441 /*
442  * Search for a given node by name, returning the category in if
443  * tp is non-NULL.
444  */
445 PkgNodePtr
446 index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
447 {
448     PkgNodePtr p, sp;
449
450     for (p = top->kids; p && p->name; p = p->next) {
451         if (p->type == PACKAGE) {
452             /* If tp == NULL, we're looking for an exact package match */
453             if (!tp && !strcmp(p->name, str))
454                 return p;
455
456             /* If tp, we're looking for both a package and a pointer to the place it's in */
457             if (tp && !strncmp(p->name, str, strlen(str))) {
458                 *tp = top;
459                 return p;
460             }
461         }
462         else if (p->kids) {
463             /* The usual recursion-out-of-laziness ploy */
464             if ((sp = index_search(p, str, tp)) != NULL)
465                 return sp;
466         }
467     }
468     if (p && !p->name)
469         p = NULL;
470     return p;
471 }
472
473 int
474 pkg_checked(dialogMenuItem *self)
475 {
476     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
477     PkgNodePtr kp = self->data, plist = lists->plist;
478     int i;
479
480     i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
481     if (kp->type == PACKAGE && plist) {
482         IndexEntryPtr ie = kp->data;
483         int markD, markX;
484
485         markD = ie->depc > 0; /* needed as dependency */
486         markX = i || ie->installed; /* selected or installed */
487         self->mark = markX ? 'X' : 'D';
488         return markD || markX;
489     } else
490         return FALSE;
491 }
492
493 int
494 pkg_fire(dialogMenuItem *self)
495 {
496     int ret;
497     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
498     PkgNodePtr sp, kp = self->data, plist = lists->plist;
499
500     if (!plist)
501         ret = DITEM_FAILURE;
502     else if (kp->type == PACKAGE) {
503         IndexEntryPtr ie = kp->data;
504
505         sp = index_search(plist, kp->name, NULL);
506         /* Not already selected? */
507         if (!sp) {
508             if (!ie->installed) {
509                 PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
510
511                 *np = *kp;
512                 np->next = plist->kids;
513                 plist->kids = np;
514                 index_recorddeps(TRUE, lists->root, ie);
515                 msgInfo("Added %s to selection list", kp->name);
516             }
517             else if (ie->depc == 0) {
518                 if (!msgNoYes("Do you really want to delete %s from the system?", kp->name)) {
519                     if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
520                         msgConfirm("Warning:  pkg_delete of %s failed.\n  Check debug output for details.", kp->name);
521                     }
522                     else {
523                         ie->installed = 0;
524                         index_recorddeps(FALSE, lists->root, ie);
525                     }
526                 }
527             }
528             else
529                 msgConfirm("Warning: Package %s is needed by\n  %d other installed package%s.",
530                            kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
531         }
532         else {
533             index_recorddeps(FALSE, lists->root, ie);
534             msgInfo("Removed %s from selection list", kp->name);
535             index_delete(sp);
536         }
537         ret = DITEM_SUCCESS;
538         /* Mark menu for redraw if we had dependencies */
539         if (strlen(ie->deps) > 0)
540             ret |= DITEM_REDRAW;
541     }
542     else {      /* Not a package, must be a directory */
543         int p, s;
544                     
545         p = s = 0;
546         index_menu(lists->root, kp, plist, &p, &s);
547         ret = DITEM_SUCCESS | DITEM_CONTINUE;
548     }
549     return ret;
550 }
551
552 void
553 pkg_selected(dialogMenuItem *self, int is_selected)
554 {
555     PkgNodePtr kp = self->data;
556
557     if (!is_selected || kp->type != PACKAGE)
558         return;
559     msgInfo("%s", kp->desc);
560 }
561
562 int
563 index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
564 {
565     struct ListPtrs lists;
566     int n, rval, maxname;
567     int curr, max;
568     PkgNodePtr kp;
569     dialogMenuItem *nitems;
570     Boolean hasPackages;
571     WINDOW *w;
572     
573     lists.root = root;
574     lists.top = top;
575     lists.plist = plist;
576
577     hasPackages = FALSE;
578     nitems = NULL;
579     n = maxname = 0;
580
581     /* Figure out if this menu is full of "leaves" or "branches" */
582     for (kp = top->kids; kp && kp->name; kp = kp->next) {
583         int len;
584
585         ++n;
586         if (kp->type == PACKAGE && plist) {
587             hasPackages = TRUE;
588             if ((len = strlen(kp->name)) > maxname)
589                 maxname = len;
590         }
591     }
592     if (!n && plist) {
593         msgConfirm("The %s menu is empty.", top->name);
594         return DITEM_LEAVE_MENU;
595     }
596
597     w = savescr();
598     while (1) {
599         n = 0;
600         curr = max = 0;
601         use_helpline(NULL);
602         use_helpfile(NULL);
603         kp = top->kids;
604         if (!hasPackages && plist) {
605             nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
606             nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
607         }
608         while (kp && kp->name) {
609             char buf[256];
610             IndexEntryPtr ie = kp->data;
611
612             /* Brutally adjust description to fit in menu */
613             if (kp->type == PACKAGE)
614                 snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
615             else
616                 SAFE_STRCPY(buf, kp->desc);
617             if (strlen(buf) > (_MAX_DESC - maxname))
618                 buf[_MAX_DESC - maxname] = '\0';
619             nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked, 
620                               pkg_fire, pkg_selected, kp, (int *)(&lists), 
621                               &curr, &max);
622             ++n;
623             kp = kp->next;
624         }
625         /* NULL delimiter so item_free() knows when to stop later */
626         nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
627                           &curr, &max);
628
629 recycle:
630         dialog_clear_norefresh();
631         if (hasPackages)
632             rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
633         else
634             rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
635         if (rval == -1 && plist) {
636             static char *cp;
637             PkgNodePtr menu;
638
639             /* Search */
640             if ((cp = msgGetInput(cp, "Search by package name.  Please enter search string:")) != NULL) {
641                 PkgNodePtr p = index_search(top, cp, &menu);
642
643                 if (p) {
644                     int pos, scroll;
645
646                     /* These need to be set to point at the found item, actually.  Hmmm! */
647                     pos = scroll = 0;
648                     index_menu(root, menu, plist, &pos, &scroll);
649                 }
650                 else
651                     msgConfirm("Search string: %s yielded no hits.", cp);
652             }
653             goto recycle;
654         }
655         items_free(nitems, &curr, &max);
656         restorescr(w);
657         return rval ? DITEM_FAILURE : DITEM_SUCCESS;
658     }
659 }
660
661 int
662 index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
663 {
664     int status = DITEM_SUCCESS;
665     PkgNodePtr tmp2;
666     IndexEntryPtr id = who->data;
667     WINDOW *w = savescr();
668
669     /* 
670      * Short-circuit the package dependency checks.  We're already
671      * maintaining a data structure of installed packages, so if a
672      * package is already installed, don't try to check to make sure
673      * that all of its dependencies are installed.  At best this
674      * wastes a ton of cycles and can cause minor delays between
675      * package extraction.  At worst it can cause an infinite loop with
676      * a certain faulty INDEX file. 
677      */
678
679     if (id->installed == 1)
680         return DITEM_SUCCESS;
681
682     /*
683      * What if the package is not available on the current media volume?
684      *
685      */
686     if (id->volume != dev->volume) {
687         if (!msgYesNo("This is disc #%d.  Package %s is on disc #%d\n"
688             "Would you like to switch discs now?\n", dev->volume,
689             id->name, id->volume)) {
690                 DEVICE_SHUTDOWN(mediaDevice);
691                 msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
692                     dev->volume, id->volume);
693                 DEVICE_INIT(mediaDevice);
694                 /* XXX, at this point we check to see if this is the
695                  * correct disc, and if not, we loop */
696         } else {
697             return DITEM_FAILURE;
698         }
699     }
700
701     if (id && id->deps && strlen(id->deps)) {
702         char t[2048 * 8], *cp, *cp2;
703
704         SAFE_STRCPY(t, id->deps);
705         cp = t;
706         while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
707             if ((cp2 = index(cp, ' ')) != NULL)
708                 *cp2 = '\0';
709             if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
710                 status = index_extract(dev, top, tmp2, TRUE);
711                 if (DITEM_STATUS(status) != DITEM_SUCCESS) {
712                     if (variable_get(VAR_NO_CONFIRM))
713                         msgNotify("Loading of dependent package %s failed", cp);
714                     else
715                         msgConfirm("Loading of dependent package %s failed", cp);
716                 }
717             }
718             else if (!package_exists(cp)) {
719                 if (variable_get(VAR_NO_CONFIRM))
720                     msgNotify("Warning: %s is a required package but was not found.", cp);
721                 else
722                     msgConfirm("Warning: %s is a required package but was not found.", cp);
723             }
724             if (cp2)
725                 cp = cp2 + 1;
726             else
727                 cp = NULL;
728         }
729     }
730     /* Done with the deps?  Load the real m'coy */
731     if (DITEM_STATUS(status) == DITEM_SUCCESS) {
732         status = package_extract(dev, who->name, depended);
733         if (DITEM_STATUS(status) == DITEM_SUCCESS)
734             id->installed = 1;
735     }
736     restorescr(w);
737     return status;
738 }
739
740 static void
741 index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
742 {
743    char depends[1024 * 16], *space, *todo;
744    PkgNodePtr found;
745    IndexEntryPtr found_ie;
746
747    SAFE_STRCPY(depends, ie->deps);
748    for (todo = depends; todo != NULL; ) {
749       space = index(todo, ' ');
750       if (space != NULL)
751          *space = '\0';
752
753       if (strlen(todo) > 0) { /* only non-empty dependencies */
754           found = index_search(root, todo, NULL);
755           if (found != NULL) {
756               found_ie = found->data;
757               if (add)
758                   ++found_ie->depc;
759               else
760                   --found_ie->depc;
761           }
762       }
763
764       if (space != NULL)
765          todo = space + 1;
766       else
767          todo = NULL;
768    }
769 }
770
771 static Boolean index_initted;
772
773 /* Read and initialize global index */
774 int
775 index_initialize(char *path)
776 {
777     FILE *fp;
778     WINDOW *w = NULL;
779
780     if (!index_initted) {
781         w = savescr();
782         dialog_clear_norefresh();
783
784         /* Got any media? */
785         if (!mediaVerify()) {
786             restorescr(w);
787             return DITEM_FAILURE;
788         }
789
790         /* Does it move when you kick it? */
791         if (!DEVICE_INIT(mediaDevice)) {
792             restorescr(w);
793             return DITEM_FAILURE;
794         }
795
796         dialog_clear_norefresh();
797         msgNotify("Attempting to fetch %s file from selected media.", path);
798         fp = DEVICE_GET(mediaDevice, path, TRUE);
799         if (!fp) {
800             msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
801                        "This may be because the packages collection is not available\n"
802                        "on the distribution media you've chosen, most likely an FTP site\n"
803                        "without the packages collection mirrored.  Please verify that\n"
804                        "your media, or your path to the media, is correct and try again.");
805             DEVICE_SHUTDOWN(mediaDevice);
806             restorescr(w);
807             return DITEM_FAILURE;
808         }
809         dialog_clear_norefresh();
810         msgNotify("Located INDEX, now reading package data from it...");
811         index_init(&Top, &Plist);
812         if (index_read(fp, &Top)) {
813             msgConfirm("I/O or format error on packages/INDEX file.\n"
814                        "Please verify media (or path to media) and try again.");
815             fclose(fp);
816             restorescr(w);
817             return DITEM_FAILURE;
818         }
819         fclose(fp);
820         index_sort(&Top);
821         index_initted = TRUE;
822         restorescr(w);
823     }
824     return DITEM_SUCCESS;
825 }