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