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