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