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