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