]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/usr.sbin/sysinstall/index.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / 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 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     "arabic", "Ported software for Arab countries.",
83     "archivers", "Utilities for archiving and unarchiving data.",
84     "astro", "Applications related to astronomy.",
85     "audio", "Audio utilities - most require a supported sound card.",
86     "benchmarks", "Utilities for measuring system performance.",
87     "biology", "Software related to biology.",
88     "cad", "Computer Aided Design utilities.",
89     "chinese", "Ported software for the Chinese market.",
90     "comms", "Communications utilities.",
91     "converters", "Format conversion utilities.",
92     "databases", "Database software.",
93     "deskutils", "Various Desktop utilities.",
94     "devel", "Software development utilities and libraries.",
95     "dns", "Domain Name Service tools.",
96     "editors", "Editors.",
97     "elisp", "Things related to Emacs Lisp.",
98     "emulators", "Utilities for emulating other operating systems.",
99     "finance", "Monetary, financial and related applications.",
100     "french", "Ported software for French countries.",
101     "ftp", "FTP client and server utilities.",
102     "games", "Various and sundry amusements.",
103     "german", "Ported software for Germanic countries.",
104     "geography", "Geography-related software.",
105     "gnome", "Components of the Gnome Desktop environment.",
106     "gnustep", "Software for GNUstep desktop environment.",
107     "graphics", "Graphics libraries and utilities.",
108     "haskell", "Software related to the Haskell language.",
109     "hamradio", "Software for amateur radio.",
110     "hebrew", "Ported software for Hebrew language.",
111     "hungarian", "Ported software for the Hungarian market.",
112     "ipv6", "IPv6 related software.",
113     "irc", "Internet Relay Chat utilities.",
114     "japanese", "Ported software for the Japanese market.",
115     "java", "Java language support.",
116     "kde", "Software for the K Desktop Environment.",
117     "kld", "Kernel loadable modules",
118     "korean", "Ported software for the Korean market.",
119     "lang", "Computer languages.",
120     "linux", "Linux programs that can run under binary compatibility.",
121     "lisp", "Software related to the Lisp language.",
122     "mail", "Electronic mail packages and utilities.",
123     "math", "Mathematical computation software.",
124     "mbone", "Applications and utilities for the MBONE.",
125     "misc", "Miscellaneous utilities.",
126     "multimedia", "Multimedia software.",
127     "net", "Networking utilities.",
128     "net-im", "Instant messaging software.",
129     "net-mgmt", "Network management tools.",
130     "net-p2p", "Peer to peer network applications.",
131     "news", "USENET News support software.",
132     "palm", "Software support for the Palm(tm) series.",
133     "parallel", "Applications dealing with parallelism in computing.",
134     "pear", "Software related to the Pear PHP framework.",
135     "perl5", "Utilities/modules for the PERL5 language.",
136     "plan9", "Software from the Plan9 operating system.",
137     "polish", "Ported software for the Polish market.",
138     "ports-mgmt", "Utilities for managing ports and packages.",
139     "portuguese", "Ported software for the Portuguese market.",
140     "print", "Utilities for dealing with printing.",
141     "python", "Software related to the Python language.",
142     "ruby", "Software related to the Ruby language.",
143     "rubygems", "Ports of RubyGems packages.",
144     "russian", "Ported software for the Russian market.",
145     "scheme", "Software related to the Scheme language.",
146     "science", "Scientific software.",
147     "security", "System security software.",
148     "shells", "Various shells (tcsh, bash, etc).",
149     "spanish", "Ported software for the Spanish market.",
150     "sysutils", "Various system utilities.",
151     "tcl", "TCL and packages that depend on it.",
152     "tcl80", "TCL v8.0 and packages that depend on it.",
153     "tcl82", "TCL v8.2 and packages that depend on it.",
154     "tcl83", "TCL v8.3 and packages that depend on it.",
155     "tcl84", "TCL v8.4 and packages that depend on it.",
156     "textproc", "Text processing/search utilities.",
157     "tk", "Tk and packages that depend on it.",
158     "tk80", "Tk8.0 and packages that depend on it.",
159     "tk82", "Tk8.2 and packages that depend on it.",
160     "tk83", "Tk8.3 and packages that depend on it.",
161     "tk84", "Tk8.4 and packages that depend on it.",
162     "tkstep80", "Ports to support the TkStep window manager.",
163     "ukrainian", "Ported software for the Ukrainian market.",
164     "vietnamese", "Ported software for the Vietnamese market.",
165     "windowmaker", "Ports to support the WindowMaker window manager.",
166     "www", "Web utilities (browsers, HTTP servers, etc).",
167     "x11", "X Window System based utilities.",
168     "x11-clocks", "X Window System based clocks.",
169     "x11-drivers", "X Window System drivers.",
170     "x11-fm", "X Window System based file managers.",
171     "x11-fonts", "X Window System fonts and font utilities.",
172     "x11-servers", "X Window System servers.",
173     "x11-themes", "X Window System themes.",
174     "x11-toolkits", "X Window System based development toolkits.",
175     "x11-wm", "X Window System window managers.",
176     "xfce", "Software related to the Xfce Desktop Environment.",
177     "zope", "Software related to the Zope platform.",
178     NULL, NULL,
179 };
180
181 static char *
182 fetch_desc(char *name)
183 {
184     int i;
185
186     for (i = 0; descrs[i]; i += 2) {
187         if (!strcmp(descrs[i], name))
188             return descrs[i + 1];
189     }
190     return "No description provided";
191 }
192
193 static PkgNodePtr
194 new_pkg_node(char *name, node_type type)
195 {
196     PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
197
198     tmp->name = _strdup(name);
199     tmp->type = type;
200     return tmp;
201 }
202
203 static char *
204 strip(char *buf)
205 {
206     int i;
207
208     for (i = 0; buf[i]; i++)
209         if (buf[i] == '\t' || buf[i] == '\n')
210             buf[i] = ' ';
211     return buf;
212 }
213
214 static IndexEntryPtr
215 new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
216 {
217     IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
218
219     tmp->name =         _strdup(name);
220     tmp->path =         _strdup(pathto);
221     tmp->prefix =       _strdup(prefix);
222     tmp->comment =      _strdup(comment);
223     tmp->descrfile =    strip(_strdup(descr));
224     tmp->maintainer =   _strdup(maint);
225     tmp->deps =         _strdup(deps);
226     tmp->depc =         0;
227     tmp->installed =    package_installed(name);
228     tmp->volume =       volume;
229     return tmp;
230 }
231
232 static void
233 index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
234 {
235     PkgNodePtr p, q;
236
237     for (q = NULL, p = top->kids; p; p = p->next) {
238         if (!strcmp(p->name, where)) {
239             q = p;
240             break;
241         }
242     }
243     if (!p) {
244         /* Add new category */
245         q = new_pkg_node(where, PLACE);
246         q->desc = fetch_desc(where);
247         q->next = top->kids;
248         top->kids = q;
249     }
250     p = new_pkg_node(ptr->name, PACKAGE);
251     p->desc = ptr->comment;
252     p->data = ptr;
253     p->next = q->kids;
254     q->kids = p;
255 }
256
257 static int
258 copy_to_sep(char *to, char *from, int sep)
259 {
260     char *tok;
261
262     tok = strchr(from, sep);
263     if (!tok) {
264         *to = '\0';
265         return 0;
266     }
267     *tok = '\0';
268     strcpy(to, from);
269     return tok + 1 - from;
270 }
271
272 static int
273 skip_to_sep(char *from, int sep)
274 {
275     char *tok;
276
277     tok = strchr(from, sep);
278     if (!tok)
279         return 0;
280     *tok = '\0';
281     return tok + 1 - from;
282 }
283
284 static int
285 readline(FILE *fp, char *buf, int max)
286 {
287     int rv, i = 0;
288     char ch;
289
290     while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
291         buf[i++] = ch;
292     if (i < max)
293         buf[i] = '\0';
294     return rv;
295 }
296
297 /*
298  * XXX - this function should do error checking, and skip corrupted INDEX
299  * lines without a set number of '|' delimited fields.
300  */
301
302 int
303 index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
304 {
305     char line[10240 + 2048 * 7];
306     char junk[2048];
307     char volstr[2048];
308     char *cp;
309     int i;
310
311     i = readline(fp, line, sizeof line);
312     if (i <= 0)
313         return EOF;
314     cp = line;
315     cp += copy_to_sep(name, cp, '|');           /* package name */
316     cp += copy_to_sep(pathto, cp, '|');         /* ports directory */
317     cp += copy_to_sep(prefix, cp, '|');         /* prefix */
318     cp += copy_to_sep(comment, cp, '|');        /* comment */
319     cp += copy_to_sep(descr, cp, '|');          /* path to pkg-descr */
320     cp += copy_to_sep(maint, cp, '|');          /* maintainer */
321     cp += copy_to_sep(cats, cp, '|');           /* categories */
322     cp += skip_to_sep(cp, '|');                 /* build deps - not used */
323     cp += copy_to_sep(rdeps, cp, '|');          /* run deps */
324     if (index(cp, '|'))
325         cp += skip_to_sep(cp, '|');             /* url - not used */
326     else {
327         strncpy(junk, cp, 1023);
328         *volume = 0;
329         return 0;
330     }
331     if (index(cp, '|'))
332         cp += skip_to_sep(cp, '|');             /* extract deps - not used */
333     if (index(cp, '|'))
334         cp += skip_to_sep(cp, '|');             /* patch deps - not used */
335     if (index(cp, '|'))
336         cp += skip_to_sep(cp, '|');             /* fetch deps - not used */
337     if (index(cp, '|'))
338         cp += copy_to_sep(volstr, cp, '|');     /* media volume */
339     else {
340         strncpy(volstr, cp, 1023);
341     }
342     *volume = atoi(volstr);
343     return 0;
344 }
345
346 int
347 index_read(FILE *fp, PkgNodePtr papa)
348 {
349     char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048 * 8];
350     int volume;
351     PkgNodePtr i;
352
353     while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
354         char *cp, *cp2, tmp[1024];
355         IndexEntryPtr idx;
356
357         idx = new_index(name, pathto, prefix, comment, descr, maint, deps, volume);
358         /* For now, we only add things to menus if they're in categories.  Keywords are ignored */
359         for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
360             *cp2 = '\0';
361             index_register(papa, cp, idx);
362         }
363         index_register(papa, cp, idx);
364
365         /* Add to special "All" category */
366         index_register(papa, "All", idx);
367     }
368
369     /* Adjust dependency counts */
370     for (i = papa->kids; i != NULL; i = i->next)
371         if (strcmp(i->name, "All") == 0)
372             break;
373     for (i = i->kids; i != NULL; i = i->next)
374         if (((IndexEntryPtr)i->data)->installed)
375             index_recorddeps(TRUE, papa, i->data);
376
377     return 0;
378 }
379
380 void
381 index_init(PkgNodePtr top, PkgNodePtr plist)
382 {
383     if (top) {
384         top->next = top->kids = NULL;
385         top->name = "Package Selection";
386         top->type = PLACE;
387         top->desc = fetch_desc(top->name);
388         top->data = NULL;
389     }
390     if (plist) {
391         plist->next = plist->kids = NULL;
392         plist->name = "Package Targets";
393         plist->type = PLACE;
394         plist->desc = fetch_desc(plist->name);
395         plist->data = NULL;
396     }
397 }
398
399 void
400 index_print(PkgNodePtr top, int level)
401 {
402     int i;
403
404     while (top) {
405         for (i = 0; i < level; i++) putchar('\t');
406         printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
407         for (i = 0; i < level; i++) putchar('\t');
408         printf("desc: %s\n", top->desc);
409         if (top->kids)
410             index_print(top->kids, level + 1);
411         top = top->next;
412     }
413 }
414
415 /* Swap one node for another */
416 static void
417 swap_nodes(PkgNodePtr a, PkgNodePtr b)
418 {
419     PkgNode tmp;
420
421     tmp = *a;
422     *a = *b;
423     a->next = tmp.next;
424     tmp.next = b->next;
425     *b = tmp;
426 }
427
428 /* Use a disgustingly simplistic bubble sort to put our lists in order */
429 void
430 index_sort(PkgNodePtr top)
431 {
432     PkgNodePtr p, q;
433
434     /* Sort everything at the top level */
435     for (p = top->kids; p; p = p->next) {
436         for (q = top->kids; q; q = q->next) {
437             if (q->next && strcmp(q->name, q->next->name) > 0)
438                 swap_nodes(q, q->next);
439         }
440     }
441
442     /* Now sub-sort everything n levels down */    
443     for (p = top->kids; p; p = p->next) {
444         if (p->kids)
445             index_sort(p);
446     }
447 }
448
449 /* Delete an entry out of the list it's in (only the plist, at present) */
450 void
451 index_delete(PkgNodePtr n)
452 {
453     if (n->next) {
454         PkgNodePtr p = n->next;
455
456         *n = *(n->next);
457         safe_free(p);
458     }
459     else /* Kludgy end sentinal */
460         n->name = NULL;
461 }
462
463 /*
464  * Search for a given node by name, returning the category in if
465  * tp is non-NULL.
466  */
467 PkgNodePtr
468 index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
469 {
470     PkgNodePtr p, sp;
471
472     for (p = top->kids; p && p->name; p = p->next) {
473         if (p->type == PACKAGE) {
474             /* If tp == NULL, we're looking for an exact package match */
475             if (!tp && !strcmp(p->name, str))
476                 return p;
477
478             /* If tp, we're looking for both a package and a pointer to the place it's in */
479             if (tp && !strncmp(p->name, str, strlen(str))) {
480                 *tp = top;
481                 return p;
482             }
483         }
484         else if (p->kids) {
485             /* The usual recursion-out-of-laziness ploy */
486             if ((sp = index_search(p, str, tp)) != NULL)
487                 return sp;
488         }
489     }
490     if (p && !p->name)
491         p = NULL;
492     return p;
493 }
494
495 int
496 pkg_checked(dialogMenuItem *self)
497 {
498     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
499     PkgNodePtr kp = self->data, plist = lists->plist;
500     int i;
501
502     i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
503     if (kp->type == PACKAGE && plist) {
504         IndexEntryPtr ie = kp->data;
505         int markD, markX;
506
507         markD = ie->depc > 0; /* needed as dependency */
508         markX = i || ie->installed; /* selected or installed */
509         self->mark = markX ? 'X' : 'D';
510         return markD || markX;
511     } else
512         return FALSE;
513 }
514
515 int
516 pkg_fire(dialogMenuItem *self)
517 {
518     int ret;
519     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
520     PkgNodePtr sp, kp = self->data, plist = lists->plist;
521
522     if (!plist)
523         ret = DITEM_FAILURE;
524     else if (kp->type == PACKAGE) {
525         IndexEntryPtr ie = kp->data;
526
527         sp = index_search(plist, kp->name, NULL);
528         /* Not already selected? */
529         if (!sp) {
530             if (!ie->installed) {
531                 PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
532
533                 *np = *kp;
534                 np->next = plist->kids;
535                 plist->kids = np;
536                 index_recorddeps(TRUE, lists->root, ie);
537                 msgInfo("Added %s to selection list", kp->name);
538             }
539             else if (ie->depc == 0) {
540                 if (!msgNoYes("Do you really want to delete %s from the system?", kp->name)) {
541                     if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
542                         msgConfirm("Warning:  pkg_delete of %s failed.\n  Check debug output for details.", kp->name);
543                     }
544                     else {
545                         ie->installed = 0;
546                         index_recorddeps(FALSE, lists->root, ie);
547                     }
548                 }
549             }
550             else
551                 msgConfirm("Warning: Package %s is needed by\n  %d other installed package%s.",
552                            kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
553         }
554         else {
555             index_recorddeps(FALSE, lists->root, ie);
556             msgInfo("Removed %s from selection list", kp->name);
557             index_delete(sp);
558         }
559         ret = DITEM_SUCCESS;
560         /* Mark menu for redraw if we had dependencies */
561         if (strlen(ie->deps) > 0)
562             ret |= DITEM_REDRAW;
563     }
564     else {      /* Not a package, must be a directory */
565         int p, s;
566                     
567         p = s = 0;
568         index_menu(lists->root, kp, plist, &p, &s);
569         ret = DITEM_SUCCESS | DITEM_CONTINUE;
570     }
571     return ret;
572 }
573
574 void
575 pkg_selected(dialogMenuItem *self, int is_selected)
576 {
577     PkgNodePtr kp = self->data;
578
579     if (!is_selected || kp->type != PACKAGE)
580         return;
581     msgInfo("%s", kp->desc);
582 }
583
584 int
585 index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
586 {
587     struct ListPtrs lists;
588     int n, rval, maxname;
589     int curr, max;
590     PkgNodePtr kp;
591     dialogMenuItem *nitems;
592     Boolean hasPackages;
593     WINDOW *w;
594     
595     lists.root = root;
596     lists.top = top;
597     lists.plist = plist;
598
599     hasPackages = FALSE;
600     nitems = NULL;
601     n = maxname = 0;
602
603     /* Figure out if this menu is full of "leaves" or "branches" */
604     for (kp = top->kids; kp && kp->name; kp = kp->next) {
605         int len;
606
607         ++n;
608         if (kp->type == PACKAGE && plist) {
609             hasPackages = TRUE;
610             if ((len = strlen(kp->name)) > maxname)
611                 maxname = len;
612         }
613     }
614     if (!n && plist) {
615         msgConfirm("The %s menu is empty.", top->name);
616         return DITEM_LEAVE_MENU;
617     }
618
619     w = savescr();
620     while (1) {
621         n = 0;
622         curr = max = 0;
623         use_helpline(NULL);
624         use_helpfile(NULL);
625         kp = top->kids;
626         if (!hasPackages && plist) {
627             nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
628             nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
629         }
630         while (kp && kp->name) {
631             char buf[256];
632             IndexEntryPtr ie = kp->data;
633
634             /* Brutally adjust description to fit in menu */
635             if (kp->type == PACKAGE)
636                 snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
637             else
638                 SAFE_STRCPY(buf, kp->desc);
639             if (strlen(buf) > (_MAX_DESC - maxname))
640                 buf[_MAX_DESC - maxname] = '\0';
641             nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked, 
642                               pkg_fire, pkg_selected, kp, (int *)(&lists), 
643                               &curr, &max);
644             ++n;
645             kp = kp->next;
646         }
647         /* NULL delimiter so item_free() knows when to stop later */
648         nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
649                           &curr, &max);
650
651 recycle:
652         dialog_clear_norefresh();
653         if (hasPackages)
654             rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
655         else
656             rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
657         if (rval == -1 && plist) {
658             static char *cp;
659             PkgNodePtr menu;
660
661             /* Search */
662             if ((cp = msgGetInput(cp, "Search by package name.  Please enter search string:")) != NULL) {
663                 PkgNodePtr p = index_search(top, cp, &menu);
664
665                 if (p) {
666                     int pos, scroll;
667
668                     /* These need to be set to point at the found item, actually.  Hmmm! */
669                     pos = scroll = 0;
670                     index_menu(root, menu, plist, &pos, &scroll);
671                 }
672                 else
673                     msgConfirm("Search string: %s yielded no hits.", cp);
674             }
675             goto recycle;
676         }
677         items_free(nitems, &curr, &max);
678         restorescr(w);
679         return rval ? DITEM_FAILURE : DITEM_SUCCESS;
680     }
681 }
682
683 int
684 index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
685 {
686     int status = DITEM_SUCCESS;
687     PkgNodePtr tmp2;
688     IndexEntryPtr id = who->data;
689     WINDOW *w;
690
691     /* 
692      * Short-circuit the package dependency checks.  We're already
693      * maintaining a data structure of installed packages, so if a
694      * package is already installed, don't try to check to make sure
695      * that all of its dependencies are installed.  At best this
696      * wastes a ton of cycles and can cause minor delays between
697      * package extraction.  At worst it can cause an infinite loop with
698      * a certain faulty INDEX file. 
699      */
700
701     if (id->installed == 1)
702         return DITEM_SUCCESS;
703
704     w = savescr();
705     if (id && id->deps && strlen(id->deps)) {
706         char t[2048 * 8], *cp, *cp2;
707
708         SAFE_STRCPY(t, id->deps);
709         cp = t;
710         while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
711             if ((cp2 = index(cp, ' ')) != NULL)
712                 *cp2 = '\0';
713             if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
714                 status = index_extract(dev, top, tmp2, TRUE);
715                 if (DITEM_STATUS(status) != DITEM_SUCCESS) {
716                     if (variable_get(VAR_NO_CONFIRM))
717                         msgNotify("Loading of dependent package %s failed", cp);
718                     else
719                         msgConfirm("Loading of dependent package %s failed", cp);
720                 }
721             }
722             else if (!package_installed(cp)) {
723                 if (variable_get(VAR_NO_CONFIRM))
724                     msgNotify("Warning: %s is a required package but was not found.", cp);
725                 else
726                     msgConfirm("Warning: %s is a required package but was not found.", cp);
727             }
728             if (cp2)
729                 cp = cp2 + 1;
730             else
731                 cp = NULL;
732         }
733     }
734     /* Done with the deps?  Load the real m'coy */
735     if (DITEM_STATUS(status) == DITEM_SUCCESS) {
736         /* Prompt user if the package is not available on the current volume. */
737         if(mediaDevice->type == DEVICE_TYPE_CDROM) {
738             while (id->volume != dev->volume) {
739                 if (!msgYesNo("This is disc #%d.  Package %s is on disc #%d\n"
740                           "Would you like to switch discs now?\n", dev->volume,
741                           id->name, id->volume)) {
742                     DEVICE_SHUTDOWN(mediaDevice);
743                     msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
744                         dev->volume, id->volume);
745                     DEVICE_INIT(mediaDevice);
746                 } else {
747                     restorescr(w);
748                     return DITEM_FAILURE;
749                 }
750             }
751         }
752         status = package_extract(dev, who->name, depended);
753         if (DITEM_STATUS(status) == DITEM_SUCCESS)
754             id->installed = 1;
755     }
756     restorescr(w);
757     return status;
758 }
759
760 static void
761 index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
762 {
763    char depends[1024 * 16], *space, *todo;
764    PkgNodePtr found;
765    IndexEntryPtr found_ie;
766
767    SAFE_STRCPY(depends, ie->deps);
768    for (todo = depends; todo != NULL; ) {
769       space = index(todo, ' ');
770       if (space != NULL)
771          *space = '\0';
772
773       if (strlen(todo) > 0) { /* only non-empty dependencies */
774           found = index_search(root, todo, NULL);
775           if (found != NULL) {
776               found_ie = found->data;
777               if (add)
778                   ++found_ie->depc;
779               else
780                   --found_ie->depc;
781           }
782       }
783
784       if (space != NULL)
785          todo = space + 1;
786       else
787          todo = NULL;
788    }
789 }
790
791 static Boolean index_initted;
792
793 /* Read and initialize global index */
794 int
795 index_initialize(char *path)
796 {
797     FILE *fp;
798     WINDOW *w = NULL;
799
800     if (!index_initted) {
801         w = savescr();
802         dialog_clear_norefresh();
803
804         /* Got any media? */
805         if (!mediaVerify()) {
806             restorescr(w);
807             return DITEM_FAILURE;
808         }
809
810         /* Does it move when you kick it? */
811         if (!DEVICE_INIT(mediaDevice)) {
812             restorescr(w);
813             return DITEM_FAILURE;
814         }
815
816         dialog_clear_norefresh();
817         msgNotify("Attempting to fetch %s file from selected media.", path);
818         fp = DEVICE_GET(mediaDevice, path, TRUE);
819         if (!fp) {
820             msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
821                        "This may be because the packages collection is not available\n"
822                        "on the distribution media you've chosen, most likely an FTP site\n"
823                        "without the packages collection mirrored.  Please verify that\n"
824                        "your media, or your path to the media, is correct and try again.");
825             DEVICE_SHUTDOWN(mediaDevice);
826             restorescr(w);
827             return DITEM_FAILURE;
828         }
829         dialog_clear_norefresh();
830         msgNotify("Located INDEX, now reading package data from it...");
831         index_init(&Top, &Plist);
832         if (index_read(fp, &Top)) {
833             msgConfirm("I/O or format error on packages/INDEX file.\n"
834                        "Please verify media (or path to media) and try again.");
835             fclose(fp);
836             restorescr(w);
837             return DITEM_FAILURE;
838         }
839         fclose(fp);
840         index_sort(&Top);
841         index_initted = TRUE;
842         restorescr(w);
843     }
844     return DITEM_SUCCESS;
845 }