]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/sysinstall/index.c
This commit was generated by cvs2svn to compensate for changes in r177580,
[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     "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 static 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 static 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 static 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 static 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 static 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     size_t maxname;
589     int n, rval;
590     int curr, max;
591     PkgNodePtr kp;
592     dialogMenuItem *nitems;
593     Boolean hasPackages;
594     WINDOW *w;
595     
596     lists.root = root;
597     lists.top = top;
598     lists.plist = plist;
599
600     hasPackages = FALSE;
601     nitems = NULL;
602     n = maxname = 0;
603
604     /* Figure out if this menu is full of "leaves" or "branches" */
605     for (kp = top->kids; kp && kp->name; kp = kp->next) {
606         size_t len;
607
608         ++n;
609         if (kp->type == PACKAGE && plist) {
610             hasPackages = TRUE;
611             if ((len = strlen(kp->name)) > maxname)
612                 maxname = len;
613         }
614     }
615     if (!n && plist) {
616         msgConfirm("The %s menu is empty.", top->name);
617         return DITEM_LEAVE_MENU;
618     }
619
620     w = savescr();
621     while (1) {
622         n = 0;
623         curr = max = 0;
624         use_helpline(NULL);
625         use_helpfile(NULL);
626         kp = top->kids;
627         if (!hasPackages && plist) {
628             nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
629             nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
630         }
631         while (kp && kp->name) {
632             char buf[256];
633             IndexEntryPtr ie = kp->data;
634
635             /* Brutally adjust description to fit in menu */
636             if (kp->type == PACKAGE)
637                 snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
638             else
639                 SAFE_STRCPY(buf, kp->desc);
640             if (strlen(buf) > (_MAX_DESC - maxname))
641                 buf[_MAX_DESC - maxname] = '\0';
642             nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked, 
643                               pkg_fire, pkg_selected, kp, (int *)(&lists), 
644                               &curr, &max);
645             ++n;
646             kp = kp->next;
647         }
648         /* NULL delimiter so item_free() knows when to stop later */
649         nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
650                           &curr, &max);
651
652 recycle:
653         dialog_clear_norefresh();
654         if (hasPackages)
655             rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
656         else
657             rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
658         if (rval == -1 && plist) {
659             static char *cp;
660             PkgNodePtr menu;
661
662             /* Search */
663             if ((cp = msgGetInput(cp, "Search by package name.  Please enter search string:")) != NULL) {
664                 PkgNodePtr p = index_search(top, cp, &menu);
665
666                 if (p) {
667                     int pos, scroll;
668
669                     /* These need to be set to point at the found item, actually.  Hmmm! */
670                     pos = scroll = 0;
671                     index_menu(root, menu, plist, &pos, &scroll);
672                 }
673                 else
674                     msgConfirm("Search string: %s yielded no hits.", cp);
675             }
676             goto recycle;
677         }
678         items_free(nitems, &curr, &max);
679         restorescr(w);
680         return rval ? DITEM_FAILURE : DITEM_SUCCESS;
681     }
682 }
683
684 int
685 index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
686 {
687     int status = DITEM_SUCCESS;
688     PkgNodePtr tmp2;
689     IndexEntryPtr id = who->data;
690     WINDOW *w;
691
692     /* 
693      * Short-circuit the package dependency checks.  We're already
694      * maintaining a data structure of installed packages, so if a
695      * package is already installed, don't try to check to make sure
696      * that all of its dependencies are installed.  At best this
697      * wastes a ton of cycles and can cause minor delays between
698      * package extraction.  At worst it can cause an infinite loop with
699      * a certain faulty INDEX file. 
700      */
701
702     if (id->installed == 1)
703         return DITEM_SUCCESS;
704
705     w = savescr();
706     if (id && id->deps && strlen(id->deps)) {
707         char t[2048 * 8], *cp, *cp2;
708
709         SAFE_STRCPY(t, id->deps);
710         cp = t;
711         while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
712             if ((cp2 = index(cp, ' ')) != NULL)
713                 *cp2 = '\0';
714             if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
715                 status = index_extract(dev, top, tmp2, TRUE);
716                 if (DITEM_STATUS(status) != DITEM_SUCCESS) {
717                     if (variable_get(VAR_NO_CONFIRM))
718                         msgNotify("Loading of dependent package %s failed", cp);
719                     else
720                         msgConfirm("Loading of dependent package %s failed", cp);
721                 }
722             }
723             else if (!package_installed(cp)) {
724                 if (variable_get(VAR_NO_CONFIRM))
725                     msgNotify("Warning: %s is a required package but was not found.", cp);
726                 else
727                     msgConfirm("Warning: %s is a required package but was not found.", cp);
728             }
729             if (cp2)
730                 cp = cp2 + 1;
731             else
732                 cp = NULL;
733         }
734     }
735     /* Done with the deps?  Load the real m'coy */
736     if (DITEM_STATUS(status) == DITEM_SUCCESS) {
737         /* Prompt user if the package is not available on the current volume. */
738         if(mediaDevice->type == DEVICE_TYPE_CDROM) {
739             while (id->volume != dev->volume) {
740                 if (!msgYesNo("This is disc #%d.  Package %s is on disc #%d\n"
741                           "Would you like to switch discs now?\n", dev->volume,
742                           id->name, id->volume)) {
743                     DEVICE_SHUTDOWN(mediaDevice);
744                     msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
745                         dev->volume, id->volume);
746                     DEVICE_INIT(mediaDevice);
747                 } else {
748                     restorescr(w);
749                     return DITEM_FAILURE;
750                 }
751             }
752         }
753         status = package_extract(dev, who->name, depended);
754         if (DITEM_STATUS(status) == DITEM_SUCCESS)
755             id->installed = 1;
756     }
757     restorescr(w);
758     return status;
759 }
760
761 static void
762 index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
763 {
764    char depends[1024 * 16], *space, *todo;
765    PkgNodePtr found;
766    IndexEntryPtr found_ie;
767
768    SAFE_STRCPY(depends, ie->deps);
769    for (todo = depends; todo != NULL; ) {
770       space = index(todo, ' ');
771       if (space != NULL)
772          *space = '\0';
773
774       if (strlen(todo) > 0) { /* only non-empty dependencies */
775           found = index_search(root, todo, NULL);
776           if (found != NULL) {
777               found_ie = found->data;
778               if (add)
779                   ++found_ie->depc;
780               else
781                   --found_ie->depc;
782           }
783       }
784
785       if (space != NULL)
786          todo = space + 1;
787       else
788          todo = NULL;
789    }
790 }
791
792 static Boolean index_initted;
793
794 /* Read and initialize global index */
795 int
796 index_initialize(char *path)
797 {
798     FILE *fp;
799     WINDOW *w = NULL;
800
801     if (!index_initted) {
802         w = savescr();
803         dialog_clear_norefresh();
804
805         /* Got any media? */
806         if (!mediaVerify()) {
807             restorescr(w);
808             return DITEM_FAILURE;
809         }
810
811         /* Does it move when you kick it? */
812         if (!DEVICE_INIT(mediaDevice)) {
813             restorescr(w);
814             return DITEM_FAILURE;
815         }
816
817         dialog_clear_norefresh();
818         msgNotify("Attempting to fetch %s file from selected media.", path);
819         fp = DEVICE_GET(mediaDevice, path, TRUE);
820         if (!fp) {
821             msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
822                        "This may be because the packages collection is not available\n"
823                        "on the distribution media you've chosen, most likely an FTP site\n"
824                        "without the packages collection mirrored.  Please verify that\n"
825                        "your media, or your path to the media, is correct and try again.");
826             DEVICE_SHUTDOWN(mediaDevice);
827             restorescr(w);
828             return DITEM_FAILURE;
829         }
830         dialog_clear_norefresh();
831         msgNotify("Located INDEX, now reading package data from it...");
832         index_init(&Top, &Plist);
833         if (index_read(fp, &Top)) {
834             msgConfirm("I/O or format error on packages/INDEX file.\n"
835                        "Please verify media (or path to media) and try again.");
836             fclose(fp);
837             restorescr(w);
838             return DITEM_FAILURE;
839         }
840         fclose(fp);
841         index_sort(&Top);
842         index_initted = TRUE;
843         restorescr(w);
844     }
845     return DITEM_SUCCESS;
846 }