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