]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/sysinstall/index.c
Add new 'ruby' ports category.
[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  * $FreeBSD$
8  *
9  * Copyright (c) 1995
10  *      Jordan Hubbard.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer,
17  *    verbatim and that no modifications are made prior to this
18  *    point in the file.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <ncurses.h>
42 #include <dialog.h>
43 #include "sysinstall.h"
44
45 /* Macros and magic values */
46 #define MAX_MENU        12
47 #define _MAX_DESC       55
48
49 /* A structure holding the root, top and plist pointer at once */
50 struct ListPtrs
51 {
52     PkgNodePtr root;    /* root of tree */
53     PkgNodePtr top;     /* part of tree we handle */
54     PkgNodePtr plist;   /* list of selected packages */
55 };
56 typedef struct ListPtrs* ListPtrsPtr;
57
58 static void     index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
59
60 /* Shared between index_initialize() and the various clients of it */
61 PkgNode Top, Plist;
62
63 /* Smarter strdup */
64 inline char *
65 _strdup(char *ptr)
66 {
67     return ptr ? strdup(ptr) : NULL;
68 }
69
70 static char *descrs[] = {
71     "Package Selection", "To mark a package, move to it and press SPACE.  If the package is\n"
72     "already marked, it will be unmarked or deleted (if installed).\n"
73     "Items marked with a `D' are dependencies which will be auto-loaded.\n"
74     "To search for a package by name, press ESC.  To select a category,\n"
75     "press RETURN.  NOTE:  The All category selection creates a very large\n"
76     "submenu!  If you select it, please be patient while it comes up.",
77     "Package Targets", "These are the packages you've selected for extraction.\n\n"
78     "If you're sure of these choices, select OK.\n"
79     "If not, select Cancel to go back to the package selection menu.\n",
80     "All", "All available packages in all categories.",
81     "afterstep", "Ports to support the AfterStep window manager.",
82     "applications", "User application software.",
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     "documentation", "Document preparation utilities.",
96     "editors", "Common text editors.",
97     "elisp", "Things related to Emacs Lisp.",
98     "emulators", "Utilities for emulating other OS types.",
99     "ftp", "FTP client and server utilities.",
100     "games", "Various and sundry amusements.",
101     "german", "Ported software for Germanic countries.",
102     "gnome", "Components of the Gnome Desktop environment.",
103     "graphics", "Graphics libraries and utilities.",
104     "ipv6", "IPv6 related software.",
105     "hebrew", "Ported software for Hebrew language.",
106     "irc", "Internet Relay Chat utilities.",
107     "japanese", "Ported software for the Japanese market.",
108     "java", "Java language support.",
109     "kde", "Software for the K Desktop Environment.",
110     "korean", "Ported software for the Korean market.",
111     "lang", "Computer languages.",
112     "languages", "Computer languages.",
113     "libraries", "Software development libraries.",
114     "mail", "Electronic mail packages and utilities.",
115     "math", "Mathematical computation software.",
116     "mbone", "Applications and utilities for the MBONE.",
117     "misc", "Miscellaneous utilities.",
118     "net", "Networking utilities.",
119     "news", "USENET News support software.",
120     "numeric", "Mathematical computation software.",
121     "offix", "An office automation suite of sorts.",
122     "orphans", "Packages without a home elsewhere.",
123     "palm", "Software support for the 3Com Palm Pilot(tm) series.",
124     "perl5", "Utilities/modules for the PERL5 language.",
125     "pilot", "Software support for the 3Com Palm Pilot(tm) series.",
126     "plan9", "Software from the Plan9 operating system.",
127     "print", "Utilities for dealing with printing.",
128     "printing", "Utilities for dealing with printing.",
129     "programming", "Software development utilities and libraries.",
130     "python", "Software related to the Python language.",
131     "ruby", "Software related to the Ruby language.",
132     "russian", "Ported software for the Russian market.",
133     "security", "System security software.",
134     "shells", "Various shells (tcsh, bash, etc).",
135     "sysutils", "Various system utilities.",
136     "tcl75", "TCL v7.5 and packages that depend on it.",
137     "tcl76", "TCL v7.6 and packages that depend on it.",
138     "tcl80", "TCL v8.0 and packages that depend on it.",
139     "tcl82", "TCL v8.2 and packages that depend on it.",
140     "textproc", "Text processing/search utilities.",
141     "tk41", "Tk4.1 and packages that depend on it.",
142     "tk42", "Tk4.2 and packages that depend on it.",
143     "tk80", "Tk8.0 and packages that depend on it.",
144     "tk81", "Tk8.1 and packages that depend on it.",
145     "tk82", "Tk8.2 and packages that depend on it.",
146     "tkstep80", "tkstep wm and packages that depend on it.",
147     "troff", "TROFF text formatting utilities.",
148     "vietnamese", "Ported software for the Vietnamese market.",
149     "windowmaker", "Ports to support the WindowMaker window manager.",
150     "www", "WEB utilities (browers, HTTP servers, etc).",
151     "x11", "X Window System based utilities.",
152     "x11-clocks", "X Window System based clocks.",
153     "x11-fm", "X Window System based file managers.",
154     "x11-fonts", "X Window System fonts and font utilities.",
155     "x11-servers", "X Window System servers.",
156     "x11-toolkits", "X Window System based development toolkits.",
157     "x11-wm", "X Window System window managers.",
158     NULL, NULL,
159 };
160
161 static char *
162 fetch_desc(char *name)
163 {
164     int i;
165
166     for (i = 0; descrs[i]; i += 2) {
167         if (!strcmp(descrs[i], name))
168             return descrs[i + 1];
169     }
170     return "No description provided";
171 }
172
173 static PkgNodePtr
174 new_pkg_node(char *name, node_type type)
175 {
176     PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
177
178     tmp->name = _strdup(name);
179     tmp->type = type;
180     return tmp;
181 }
182
183 static char *
184 strip(char *buf)
185 {
186     int i;
187
188     for (i = 0; buf[i]; i++)
189         if (buf[i] == '\t' || buf[i] == '\n')
190             buf[i] = ' ';
191     return buf;
192 }
193
194 static IndexEntryPtr
195 new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps)
196 {
197     IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
198
199     tmp->name =         _strdup(name);
200     tmp->path =         _strdup(pathto);
201     tmp->prefix =       _strdup(prefix);
202     tmp->comment =      _strdup(comment);
203     tmp->descrfile =    strip(_strdup(descr));
204     tmp->maintainer =   _strdup(maint);
205     tmp->deps =         _strdup(deps);
206     tmp->depc =         0;
207     tmp->installed =    package_exists(name);
208     return tmp;
209 }
210
211 static void
212 index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
213 {
214     PkgNodePtr p, q;
215
216     for (q = NULL, p = top->kids; p; p = p->next) {
217         if (!strcmp(p->name, where)) {
218             q = p;
219             break;
220         }
221     }
222     if (!p) {
223         /* Add new category */
224         q = new_pkg_node(where, PLACE);
225         q->desc = fetch_desc(where);
226         q->next = top->kids;
227         top->kids = q;
228     }
229     p = new_pkg_node(ptr->name, PACKAGE);
230     p->desc = ptr->comment;
231     p->data = ptr;
232     p->next = q->kids;
233     q->kids = p;
234 }
235
236 static int
237 copy_to_sep(char *to, char *from, int sep)
238 {
239     char *tok;
240
241     tok = strchr(from, sep);
242     if (!tok) {
243         *to = '\0';
244         return 0;
245     }
246     *tok = '\0';
247     strcpy(to, from);
248     return tok + 1 - from;
249 }
250
251 static int
252 readline(FILE *fp, char *buf, int max)
253 {
254     int rv, i = 0;
255     char ch;
256
257     while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
258         buf[i++] = ch;
259     if (i < max)
260         buf[i] = '\0';
261     return rv;
262 }
263
264 int
265 index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps)
266 {
267     char line[2048];
268     char junk[511];
269     char *cp;
270     int i;
271
272     i = readline(fp, line, sizeof line);
273     if (i <= 0)
274         return EOF;
275     cp = line;
276     cp += copy_to_sep(name, cp, '|');
277     cp += copy_to_sep(pathto, cp, '|');
278     cp += copy_to_sep(prefix, cp, '|');
279     cp += copy_to_sep(comment, cp, '|');
280     cp += copy_to_sep(descr, cp, '|');
281     cp += copy_to_sep(maint, cp, '|');
282     cp += copy_to_sep(cats, cp, '|');
283     cp += copy_to_sep(junk, cp, '|');   /* build deps - not used */
284     if (index(cp, '|'))
285         copy_to_sep(rdeps, cp, '|');
286     else
287         strncpy(rdeps, cp, 1023);
288     return 0;
289 }
290
291 int
292 index_read(FILE *fp, PkgNodePtr papa)
293 {
294     char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[1024];
295     PkgNodePtr i;
296
297     while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps) != EOF) {
298         char *cp, *cp2, tmp[1024];
299         IndexEntryPtr idx;
300
301         idx = new_index(name, pathto, prefix, comment, descr, maint, deps);
302         /* For now, we only add things to menus if they're in categories.  Keywords are ignored */
303         for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
304             *cp2 = '\0';
305             index_register(papa, cp, idx);
306         }
307         index_register(papa, cp, idx);
308
309         /* Add to special "All" category */
310         index_register(papa, "All", idx);
311     }
312
313     /* Adjust dependency counts */
314     for (i = papa->kids; i != NULL; i = i->next)
315         if (strcmp(i->name, "All") == 0)
316             break;
317     for (i = i->kids; i != NULL; i = i->next)
318         if (((IndexEntryPtr)i->data)->installed)
319             index_recorddeps(TRUE, papa, i->data);
320
321     return 0;
322 }
323
324 void
325 index_init(PkgNodePtr top, PkgNodePtr plist)
326 {
327     if (top) {
328         top->next = top->kids = NULL;
329         top->name = "Package Selection";
330         top->type = PLACE;
331         top->desc = fetch_desc(top->name);
332         top->data = NULL;
333     }
334     if (plist) {
335         plist->next = plist->kids = NULL;
336         plist->name = "Package Targets";
337         plist->type = PLACE;
338         plist->desc = fetch_desc(plist->name);
339         plist->data = NULL;
340     }
341 }
342
343 void
344 index_print(PkgNodePtr top, int level)
345 {
346     int i;
347
348     while (top) {
349         for (i = 0; i < level; i++) putchar('\t');
350         printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
351         for (i = 0; i < level; i++) putchar('\t');
352         printf("desc: %s\n", top->desc);
353         if (top->kids)
354             index_print(top->kids, level + 1);
355         top = top->next;
356     }
357 }
358
359 /* Swap one node for another */
360 static void
361 swap_nodes(PkgNodePtr a, PkgNodePtr b)
362 {
363     PkgNode tmp;
364
365     tmp = *a;
366     *a = *b;
367     a->next = tmp.next;
368     tmp.next = b->next;
369     *b = tmp;
370 }
371
372 /* Use a disgustingly simplistic bubble sort to put our lists in order */
373 void
374 index_sort(PkgNodePtr top)
375 {
376     PkgNodePtr p, q;
377
378     /* Sort everything at the top level */
379     for (p = top->kids; p; p = p->next) {
380         for (q = top->kids; q; q = q->next) {
381             if (q->next && strcmp(q->name, q->next->name) > 0)
382                 swap_nodes(q, q->next);
383         }
384     }
385
386     /* Now sub-sort everything n levels down */    
387     for (p = top->kids; p; p = p->next) {
388         if (p->kids)
389             index_sort(p);
390     }
391 }
392
393 /* Delete an entry out of the list it's in (only the plist, at present) */
394 void
395 index_delete(PkgNodePtr n)
396 {
397     if (n->next) {
398         PkgNodePtr p = n->next;
399
400         *n = *(n->next);
401         safe_free(p);
402     }
403     else /* Kludgy end sentinal */
404         n->name = NULL;
405 }
406
407 /*
408  * Search for a given node by name, returning the category in if
409  * tp is non-NULL.
410  */
411 PkgNodePtr
412 index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
413 {
414     PkgNodePtr p, sp;
415
416     for (p = top->kids; p && p->name; p = p->next) {
417         if (p->type == PACKAGE) {
418             /* If tp == NULL, we're looking for an exact package match */
419             if (!tp && !strcmp(p->name, str))
420                 return p;
421
422             /* If tp, we're looking for both a package and a pointer to the place it's in */
423             if (tp && !strncmp(p->name, str, strlen(str))) {
424                 *tp = top;
425                 return p;
426             }
427         }
428         else if (p->kids) {
429             /* The usual recursion-out-of-laziness ploy */
430             if ((sp = index_search(p, str, tp)) != NULL)
431                 return sp;
432         }
433     }
434     if (p && !p->name)
435         p = NULL;
436     return p;
437 }
438
439 int
440 pkg_checked(dialogMenuItem *self)
441 {
442     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
443     PkgNodePtr kp = self->data, plist = lists->plist;
444     int i;
445
446     i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
447     if (kp->type == PACKAGE && plist) {
448         IndexEntryPtr ie = kp->data;
449         int markD, markX;
450
451         markD = ie->depc > 0; /* needed as dependency */
452         markX = i || ie->installed; /* selected or installed */
453         self->mark = markX ? 'X' : 'D';
454         return markD || markX;
455     } else
456         return FALSE;
457 }
458
459 int
460 pkg_fire(dialogMenuItem *self)
461 {
462     int ret;
463     ListPtrsPtr lists = (ListPtrsPtr)self->aux;
464     PkgNodePtr sp, kp = self->data, plist = lists->plist;
465
466     if (!plist)
467         ret = DITEM_FAILURE;
468     else if (kp->type == PACKAGE) {
469         IndexEntryPtr ie = kp->data;
470
471         sp = index_search(plist, kp->name, NULL);
472         /* Not already selected? */
473         if (!sp) {
474             if (!ie->installed) {
475                 PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
476
477                 *np = *kp;
478                 np->next = plist->kids;
479                 plist->kids = np;
480                 index_recorddeps(TRUE, lists->root, ie);
481                 msgInfo("Added %s to selection list", kp->name);
482             }
483             else if (ie->depc == 0) {
484                 if (!msgYesNo("Do you really want to delete %s from the system?", kp->name)) {
485                     if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
486                         msgConfirm("Warning:  pkg_delete of %s failed.\n  Check debug output for details.", kp->name);
487                     }
488                     else {
489                         ie->installed = 0;
490                         index_recorddeps(FALSE, lists->root, ie);
491                     }
492                 }
493             }
494             else
495                 msgConfirm("Warning: Package %s is needed by\n  %d other installed package%s.",
496                            kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
497         }
498         else {
499             index_recorddeps(FALSE, lists->root, ie);
500             msgInfo("Removed %s from selection list", kp->name);
501             index_delete(sp);
502         }
503         ret = DITEM_SUCCESS;
504         /* Mark menu for redraw if we had dependencies */
505         if (strlen(ie->deps) > 0)
506             ret |= DITEM_REDRAW;
507     }
508     else {      /* Not a package, must be a directory */
509         int p, s;
510                     
511         p = s = 0;
512         index_menu(lists->root, kp, plist, &p, &s);
513         ret = DITEM_SUCCESS | DITEM_CONTINUE;
514     }
515     return ret;
516 }
517
518 void
519 pkg_selected(dialogMenuItem *self, int is_selected)
520 {
521     PkgNodePtr kp = self->data;
522
523     if (!is_selected || kp->type != PACKAGE)
524         return;
525     msgInfo(kp->desc);
526 }
527
528 int
529 index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
530 {
531     struct ListPtrs lists;
532     int n, rval, maxname;
533     int curr, max;
534     PkgNodePtr kp;
535     dialogMenuItem *nitems;
536     Boolean hasPackages;
537     WINDOW *w;
538     
539     lists.root = root;
540     lists.top = top;
541     lists.plist = plist;
542
543     hasPackages = FALSE;
544     nitems = NULL;
545     n = maxname = 0;
546
547     /* Figure out if this menu is full of "leaves" or "branches" */
548     for (kp = top->kids; kp && kp->name; kp = kp->next) {
549         int len;
550
551         ++n;
552         if (kp->type == PACKAGE && plist) {
553             hasPackages = TRUE;
554             if ((len = strlen(kp->name)) > maxname)
555                 maxname = len;
556         }
557     }
558     if (!n && plist) {
559         msgConfirm("The %s menu is empty.", top->name);
560         return DITEM_LEAVE_MENU;
561     }
562
563     w = savescr();
564     while (1) {
565         n = 0;
566         curr = max = 0;
567         use_helpline(NULL);
568         use_helpfile(NULL);
569         kp = top->kids;
570         if (!hasPackages && plist) {
571             nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, 0, &curr, &max);
572             nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, 0, &curr, &max);
573         }
574         while (kp && kp->name) {
575             char buf[256];
576             IndexEntryPtr ie = kp->data;
577
578             /* Brutally adjust description to fit in menu */
579             if (kp->type == PACKAGE)
580                 snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
581             else
582                 SAFE_STRCPY(buf, kp->desc);
583             if (strlen(buf) > (_MAX_DESC - maxname))
584                 buf[_MAX_DESC - maxname] = '\0';
585             nitems = item_add(nitems, kp->name, buf, pkg_checked, pkg_fire, pkg_selected, kp, (int)&lists, &curr, &max);
586             ++n;
587             kp = kp->next;
588         }
589         /* NULL delimiter so item_free() knows when to stop later */
590         nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, 0, &curr, &max);
591
592 recycle:
593         dialog_clear_norefresh();
594         if (hasPackages)
595             rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
596         else
597             rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
598         if (rval == -1 && plist) {
599             static char *cp;
600             PkgNodePtr menu;
601
602             /* Search */
603             if ((cp = msgGetInput(cp, "Search by package name.  Please enter search string:")) != NULL) {
604                 PkgNodePtr p = index_search(top, cp, &menu);
605
606                 if (p) {
607                     int pos, scroll;
608
609                     /* These need to be set to point at the found item, actually.  Hmmm! */
610                     pos = scroll = 0;
611                     index_menu(root, menu, plist, &pos, &scroll);
612                 }
613                 else
614                     msgConfirm("Search string: %s yielded no hits.", cp);
615             }
616             goto recycle;
617         }
618         items_free(nitems, &curr, &max);
619         restorescr(w);
620         return rval ? DITEM_FAILURE : DITEM_SUCCESS;
621     }
622 }
623
624 int
625 index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
626 {
627     int status = DITEM_SUCCESS;
628     PkgNodePtr tmp2;
629     IndexEntryPtr id = who->data;
630     WINDOW *w = savescr();
631
632     if (id && id->deps && strlen(id->deps)) {
633         char t[1024], *cp, *cp2;
634
635         SAFE_STRCPY(t, id->deps);
636         cp = t;
637         while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
638             if ((cp2 = index(cp, ' ')) != NULL)
639                 *cp2 = '\0';
640             if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
641                 status = index_extract(dev, top, tmp2, TRUE);
642                 if (DITEM_STATUS(status) != DITEM_SUCCESS) {
643                     if (variable_get(VAR_NO_CONFIRM))
644                         msgNotify("Loading of dependant package %s failed", cp);
645                     else
646                         msgConfirm("Loading of dependant package %s failed", cp);
647                 }
648             }
649             else if (!package_exists(cp)) {
650                 if (variable_get(VAR_NO_CONFIRM))
651                     msgNotify("Warning: %s is a required package but was not found.", cp);
652                 else
653                     msgConfirm("Warning: %s is a required package but was not found.", cp);
654             }
655             if (cp2)
656                 cp = cp2 + 1;
657             else
658                 cp = NULL;
659         }
660     }
661     /* Done with the deps?  Load the real m'coy */
662     if (DITEM_STATUS(status) == DITEM_SUCCESS) {
663         status = package_extract(dev, who->name, depended);
664         if (DITEM_STATUS(status) == DITEM_SUCCESS)
665             id->installed = 1;
666     }
667     restorescr(w);
668     return status;
669 }
670
671 static void
672 index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
673 {
674    char depends[1024], *space, *todo;
675    PkgNodePtr found;
676    IndexEntryPtr found_ie;
677
678    SAFE_STRCPY(depends, ie->deps);
679    for (todo = depends; todo != NULL; ) {
680       space = index(todo, ' ');
681       if (space != NULL)
682          *space = '\0';
683
684       if (strlen(todo) > 0) { /* only non-empty dependencies */
685           found = index_search(root, todo, NULL);
686           if (found != NULL) {
687               found_ie = found->data;
688               if (add)
689                   ++found_ie->depc;
690               else
691                   --found_ie->depc;
692           }
693       }
694
695       if (space != NULL)
696          todo = space + 1;
697       else
698          todo = NULL;
699    }
700 }
701
702 static Boolean index_initted;
703
704 /* Read and initialize global index */
705 int
706 index_initialize(char *path)
707 {
708     FILE *fp;
709     WINDOW *w = NULL;
710
711     if (!index_initted) {
712         w = savescr();
713         dialog_clear_norefresh();
714
715         /* Got any media? */
716         if (!mediaVerify()) {
717             restorescr(w);
718             return DITEM_FAILURE;
719         }
720
721         /* Does it move when you kick it? */
722         if (!mediaDevice->init(mediaDevice)) {
723             restorescr(w);
724             return DITEM_FAILURE;
725         }
726
727         dialog_clear_norefresh();
728         msgNotify("Attempting to fetch %s file from selected media.", path);
729         fp = mediaDevice->get(mediaDevice, path, TRUE);
730         if (!fp) {
731             msgConfirm("Unable to get packages/INDEX file from selected media.\n"
732                        "This may be because the packages collection is not available at\n"
733                        "on the distribution media you've chosen (most likely an FTP site\n"
734                        "without the packages collection mirrored).  Please verify media\n"
735                        "(or path to media) and try again.  If your local site does not\n"
736                        "carry the packages collection, then we recommend either a CD\n"
737                        "distribution or the master distribution on ftp.freebsd.org.");
738             mediaDevice->shutdown(mediaDevice);
739             restorescr(w);
740             return DITEM_FAILURE;
741         }
742         dialog_clear_norefresh();
743         msgNotify("Located INDEX, now reading package data from it...");
744         index_init(&Top, &Plist);
745         if (index_read(fp, &Top)) {
746             msgConfirm("I/O or format error on packages/INDEX file.\n"
747                        "Please verify media (or path to media) and try again.");
748             fclose(fp);
749             restorescr(w);
750             return DITEM_FAILURE;
751         }
752         fclose(fp);
753         index_sort(&Top);
754         index_initted = TRUE;
755         restorescr(w);
756     }
757     return DITEM_SUCCESS;
758 }