]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/sysinstall/index.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / sysinstall / index.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * Copyright (c) 1995
8  *      Jordan Hubbard.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer,
15  *    verbatim and that no modifications are made prior to this
16  *    point in the file.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD$
34  */
35
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <ncurses.h>
41 #include <dialog.h>
42 #include "sysinstall.h"
43
44 /* Macros and magic values */
45 #define MAX_MENU        12
46 #define _MAX_DESC       55
47
48 /* A structure holding the root, top and plist pointer at once */
49 struct ListPtrs
50 {
51     PkgNodePtr root;    /* root of tree */
52     PkgNodePtr top;     /* part of tree we handle */
53     PkgNodePtr plist;   /* list of selected packages */
54 };
55 typedef struct ListPtrs* ListPtrsPtr;
56
57 static void     index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
58
59 /* Shared between index_initialize() and the various clients of it */
60 PkgNode Top, Plist;
61
62 /* Smarter strdup */
63 static inline char *
64 _strdup(char *ptr)
65 {
66     return ptr ? strdup(ptr) : NULL;
67 }
68
69 static char *descrs[] = {
70     "Package Selection", "To mark a package, move to it and press SPACE.  If the package is\n"
71     "already marked, it will be unmarked or deleted (if installed).\n"
72     "Items marked with a `D' are dependencies which will be auto-loaded.\n"
73     "To search for a package by name, press ESC.  To select a category,\n"
74     "press RETURN.  NOTE:  The All category selection creates a very large\n"
75     "submenu!  If you select it, please be patient while it comes up.",
76     "Package Targets", "These are the packages you've selected for extraction.\n\n"
77     "If you're sure of these choices, select OK.\n"
78     "If not, select Cancel to go back to the package selection menu.\n",
79     "All", "All available packages in all categories.",
80     "accessibility", "Ports to help disabled users.",
81     "afterstep", "Ports to support the AfterStep window manager.",
82     "arabic", "Ported software for Arab countries.",
83     "archivers", "Utilities for archiving and unarchiving data.",
84     "astro", "Applications related to astronomy.",
85     "audio", "Audio utilities - most require a supported sound card.",
86     "benchmarks", "Utilities for measuring system performance.",
87     "biology", "Software related to biology.",
88     "cad", "Computer Aided Design utilities.",
89     "chinese", "Ported software for the Chinese market.",
90     "comms", "Communications utilities.",
91     "converters", "Format conversion utilities.",
92     "databases", "Database software.",
93     "deskutils", "Various Desktop utilities.",
94     "devel", "Software development utilities and libraries.",
95     "dns", "Domain Name Service tools.",
96     "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     "kld", "Kernel loadable modules",
118     "korean", "Ported software for the Korean market.",
119     "lang", "Computer languages.",
120     "linux", "Linux programs that can run under binary compatibility.",
121     "lisp", "Software related to the Lisp language.",
122     "mail", "Electronic mail packages and utilities.",
123     "math", "Mathematical computation software.",
124     "mbone", "Applications and utilities for the MBONE.",
125     "misc", "Miscellaneous utilities.",
126     "multimedia", "Multimedia software.",
127     "net", "Networking utilities.",
128     "net-im", "Instant messaging software.",
129     "net-mgmt", "Network management tools.",
130     "net-p2p", "Peer to peer network applications.",
131     "news", "USENET News support software.",
132     "palm", "Software support for the Palm(tm) series.",
133     "parallel", "Applications dealing with parallelism in computing.",
134     "pear", "Software related to the Pear PHP framework.",
135     "perl5", "Utilities/modules for the PERL5 language.",
136     "plan9", "Software from the Plan9 operating system.",
137     "polish", "Ported software for the Polish market.",
138     "ports-mgmt", "Utilities for managing ports and packages.",
139     "portuguese", "Ported software for the Portuguese market.",
140     "print", "Utilities for dealing with printing.",
141     "python", "Software related to the Python language.",
142     "ruby", "Software related to the Ruby language.",
143     "rubygems", "Ports of RubyGems packages.",
144     "russian", "Ported software for the Russian market.",
145     "scheme", "Software related to the Scheme language.",
146     "science", "Scientific software.",
147     "security", "System security software.",
148     "shells", "Various shells (tcsh, bash, etc).",
149     "spanish", "Ported software for the Spanish market.",
150     "sysutils", "Various system utilities.",
151     "tcl", "TCL and packages that depend on it.",
152     "tcl80", "TCL v8.0 and packages that depend on it.",
153     "tcl82", "TCL v8.2 and packages that depend on it.",
154     "tcl83", "TCL v8.3 and packages that depend on it.",
155     "tcl84", "TCL v8.4 and packages that depend on it.",
156     "textproc", "Text processing/search utilities.",
157     "tk", "Tk and packages that depend on it.",
158     "tk80", "Tk8.0 and packages that depend on it.",
159     "tk82", "Tk8.2 and packages that depend on it.",
160     "tk83", "Tk8.3 and packages that depend on it.",
161     "tk84", "Tk8.4 and packages that depend on it.",
162     "tkstep80", "Ports to support the TkStep window manager.",
163     "ukrainian", "Ported software for the Ukrainian market.",
164     "vietnamese", "Ported software for the Vietnamese market.",
165     "windowmaker", "Ports to support the WindowMaker window manager.",
166     "www", "Web utilities (browsers, HTTP servers, etc).",
167     "x11", "X Window System based utilities.",
168     "x11-clocks", "X Window System based clocks.",
169     "x11-drivers", "X Window System drivers.",
170     "x11-fm", "X Window System based file managers.",
171     "x11-fonts", "X Window System fonts and font utilities.",
172     "x11-servers", "X Window System servers.",
173     "x11-themes", "X Window System themes.",
174     "x11-toolkits", "X Window System based development toolkits.",
175     "x11-wm", "X Window System window managers.",
176     "xfce", "Software related to the Xfce Desktop Environment.",
177     "zope", "Software related to the Zope platform.",
178     NULL, NULL,
179 };
180
181 static char *
182 fetch_desc(char *name)
183 {
184     int i;
185
186     for (i = 0; descrs[i]; i += 2) {
187         if (!strcmp(descrs[i], name))
188             return descrs[i + 1];
189     }
190     return "No description provided";
191 }
192
193 static PkgNodePtr
194 new_pkg_node(char *name, node_type type)
195 {
196     PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
197
198     tmp->name = _strdup(name);
199     tmp->type = type;
200     return tmp;
201 }
202
203 static char *
204 strip(char *buf)
205 {
206     int i;
207
208     for (i = 0; buf[i]; i++)
209         if (buf[i] == '\t' || buf[i] == '\n')
210             buf[i] = ' ';
211     return buf;
212 }
213
214 static IndexEntryPtr
215 new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
216 {
217     IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
218
219     tmp->name =         _strdup(name);
220     tmp->path =         _strdup(pathto);
221     tmp->prefix =       _strdup(prefix);
222     tmp->comment =      _strdup(comment);
223     tmp->descrfile =    strip(_strdup(descr));
224     tmp->maintainer =   _strdup(maint);
225     tmp->deps =         _strdup(deps);
226     tmp->depc =         0;
227     tmp->installed =    package_installed(name);
228     tmp->vol_checked =  0;
229     tmp->volume =       volume;
230     if (volume != 0) {
231         have_volumes = TRUE;
232         if (low_volume == 0)
233             low_volume = volume;
234         else if (low_volume > volume)
235             low_volume = volume;
236         if (high_volume < volume)
237             high_volume = volume;
238     }
239     return tmp;
240 }
241
242 static void
243 index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
244 {
245     PkgNodePtr p, q;
246
247     for (q = NULL, p = top->kids; p; p = p->next) {
248         if (!strcmp(p->name, where)) {
249             q = p;
250             break;
251         }
252     }
253     if (!p) {
254         /* Add new category */
255         q = new_pkg_node(where, PLACE);
256         q->desc = fetch_desc(where);
257         q->next = top->kids;
258         top->kids = q;
259     }
260     p = new_pkg_node(ptr->name, PACKAGE);
261     p->desc = ptr->comment;
262     p->data = ptr;
263     p->next = q->kids;
264     q->kids = p;
265 }
266
267 static int
268 copy_to_sep(char *to, char *from, int sep)
269 {
270     char *tok;
271
272     tok = strchr(from, sep);
273     if (!tok) {
274         *to = '\0';
275         return 0;
276     }
277     *tok = '\0';
278     strcpy(to, from);
279     return tok + 1 - from;
280 }
281
282 static int
283 skip_to_sep(char *from, int sep)
284 {
285     char *tok;
286
287     tok = strchr(from, sep);
288     if (!tok)
289         return 0;
290     *tok = '\0';
291     return tok + 1 - from;
292 }
293
294 static int
295 readline(FILE *fp, char *buf, int max)
296 {
297     int rv, i = 0;
298     char ch;
299
300     while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
301         buf[i++] = ch;
302     if (i < max)
303         buf[i] = '\0';
304     return rv;
305 }
306
307 /*
308  * XXX - this function should do error checking, and skip corrupted INDEX
309  * lines without a set number of '|' delimited fields.
310  */
311
312 static int
313 index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
314 {
315     char line[10240 + 2048 * 7];
316     char junk[2048];
317     char volstr[2048];
318     char *cp;
319     int i;
320
321     i = readline(fp, line, sizeof line);
322     if (i <= 0)
323         return EOF;
324     cp = line;
325     cp += copy_to_sep(name, cp, '|');           /* package name */
326     cp += copy_to_sep(pathto, cp, '|');         /* ports directory */
327     cp += copy_to_sep(prefix, cp, '|');         /* prefix */
328     cp += copy_to_sep(comment, cp, '|');        /* comment */
329     cp += copy_to_sep(descr, cp, '|');          /* path to pkg-descr */
330     cp += copy_to_sep(maint, cp, '|');          /* maintainer */
331     cp += copy_to_sep(cats, cp, '|');           /* categories */
332     cp += skip_to_sep(cp, '|');                 /* build deps - not used */
333     cp += copy_to_sep(rdeps, cp, '|');          /* run deps */
334     if (index(cp, '|'))
335         cp += skip_to_sep(cp, '|');             /* url - not used */
336     else {
337         strncpy(junk, cp, 1023);
338         *volume = 0;
339         return 0;
340     }
341     if (index(cp, '|'))
342         cp += skip_to_sep(cp, '|');             /* extract deps - not used */
343     if (index(cp, '|'))
344         cp += skip_to_sep(cp, '|');             /* patch deps - not used */
345     if (index(cp, '|'))
346         cp += skip_to_sep(cp, '|');             /* fetch deps - not used */
347     if (index(cp, '|'))
348         cp += copy_to_sep(volstr, cp, '|');     /* media volume */
349     else {
350         strncpy(volstr, cp, 1023);
351     }
352     *volume = atoi(volstr);
353     return 0;
354 }
355
356 int
357 index_read(FILE *fp, PkgNodePtr papa)
358 {
359     char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048 * 8];
360     int volume;
361     PkgNodePtr i;
362
363     while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
364         char *cp, *cp2, tmp[1024];
365         IndexEntryPtr idx;
366
367         idx = new_index(name, pathto, prefix, comment, descr, maint, deps, volume);
368         /* For now, we only add things to menus if they're in categories.  Keywords are ignored */
369         for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
370             *cp2 = '\0';
371             index_register(papa, cp, idx);
372         }
373         index_register(papa, cp, idx);
374
375         /* Add to special "All" category */
376         index_register(papa, "All", idx);
377     }
378
379     /* Adjust dependency counts */
380     for (i = papa->kids; i != NULL; i = i->next)
381         if (strcmp(i->name, "All") == 0)
382             break;
383     for (i = i->kids; i != NULL; i = i->next)
384         if (((IndexEntryPtr)i->data)->installed)
385             index_recorddeps(TRUE, papa, i->data);
386
387     return 0;
388 }
389
390 void
391 index_init(PkgNodePtr top, PkgNodePtr plist)
392 {
393     if (top) {
394         top->next = top->kids = NULL;
395         top->name = "Package Selection";
396         top->type = PLACE;
397         top->desc = fetch_desc(top->name);
398         top->data = NULL;
399     }
400     if (plist) {
401         plist->next = plist->kids = NULL;
402         plist->name = "Package Targets";
403         plist->type = PLACE;
404         plist->desc = fetch_desc(plist->name);
405         plist->data = NULL;
406     }
407 }
408
409 void
410 index_print(PkgNodePtr top, int level)
411 {
412     int i;
413
414     while (top) {
415         for (i = 0; i < level; i++) putchar('\t');
416         printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
417         for (i = 0; i < level; i++) putchar('\t');
418         printf("desc: %s\n", top->desc);
419         if (top->kids)
420             index_print(top->kids, level + 1);
421         top = top->next;
422     }
423 }
424
425 /* Swap one node for another */
426 static void
427 swap_nodes(PkgNodePtr a, PkgNodePtr b)
428 {
429     PkgNode tmp;
430
431     tmp = *a;
432     *a = *b;
433     a->next = tmp.next;
434     tmp.next = b->next;
435     *b = tmp;
436 }
437
438 /* Use a disgustingly simplistic bubble sort to put our lists in order */
439 void
440 index_sort(PkgNodePtr top)
441 {
442     PkgNodePtr p, q;
443
444     /* Sort everything at the top level */
445     for (p = top->kids; p; p = p->next) {
446         for (q = top->kids; q; q = q->next) {
447             if (q->next && strcmp(q->name, q->next->name) > 0)
448                 swap_nodes(q, q->next);
449         }
450     }
451
452     /* Now sub-sort everything n levels down */    
453     for (p = top->kids; p; p = p->next) {
454         if (p->kids)
455             index_sort(p);
456     }
457 }
458
459 /* Delete an entry out of the list it's in (only the plist, at present) */
460 static void
461 index_delete(PkgNodePtr n)
462 {
463     if (n->next) {
464         PkgNodePtr p = n->next;
465
466         *n = *(n->next);
467         safe_free(p);
468     }
469     else /* Kludgy end sentinal */
470         n->name = NULL;
471 }
472
473 /*
474  * Search for a given node by name, returning the category in if
475  * tp is non-NULL.
476  */
477 PkgNodePtr
478 index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
479 {
480     PkgNodePtr p, sp;
481
482     for (p = top->kids; p && p->name; p = p->next) {
483         if (p->type == PACKAGE) {
484             /* If tp == NULL, we're looking for an exact package match */
485             if (!tp && !strcmp(p->name, str))
486                 return p;
487
488             /* If tp, we're looking for both a package and a pointer to the place it's in */
489             if (tp && !strncmp(p->name, str, strlen(str))) {
490                 *tp = top;
491                 return p;
492             }
493         }
494         else if (p->kids) {
495             /* The usual recursion-out-of-laziness ploy */
496             if ((sp = index_search(p, str, tp)) != NULL)
497                 return sp;
498         }
499     }
500     if (p && !p->name)
501         p = NULL;
502     return p;
503 }
504
505 static int
506 pkg_checked(dialogMenuItem *self)
507 {
508     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
509     PkgNodePtr kp = self->data, plist = lists->plist;
510     int i;
511
512     i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
513     if (kp->type == PACKAGE && plist) {
514         IndexEntryPtr ie = kp->data;
515         int markD, markX;
516
517         markD = ie->depc > 0; /* needed as dependency */
518         markX = i || ie->installed; /* selected or installed */
519         self->mark = markX ? 'X' : 'D';
520         return markD || markX;
521     } else
522         return FALSE;
523 }
524
525 static int
526 pkg_fire(dialogMenuItem *self)
527 {
528     int ret;
529     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
530     PkgNodePtr sp, kp = self->data, plist = lists->plist;
531
532     if (!plist)
533         ret = DITEM_FAILURE;
534     else if (kp->type == PACKAGE) {
535         IndexEntryPtr ie = kp->data;
536
537         sp = index_search(plist, kp->name, NULL);
538         /* Not already selected? */
539         if (!sp) {
540             if (!ie->installed) {
541                 PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
542
543                 *np = *kp;
544                 np->next = plist->kids;
545                 plist->kids = np;
546                 index_recorddeps(TRUE, lists->root, ie);
547                 msgInfo("Added %s to selection list", kp->name);
548             }
549             else if (ie->depc == 0) {
550                 if (!msgNoYes("Do you really want to delete %s from the system?", kp->name)) {
551                     if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
552                         msgConfirm("Warning:  pkg_delete of %s failed.\n  Check debug output for details.", kp->name);
553                     }
554                     else {
555                         ie->installed = 0;
556                         index_recorddeps(FALSE, lists->root, ie);
557                     }
558                 }
559             }
560             else
561                 msgConfirm("Warning: Package %s is needed by\n  %d other installed package%s.",
562                            kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
563         }
564         else {
565             index_recorddeps(FALSE, lists->root, ie);
566             msgInfo("Removed %s from selection list", kp->name);
567             index_delete(sp);
568         }
569         ret = DITEM_SUCCESS;
570         /* Mark menu for redraw if we had dependencies */
571         if (strlen(ie->deps) > 0)
572             ret |= DITEM_REDRAW;
573     }
574     else {      /* Not a package, must be a directory */
575         int p, s;
576                     
577         p = s = 0;
578         index_menu(lists->root, kp, plist, &p, &s);
579         ret = DITEM_SUCCESS | DITEM_CONTINUE;
580     }
581     return ret;
582 }
583
584 static void
585 pkg_selected(dialogMenuItem *self, int is_selected)
586 {
587     PkgNodePtr kp = self->data;
588
589     if (!is_selected || kp->type != PACKAGE)
590         return;
591     msgInfo("%s", kp->desc);
592 }
593
594 int
595 index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
596 {
597     struct ListPtrs lists;
598     size_t maxname;
599     int n, rval;
600     int curr, max;
601     PkgNodePtr kp;
602     dialogMenuItem *nitems;
603     Boolean hasPackages;
604     WINDOW *w;
605     
606     lists.root = root;
607     lists.top = top;
608     lists.plist = plist;
609
610     hasPackages = FALSE;
611     nitems = NULL;
612     n = maxname = 0;
613
614     /* Figure out if this menu is full of "leaves" or "branches" */
615     for (kp = top->kids; kp && kp->name; kp = kp->next) {
616         size_t len;
617
618         ++n;
619         if (kp->type == PACKAGE && plist) {
620             hasPackages = TRUE;
621             if ((len = strlen(kp->name)) > maxname)
622                 maxname = len;
623         }
624     }
625     if (!n && plist) {
626         msgConfirm("The %s menu is empty.", top->name);
627         return DITEM_LEAVE_MENU;
628     }
629
630     w = savescr();
631     while (1) {
632         n = 0;
633         curr = max = 0;
634         use_helpline(NULL);
635         use_helpfile(NULL);
636         kp = top->kids;
637         if (!hasPackages && plist) {
638             nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
639             nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
640         }
641         while (kp && kp->name) {
642             char buf[256];
643             IndexEntryPtr ie = kp->data;
644
645             /* Brutally adjust description to fit in menu */
646             if (kp->type == PACKAGE)
647                 snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
648             else
649                 SAFE_STRCPY(buf, kp->desc);
650             if (strlen(buf) > (_MAX_DESC - maxname))
651                 buf[_MAX_DESC - maxname] = '\0';
652             nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked, 
653                               pkg_fire, pkg_selected, kp, (int *)(&lists), 
654                               &curr, &max);
655             ++n;
656             kp = kp->next;
657         }
658         /* NULL delimiter so item_free() knows when to stop later */
659         nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
660                           &curr, &max);
661
662 recycle:
663         dialog_clear_norefresh();
664         if (hasPackages)
665             rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
666         else
667             rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
668         if (rval == -1 && plist) {
669             static char *cp;
670             PkgNodePtr menu;
671
672             /* Search */
673             if ((cp = msgGetInput(cp, "Search by package name.  Please enter search string:")) != NULL) {
674                 PkgNodePtr p = index_search(top, cp, &menu);
675
676                 if (p) {
677                     int pos, scroll;
678
679                     /* These need to be set to point at the found item, actually.  Hmmm! */
680                     pos = scroll = 0;
681                     index_menu(root, menu, plist, &pos, &scroll);
682                 }
683                 else
684                     msgConfirm("Search string: %s yielded no hits.", cp);
685             }
686             goto recycle;
687         }
688         items_free(nitems, &curr, &max);
689         restorescr(w);
690         return rval ? DITEM_FAILURE : DITEM_SUCCESS;
691     }
692 }
693
694 int
695 index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended,
696     int current_volume)
697 {
698     int status = DITEM_SUCCESS;
699     Boolean notyet = FALSE;
700     PkgNodePtr tmp2;
701     IndexEntryPtr id = who->data;
702     WINDOW *w;
703
704     /* 
705      * Short-circuit the package dependency checks.  We're already
706      * maintaining a data structure of installed packages, so if a
707      * package is already installed, don't try to check to make sure
708      * that all of its dependencies are installed.  At best this
709      * wastes a ton of cycles and can cause minor delays between
710      * package extraction.  At worst it can cause an infinite loop with
711      * a certain faulty INDEX file. 
712      */
713
714     if (id->installed == 1 || (have_volumes && id->vol_checked == current_volume))
715         return DITEM_SUCCESS;
716
717     w = savescr();
718     if (id && id->deps && strlen(id->deps)) {
719         char t[2048 * 8], *cp, *cp2;
720
721         SAFE_STRCPY(t, id->deps);
722         cp = t;
723         while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
724             if ((cp2 = index(cp, ' ')) != NULL)
725                 *cp2 = '\0';
726             if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
727                 status = index_extract(dev, top, tmp2, TRUE, current_volume);
728                 if (DITEM_STATUS(status) != DITEM_SUCCESS) {
729                     /* package probably on a future disc volume */
730                     if (status & DITEM_CONTINUE) {
731                         status = DITEM_SUCCESS;
732                         notyet = TRUE;
733                     } else if (variable_get(VAR_NO_CONFIRM))
734                         msgNotify("Loading of dependent package %s failed", cp);
735                     else
736                         msgConfirm("Loading of dependent package %s failed", cp);
737                 }
738             }
739             else if (!package_installed(cp)) {
740                 if (variable_get(VAR_NO_CONFIRM))
741                     msgNotify("Warning: %s is a required package but was not found.", cp);
742                 else
743                     msgConfirm("Warning: %s is a required package but was not found.", cp);
744             }
745             if (cp2)
746                 cp = cp2 + 1;
747             else
748                 cp = NULL;
749         }
750     }
751
752     /*
753      * If iterating through disc volumes one at a time indicate failure if
754      * dependency install failed due to package being on a higher volume
755      * numbered disc, but that we should continue anyway.  Note that this
756      * package has already been processed for this disc volume so we don't
757      * need to do it again.
758      */
759
760     if (notyet) {
761         restorescr(w);
762         id->vol_checked = current_volume;
763         return DITEM_FAILURE | DITEM_CONTINUE;
764     }
765
766     /*
767      * Done with the deps?  Try to load the real m'coy.  If iterating
768      * through a multi-volume disc set fail the install if the package
769      * is on a higher numbered volume to cut down on disc switches the
770      * user needs to do, but indicate caller should continue processing
771      * despite error return.  Note this package was processed for the
772      * current disc being checked.
773      */
774
775     if (DITEM_STATUS(status) == DITEM_SUCCESS) {
776         /* Prompt user if the package is not available on the current volume. */
777         if(mediaDevice->type == DEVICE_TYPE_CDROM) {
778             if (current_volume != 0 && id->volume > current_volume) {
779                 restorescr(w);
780                 id->vol_checked = current_volume;
781                 return DITEM_FAILURE | DITEM_CONTINUE;
782             }
783             while (id->volume != dev->volume) {
784                 if (!msgYesNo("This is disc #%d.  Package %s is on disc #%d\n"
785                           "Would you like to switch discs now?\n", dev->volume,
786                           id->name, id->volume)) {
787                     DEVICE_SHUTDOWN(mediaDevice);
788                     msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
789                         dev->volume, id->volume);
790                     DEVICE_INIT(mediaDevice);
791                 } else {
792                     restorescr(w);
793                     return DITEM_FAILURE;
794                 }
795             }
796         }
797         status = package_extract(dev, who->name, depended);
798         if (DITEM_STATUS(status) == DITEM_SUCCESS)
799             id->installed = 1;
800     }
801     restorescr(w);
802     return status;
803 }
804
805 static void
806 index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
807 {
808    char depends[1024 * 16], *space, *todo;
809    PkgNodePtr found;
810    IndexEntryPtr found_ie;
811
812    SAFE_STRCPY(depends, ie->deps);
813    for (todo = depends; todo != NULL; ) {
814       space = index(todo, ' ');
815       if (space != NULL)
816          *space = '\0';
817
818       if (strlen(todo) > 0) { /* only non-empty dependencies */
819           found = index_search(root, todo, NULL);
820           if (found != NULL) {
821               found_ie = found->data;
822               if (add)
823                   ++found_ie->depc;
824               else
825                   --found_ie->depc;
826           }
827       }
828
829       if (space != NULL)
830          todo = space + 1;
831       else
832          todo = NULL;
833    }
834 }
835
836 static Boolean index_initted;
837
838 /* Read and initialize global index */
839 int
840 index_initialize(char *path)
841 {
842     FILE *fp;
843     WINDOW *w = NULL;
844
845     if (!index_initted) {
846         w = savescr();
847         dialog_clear_norefresh();
848         have_volumes = FALSE;
849         low_volume = high_volume = 0;
850
851         /* Got any media? */
852         if (!mediaVerify()) {
853             restorescr(w);
854             return DITEM_FAILURE;
855         }
856
857         /* Does it move when you kick it? */
858         if (!DEVICE_INIT(mediaDevice)) {
859             restorescr(w);
860             return DITEM_FAILURE;
861         }
862
863         dialog_clear_norefresh();
864         msgNotify("Attempting to fetch %s file from selected media.", path);
865         fp = DEVICE_GET(mediaDevice, path, TRUE);
866         if (!fp) {
867             msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
868                        "This may be because the packages collection is not available\n"
869                        "on the distribution media you've chosen, most likely an FTP site\n"
870                        "without the packages collection mirrored.  Please verify that\n"
871                        "your media, or your path to the media, is correct and try again.");
872             DEVICE_SHUTDOWN(mediaDevice);
873             restorescr(w);
874             return DITEM_FAILURE;
875         }
876         dialog_clear_norefresh();
877         msgNotify("Located INDEX, now reading package data from it...");
878         index_init(&Top, &Plist);
879         if (index_read(fp, &Top)) {
880             msgConfirm("I/O or format error on packages/INDEX file.\n"
881                        "Please verify media (or path to media) and try again.");
882             fclose(fp);
883             restorescr(w);
884             return DITEM_FAILURE;
885         }
886         fclose(fp);
887         index_sort(&Top);
888         index_initted = TRUE;
889         restorescr(w);
890     }
891     return DITEM_SUCCESS;
892 }