2 * The new sysinstall program.
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
8 * Jordan Hubbard. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
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
42 #include "sysinstall.h"
44 /* Macros and magic values */
48 /* A structure holding the root, top and plist pointer at once */
51 PkgNodePtr root; /* root of tree */
52 PkgNodePtr top; /* part of tree we handle */
53 PkgNodePtr plist; /* list of selected packages */
55 typedef struct ListPtrs* ListPtrsPtr;
57 static void index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
59 /* Shared between index_initialize() and the various clients of it */
66 return ptr ? strdup(ptr) : NULL;
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.",
182 fetch_desc(char *name)
186 for (i = 0; descrs[i]; i += 2) {
187 if (!strcmp(descrs[i], name))
188 return descrs[i + 1];
190 return "No description provided";
194 new_pkg_node(char *name, node_type type)
196 PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
198 tmp->name = _strdup(name);
208 for (i = 0; buf[i]; i++)
209 if (buf[i] == '\t' || buf[i] == '\n')
215 new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
217 IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
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);
227 tmp->installed = package_installed(name);
228 tmp->vol_checked = 0;
229 tmp->volume = volume;
234 else if (low_volume > volume)
236 if (high_volume < volume)
237 high_volume = volume;
243 index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
247 for (q = NULL, p = top->kids; p; p = p->next) {
248 if (!strcmp(p->name, where)) {
254 /* Add new category */
255 q = new_pkg_node(where, PLACE);
256 q->desc = fetch_desc(where);
260 p = new_pkg_node(ptr->name, PACKAGE);
261 p->desc = ptr->comment;
268 copy_to_sep(char *to, char *from, int sep)
272 tok = strchr(from, sep);
279 return tok + 1 - from;
283 skip_to_sep(char *from, int sep)
287 tok = strchr(from, sep);
291 return tok + 1 - from;
295 readline(FILE *fp, char *buf, int max)
300 while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
308 * XXX - this function should do error checking, and skip corrupted INDEX
309 * lines without a set number of '|' delimited fields.
313 index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
315 char line[10240 + 2048 * 7];
321 i = readline(fp, line, sizeof 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 */
335 cp += skip_to_sep(cp, '|'); /* url - not used */
337 strncpy(junk, cp, 1023);
342 cp += skip_to_sep(cp, '|'); /* extract deps - not used */
344 cp += skip_to_sep(cp, '|'); /* patch deps - not used */
346 cp += skip_to_sep(cp, '|'); /* fetch deps - not used */
348 cp += copy_to_sep(volstr, cp, '|'); /* media volume */
350 strncpy(volstr, cp, 1023);
352 *volume = atoi(volstr);
357 index_read(FILE *fp, PkgNodePtr papa)
359 char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048 * 8];
363 while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
364 char *cp, *cp2, tmp[1024];
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) {
371 index_register(papa, cp, idx);
373 index_register(papa, cp, idx);
375 /* Add to special "All" category */
376 index_register(papa, "All", idx);
379 /* Adjust dependency counts */
380 for (i = papa->kids; i != NULL; i = i->next)
381 if (strcmp(i->name, "All") == 0)
383 for (i = i->kids; i != NULL; i = i->next)
384 if (((IndexEntryPtr)i->data)->installed)
385 index_recorddeps(TRUE, papa, i->data);
391 index_init(PkgNodePtr top, PkgNodePtr plist)
394 top->next = top->kids = NULL;
395 top->name = "Package Selection";
397 top->desc = fetch_desc(top->name);
401 plist->next = plist->kids = NULL;
402 plist->name = "Package Targets";
404 plist->desc = fetch_desc(plist->name);
410 index_print(PkgNodePtr top, int level)
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);
420 index_print(top->kids, level + 1);
425 /* Swap one node for another */
427 swap_nodes(PkgNodePtr a, PkgNodePtr b)
438 /* Use a disgustingly simplistic bubble sort to put our lists in order */
440 index_sort(PkgNodePtr top)
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);
452 /* Now sub-sort everything n levels down */
453 for (p = top->kids; p; p = p->next) {
459 /* Delete an entry out of the list it's in (only the plist, at present) */
461 index_delete(PkgNodePtr n)
464 PkgNodePtr p = n->next;
469 else /* Kludgy end sentinal */
474 * Search for a given node by name, returning the category in if
478 index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
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))
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))) {
495 /* The usual recursion-out-of-laziness ploy */
496 if ((sp = index_search(p, str, tp)) != NULL)
506 pkg_checked(dialogMenuItem *self)
508 ListPtrsPtr lists = (ListPtrsPtr)self->aux;
509 PkgNodePtr kp = self->data, plist = lists->plist;
512 i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
513 if (kp->type == PACKAGE && plist) {
514 IndexEntryPtr ie = kp->data;
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;
526 pkg_fire(dialogMenuItem *self)
529 ListPtrsPtr lists = (ListPtrsPtr)self->aux;
530 PkgNodePtr sp, kp = self->data, plist = lists->plist;
534 else if (kp->type == PACKAGE) {
535 IndexEntryPtr ie = kp->data;
537 sp = index_search(plist, kp->name, NULL);
538 /* Not already selected? */
540 if (!ie->installed) {
541 PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
544 np->next = plist->kids;
546 index_recorddeps(TRUE, lists->root, ie);
547 msgInfo("Added %s to selection list", kp->name);
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);
556 index_recorddeps(FALSE, lists->root, ie);
561 msgConfirm("Warning: Package %s is needed by\n %d other installed package%s.",
562 kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
565 index_recorddeps(FALSE, lists->root, ie);
566 msgInfo("Removed %s from selection list", kp->name);
570 /* Mark menu for redraw if we had dependencies */
571 if (strlen(ie->deps) > 0)
574 else { /* Not a package, must be a directory */
578 index_menu(lists->root, kp, plist, &p, &s);
579 ret = DITEM_SUCCESS | DITEM_CONTINUE;
585 pkg_selected(dialogMenuItem *self, int is_selected)
587 PkgNodePtr kp = self->data;
589 if (!is_selected || kp->type != PACKAGE)
591 msgInfo("%s", kp->desc);
595 index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
597 struct ListPtrs lists;
602 dialogMenuItem *nitems;
614 /* Figure out if this menu is full of "leaves" or "branches" */
615 for (kp = top->kids; kp && kp->name; kp = kp->next) {
619 if (kp->type == PACKAGE && plist) {
621 if ((len = strlen(kp->name)) > maxname)
626 msgConfirm("The %s menu is empty.", top->name);
627 return DITEM_LEAVE_MENU;
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);
641 while (kp && kp->name) {
643 IndexEntryPtr ie = kp->data;
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");
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),
658 /* NULL delimiter so item_free() knows when to stop later */
659 nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
663 dialog_clear_norefresh();
665 rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
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) {
673 if ((cp = msgGetInput(cp, "Search by package name. Please enter search string:")) != NULL) {
674 PkgNodePtr p = index_search(top, cp, &menu);
679 /* These need to be set to point at the found item, actually. Hmmm! */
681 index_menu(root, menu, plist, &pos, &scroll);
684 msgConfirm("Search string: %s yielded no hits.", cp);
688 items_free(nitems, &curr, &max);
690 return rval ? DITEM_FAILURE : DITEM_SUCCESS;
695 index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended,
698 int status = DITEM_SUCCESS;
699 Boolean notyet = FALSE;
701 IndexEntryPtr id = who->data;
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.
714 if (id->installed == 1 || (have_volumes && id->vol_checked == current_volume))
715 return DITEM_SUCCESS;
718 if (id && id->deps && strlen(id->deps)) {
719 char t[2048 * 8], *cp, *cp2;
721 SAFE_STRCPY(t, id->deps);
723 while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
724 if ((cp2 = index(cp, ' ')) != NULL)
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;
733 } else if (variable_get(VAR_NO_CONFIRM))
734 msgNotify("Loading of dependent package %s failed", cp);
736 msgConfirm("Loading of dependent package %s failed", cp);
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);
743 msgConfirm("Warning: %s is a required package but was not found.", cp);
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.
762 id->vol_checked = current_volume;
763 return DITEM_FAILURE | DITEM_CONTINUE;
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.
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) {
780 id->vol_checked = current_volume;
781 return DITEM_FAILURE | DITEM_CONTINUE;
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);
793 return DITEM_FAILURE;
797 status = package_extract(dev, who->name, depended);
798 if (DITEM_STATUS(status) == DITEM_SUCCESS)
806 index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
808 char depends[1024 * 16], *space, *todo;
810 IndexEntryPtr found_ie;
812 SAFE_STRCPY(depends, ie->deps);
813 for (todo = depends; todo != NULL; ) {
814 space = index(todo, ' ');
818 if (strlen(todo) > 0) { /* only non-empty dependencies */
819 found = index_search(root, todo, NULL);
821 found_ie = found->data;
836 static Boolean index_initted;
838 /* Read and initialize global index */
840 index_initialize(char *path)
845 if (!index_initted) {
847 dialog_clear_norefresh();
848 have_volumes = FALSE;
849 low_volume = high_volume = 0;
852 if (!mediaVerify()) {
854 return DITEM_FAILURE;
857 /* Does it move when you kick it? */
858 if (!DEVICE_INIT(mediaDevice)) {
860 return DITEM_FAILURE;
863 dialog_clear_norefresh();
864 msgNotify("Attempting to fetch %s file from selected media.", path);
865 fp = DEVICE_GET(mediaDevice, path, TRUE);
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);
874 return DITEM_FAILURE;
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.");
884 return DITEM_FAILURE;
888 index_initted = TRUE;
891 return DITEM_SUCCESS;