c4af534777
[Those marked with an asterisk (*) are virtual categories] - accessibility* : Ports to help disabled users PR: ports/39103 Requested by: trevor - finance : Monetary, financial and related applications PR: ports/39102 Requested by: trevor - haskell* : Software related to the Haskell language PR: ports/41959 Requested by: obraun - hungarian : Hungarian language support PR: ports/37576 Requested by: Janos Mohacsi <janos.mohacsi@bsd.hu> - multimedia : Multimedia software Requested by: many - parallel* : pplications dealing with parallelism in computing PR: ports/39094 Requested by: trevor - portuguese : Portuguese language support PR: ports/35991 Requested by: trevor
826 lines
24 KiB
C
826 lines
24 KiB
C
/*
|
|
* The new sysinstall program.
|
|
*
|
|
* This is probably the last program in the `sysinstall' line - the next
|
|
* generation being essentially a complete rewrite.
|
|
*
|
|
* $FreeBSD$
|
|
*
|
|
* Copyright (c) 1995
|
|
* Jordan Hubbard. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer,
|
|
* verbatim and that no modifications are made prior to this
|
|
* point in the file.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ncurses.h>
|
|
#include <dialog.h>
|
|
#include "sysinstall.h"
|
|
|
|
/* Macros and magic values */
|
|
#define MAX_MENU 12
|
|
#define _MAX_DESC 55
|
|
|
|
/* A structure holding the root, top and plist pointer at once */
|
|
struct ListPtrs
|
|
{
|
|
PkgNodePtr root; /* root of tree */
|
|
PkgNodePtr top; /* part of tree we handle */
|
|
PkgNodePtr plist; /* list of selected packages */
|
|
};
|
|
typedef struct ListPtrs* ListPtrsPtr;
|
|
|
|
static void index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
|
|
|
|
/* Shared between index_initialize() and the various clients of it */
|
|
PkgNode Top, Plist;
|
|
|
|
/* Smarter strdup */
|
|
inline char *
|
|
_strdup(char *ptr)
|
|
{
|
|
return ptr ? strdup(ptr) : NULL;
|
|
}
|
|
|
|
static char *descrs[] = {
|
|
"Package Selection", "To mark a package, move to it and press SPACE. If the package is\n"
|
|
"already marked, it will be unmarked or deleted (if installed).\n"
|
|
"Items marked with a `D' are dependencies which will be auto-loaded.\n"
|
|
"To search for a package by name, press ESC. To select a category,\n"
|
|
"press RETURN. NOTE: The All category selection creates a very large\n"
|
|
"submenu! If you select it, please be patient while it comes up.",
|
|
"Package Targets", "These are the packages you've selected for extraction.\n\n"
|
|
"If you're sure of these choices, select OK.\n"
|
|
"If not, select Cancel to go back to the package selection menu.\n",
|
|
"All", "All available packages in all categories.",
|
|
"accessibility", "Ports to help disabled users.",
|
|
"afterstep", "Ports to support the AfterStep window manager.",
|
|
"applications", "User application software.",
|
|
"archivers", "Utilities for archiving and unarchiving data.",
|
|
"astro", "Applications related to astronomy.",
|
|
"audio", "Audio utilities - most require a supported sound card.",
|
|
"benchmarks", "Utilities for measuring system performance.",
|
|
"biology", "Software related to biology.",
|
|
"cad", "Computer Aided Design utilities.",
|
|
"chinese", "Ported software for the Chinese market.",
|
|
"comms", "Communications utilities.",
|
|
"converters", "Format conversion utilities.",
|
|
"databases", "Database software.",
|
|
"deskutils", "Various Desktop utilities.",
|
|
"devel", "Software development utilities and libraries.",
|
|
"documentation", "Document preparation utilities.",
|
|
"editors", "Common text editors.",
|
|
"elisp", "Things related to Emacs Lisp.",
|
|
"emulators", "Utilities for emulating other OS types.",
|
|
"finance", "Monetary, financial and related applications.",
|
|
"french", "Ported software for French countries.",
|
|
"ftp", "FTP client and server utilities.",
|
|
"games", "Various and sundry amusements.",
|
|
"german", "Ported software for Germanic countries.",
|
|
"gnome", "Components of the Gnome Desktop environment.",
|
|
"graphics", "Graphics libraries and utilities.",
|
|
"haskell", "Software related to the Haskell language.",
|
|
"hebrew", "Ported software for Hebrew language.",
|
|
"hungarian", "Ported software for the Hungarian market.",
|
|
"ipv6", "IPv6 related software.",
|
|
"irc", "Internet Relay Chat utilities.",
|
|
"japanese", "Ported software for the Japanese market.",
|
|
"java", "Java language support.",
|
|
"kde", "Software for the K Desktop Environment.",
|
|
"korean", "Ported software for the Korean market.",
|
|
"lang", "Computer languages.",
|
|
"languages", "Computer languages.",
|
|
"libraries", "Software development libraries.",
|
|
"linux", "Linux programs that can be run under binary compatibility.",
|
|
"mail", "Electronic mail packages and utilities.",
|
|
"math", "Mathematical computation software.",
|
|
"mbone", "Applications and utilities for the MBONE.",
|
|
"misc", "Miscellaneous utilities.",
|
|
"multimedia", "Multimedia software.",
|
|
"net", "Networking utilities.",
|
|
"news", "USENET News support software.",
|
|
"numeric", "Mathematical computation software.",
|
|
"offix", "An office automation suite of sorts.",
|
|
"orphans", "Packages without a home elsewhere.",
|
|
"palm", "Software support for the Palm(tm) series.",
|
|
"parallel", "Applications dealing with parallelism in computing.",
|
|
"perl5", "Utilities/modules for the PERL5 language.",
|
|
"picobsd", "Ports to support PicoBSD.",
|
|
"pilot", "Software support for the 3Com Palm Pilot(tm) series.",
|
|
"plan9", "Software from the Plan9 operating system.",
|
|
"portuguese", "Ported software for the Portuguese market.",
|
|
"print", "Utilities for dealing with printing.",
|
|
"printing", "Utilities for dealing with printing.",
|
|
"programming", "Software development utilities and libraries.",
|
|
"python", "Software related to the Python language.",
|
|
"ruby", "Software related to the Ruby language.",
|
|
"russian", "Ported software for the Russian market.",
|
|
"science", "Scientific software.",
|
|
"security", "System security software.",
|
|
"shells", "Various shells (tcsh, bash, etc).",
|
|
"sysutils", "Various system utilities.",
|
|
"tcl75", "TCL v7.5 and packages that depend on it.",
|
|
"tcl76", "TCL v7.6 and packages that depend on it.",
|
|
"tcl80", "TCL v8.0 and packages that depend on it.",
|
|
"tcl81", "TCL v8.1 and packages that depend on it.",
|
|
"tcl82", "TCL v8.2 and packages that depend on it.",
|
|
"tcl83", "TCL v8.3 and packages that depend on it.",
|
|
"textproc", "Text processing/search utilities.",
|
|
"tk41", "Tk4.1 and packages that depend on it.",
|
|
"tk42", "Tk4.2 and packages that depend on it.",
|
|
"tk80", "Tk8.0 and packages that depend on it.",
|
|
"tk81", "Tk8.1 and packages that depend on it.",
|
|
"tk82", "Tk8.2 and packages that depend on it.",
|
|
"tk83", "Tk8.3 and packages that depend on it.",
|
|
"tkstep80", "tkstep wm and packages that depend on it.",
|
|
"troff", "TROFF text formatting utilities.",
|
|
"ukrainian", "Ported software for the Ukrainian market.",
|
|
"vietnamese", "Ported software for the Vietnamese market.",
|
|
"windowmaker", "Ports to support the WindowMaker window manager.",
|
|
"www", "WEB utilities (browers, HTTP servers, etc).",
|
|
"x11", "X Window System based utilities.",
|
|
"x11-clocks", "X Window System based clocks.",
|
|
"x11-fm", "X Window System based file managers.",
|
|
"x11-fonts", "X Window System fonts and font utilities.",
|
|
"x11-servers", "X Window System servers.",
|
|
"x11-toolkits", "X Window System based development toolkits.",
|
|
"x11-wm", "X Window System window managers.",
|
|
"zope", "Software related to the Zope platform.",
|
|
NULL, NULL,
|
|
};
|
|
|
|
static char *
|
|
fetch_desc(char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; descrs[i]; i += 2) {
|
|
if (!strcmp(descrs[i], name))
|
|
return descrs[i + 1];
|
|
}
|
|
return "No description provided";
|
|
}
|
|
|
|
static PkgNodePtr
|
|
new_pkg_node(char *name, node_type type)
|
|
{
|
|
PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
|
|
|
|
tmp->name = _strdup(name);
|
|
tmp->type = type;
|
|
return tmp;
|
|
}
|
|
|
|
static char *
|
|
strip(char *buf)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; buf[i]; i++)
|
|
if (buf[i] == '\t' || buf[i] == '\n')
|
|
buf[i] = ' ';
|
|
return buf;
|
|
}
|
|
|
|
static IndexEntryPtr
|
|
new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
|
|
{
|
|
IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
|
|
|
|
tmp->name = _strdup(name);
|
|
tmp->path = _strdup(pathto);
|
|
tmp->prefix = _strdup(prefix);
|
|
tmp->comment = _strdup(comment);
|
|
tmp->descrfile = strip(_strdup(descr));
|
|
tmp->maintainer = _strdup(maint);
|
|
tmp->deps = _strdup(deps);
|
|
tmp->depc = 0;
|
|
tmp->installed = package_exists(name);
|
|
tmp->volume = volume;
|
|
return tmp;
|
|
}
|
|
|
|
static void
|
|
index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
|
|
{
|
|
PkgNodePtr p, q;
|
|
|
|
for (q = NULL, p = top->kids; p; p = p->next) {
|
|
if (!strcmp(p->name, where)) {
|
|
q = p;
|
|
break;
|
|
}
|
|
}
|
|
if (!p) {
|
|
/* Add new category */
|
|
q = new_pkg_node(where, PLACE);
|
|
q->desc = fetch_desc(where);
|
|
q->next = top->kids;
|
|
top->kids = q;
|
|
}
|
|
p = new_pkg_node(ptr->name, PACKAGE);
|
|
p->desc = ptr->comment;
|
|
p->data = ptr;
|
|
p->next = q->kids;
|
|
q->kids = p;
|
|
}
|
|
|
|
static int
|
|
copy_to_sep(char *to, char *from, int sep)
|
|
{
|
|
char *tok;
|
|
|
|
tok = strchr(from, sep);
|
|
if (!tok) {
|
|
*to = '\0';
|
|
return 0;
|
|
}
|
|
*tok = '\0';
|
|
strcpy(to, from);
|
|
return tok + 1 - from;
|
|
}
|
|
|
|
static int
|
|
readline(FILE *fp, char *buf, int max)
|
|
{
|
|
int rv, i = 0;
|
|
char ch;
|
|
|
|
while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
|
|
buf[i++] = ch;
|
|
if (i < max)
|
|
buf[i] = '\0';
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* XXX - this function should do error checking, and skip corrupted INDEX
|
|
* lines without a set number of '|' delimited fields.
|
|
*/
|
|
|
|
int
|
|
index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
|
|
{
|
|
char line[10240];
|
|
char junk[2048];
|
|
char volstr[2048];
|
|
char *cp;
|
|
int i;
|
|
|
|
i = readline(fp, line, sizeof line);
|
|
if (i <= 0)
|
|
return EOF;
|
|
cp = line;
|
|
cp += copy_to_sep(name, cp, '|');
|
|
cp += copy_to_sep(pathto, cp, '|');
|
|
cp += copy_to_sep(prefix, cp, '|');
|
|
cp += copy_to_sep(comment, cp, '|');
|
|
cp += copy_to_sep(descr, cp, '|');
|
|
cp += copy_to_sep(maint, cp, '|');
|
|
cp += copy_to_sep(cats, cp, '|');
|
|
cp += copy_to_sep(junk, cp, '|'); /* build deps - not used */
|
|
cp += copy_to_sep(rdeps, cp, '|');
|
|
if (index(cp, '|'))
|
|
cp += copy_to_sep(junk, cp, '|'); /* url - not used */
|
|
else {
|
|
strncpy(junk, cp, 1023);
|
|
*volume = 0;
|
|
return 0;
|
|
}
|
|
if (index(cp, '|'))
|
|
cp += copy_to_sep(volstr, cp, '|'); /* media volume */
|
|
else {
|
|
strncpy(volstr, cp, 1023);
|
|
}
|
|
*volume = atoi(volstr);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
index_read(FILE *fp, PkgNodePtr papa)
|
|
{
|
|
char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048];
|
|
int volume;
|
|
PkgNodePtr i;
|
|
|
|
while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
|
|
char *cp, *cp2, tmp[1024];
|
|
IndexEntryPtr idx;
|
|
|
|
idx = new_index(name, pathto, prefix, comment, descr, maint, deps, volume);
|
|
/* For now, we only add things to menus if they're in categories. Keywords are ignored */
|
|
for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
|
|
*cp2 = '\0';
|
|
index_register(papa, cp, idx);
|
|
}
|
|
index_register(papa, cp, idx);
|
|
|
|
/* Add to special "All" category */
|
|
index_register(papa, "All", idx);
|
|
}
|
|
|
|
/* Adjust dependency counts */
|
|
for (i = papa->kids; i != NULL; i = i->next)
|
|
if (strcmp(i->name, "All") == 0)
|
|
break;
|
|
for (i = i->kids; i != NULL; i = i->next)
|
|
if (((IndexEntryPtr)i->data)->installed)
|
|
index_recorddeps(TRUE, papa, i->data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
index_init(PkgNodePtr top, PkgNodePtr plist)
|
|
{
|
|
if (top) {
|
|
top->next = top->kids = NULL;
|
|
top->name = "Package Selection";
|
|
top->type = PLACE;
|
|
top->desc = fetch_desc(top->name);
|
|
top->data = NULL;
|
|
}
|
|
if (plist) {
|
|
plist->next = plist->kids = NULL;
|
|
plist->name = "Package Targets";
|
|
plist->type = PLACE;
|
|
plist->desc = fetch_desc(plist->name);
|
|
plist->data = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
index_print(PkgNodePtr top, int level)
|
|
{
|
|
int i;
|
|
|
|
while (top) {
|
|
for (i = 0; i < level; i++) putchar('\t');
|
|
printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
|
|
for (i = 0; i < level; i++) putchar('\t');
|
|
printf("desc: %s\n", top->desc);
|
|
if (top->kids)
|
|
index_print(top->kids, level + 1);
|
|
top = top->next;
|
|
}
|
|
}
|
|
|
|
/* Swap one node for another */
|
|
static void
|
|
swap_nodes(PkgNodePtr a, PkgNodePtr b)
|
|
{
|
|
PkgNode tmp;
|
|
|
|
tmp = *a;
|
|
*a = *b;
|
|
a->next = tmp.next;
|
|
tmp.next = b->next;
|
|
*b = tmp;
|
|
}
|
|
|
|
/* Use a disgustingly simplistic bubble sort to put our lists in order */
|
|
void
|
|
index_sort(PkgNodePtr top)
|
|
{
|
|
PkgNodePtr p, q;
|
|
|
|
/* Sort everything at the top level */
|
|
for (p = top->kids; p; p = p->next) {
|
|
for (q = top->kids; q; q = q->next) {
|
|
if (q->next && strcmp(q->name, q->next->name) > 0)
|
|
swap_nodes(q, q->next);
|
|
}
|
|
}
|
|
|
|
/* Now sub-sort everything n levels down */
|
|
for (p = top->kids; p; p = p->next) {
|
|
if (p->kids)
|
|
index_sort(p);
|
|
}
|
|
}
|
|
|
|
/* Delete an entry out of the list it's in (only the plist, at present) */
|
|
void
|
|
index_delete(PkgNodePtr n)
|
|
{
|
|
if (n->next) {
|
|
PkgNodePtr p = n->next;
|
|
|
|
*n = *(n->next);
|
|
safe_free(p);
|
|
}
|
|
else /* Kludgy end sentinal */
|
|
n->name = NULL;
|
|
}
|
|
|
|
/*
|
|
* Search for a given node by name, returning the category in if
|
|
* tp is non-NULL.
|
|
*/
|
|
PkgNodePtr
|
|
index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
|
|
{
|
|
PkgNodePtr p, sp;
|
|
|
|
for (p = top->kids; p && p->name; p = p->next) {
|
|
if (p->type == PACKAGE) {
|
|
/* If tp == NULL, we're looking for an exact package match */
|
|
if (!tp && !strcmp(p->name, str))
|
|
return p;
|
|
|
|
/* If tp, we're looking for both a package and a pointer to the place it's in */
|
|
if (tp && !strncmp(p->name, str, strlen(str))) {
|
|
*tp = top;
|
|
return p;
|
|
}
|
|
}
|
|
else if (p->kids) {
|
|
/* The usual recursion-out-of-laziness ploy */
|
|
if ((sp = index_search(p, str, tp)) != NULL)
|
|
return sp;
|
|
}
|
|
}
|
|
if (p && !p->name)
|
|
p = NULL;
|
|
return p;
|
|
}
|
|
|
|
int
|
|
pkg_checked(dialogMenuItem *self)
|
|
{
|
|
ListPtrsPtr lists = (ListPtrsPtr)self->aux;
|
|
PkgNodePtr kp = self->data, plist = lists->plist;
|
|
int i;
|
|
|
|
i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
|
|
if (kp->type == PACKAGE && plist) {
|
|
IndexEntryPtr ie = kp->data;
|
|
int markD, markX;
|
|
|
|
markD = ie->depc > 0; /* needed as dependency */
|
|
markX = i || ie->installed; /* selected or installed */
|
|
self->mark = markX ? 'X' : 'D';
|
|
return markD || markX;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
pkg_fire(dialogMenuItem *self)
|
|
{
|
|
int ret;
|
|
ListPtrsPtr lists = (ListPtrsPtr)self->aux;
|
|
PkgNodePtr sp, kp = self->data, plist = lists->plist;
|
|
|
|
if (!plist)
|
|
ret = DITEM_FAILURE;
|
|
else if (kp->type == PACKAGE) {
|
|
IndexEntryPtr ie = kp->data;
|
|
|
|
sp = index_search(plist, kp->name, NULL);
|
|
/* Not already selected? */
|
|
if (!sp) {
|
|
if (!ie->installed) {
|
|
PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
|
|
|
|
*np = *kp;
|
|
np->next = plist->kids;
|
|
plist->kids = np;
|
|
index_recorddeps(TRUE, lists->root, ie);
|
|
msgInfo("Added %s to selection list", kp->name);
|
|
}
|
|
else if (ie->depc == 0) {
|
|
if (!msgNoYes("Do you really want to delete %s from the system?", kp->name)) {
|
|
if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
|
|
msgConfirm("Warning: pkg_delete of %s failed.\n Check debug output for details.", kp->name);
|
|
}
|
|
else {
|
|
ie->installed = 0;
|
|
index_recorddeps(FALSE, lists->root, ie);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
msgConfirm("Warning: Package %s is needed by\n %d other installed package%s.",
|
|
kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
|
|
}
|
|
else {
|
|
index_recorddeps(FALSE, lists->root, ie);
|
|
msgInfo("Removed %s from selection list", kp->name);
|
|
index_delete(sp);
|
|
}
|
|
ret = DITEM_SUCCESS;
|
|
/* Mark menu for redraw if we had dependencies */
|
|
if (strlen(ie->deps) > 0)
|
|
ret |= DITEM_REDRAW;
|
|
}
|
|
else { /* Not a package, must be a directory */
|
|
int p, s;
|
|
|
|
p = s = 0;
|
|
index_menu(lists->root, kp, plist, &p, &s);
|
|
ret = DITEM_SUCCESS | DITEM_CONTINUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
pkg_selected(dialogMenuItem *self, int is_selected)
|
|
{
|
|
PkgNodePtr kp = self->data;
|
|
|
|
if (!is_selected || kp->type != PACKAGE)
|
|
return;
|
|
msgInfo("%s", kp->desc);
|
|
}
|
|
|
|
int
|
|
index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
|
|
{
|
|
struct ListPtrs lists;
|
|
int n, rval, maxname;
|
|
int curr, max;
|
|
PkgNodePtr kp;
|
|
dialogMenuItem *nitems;
|
|
Boolean hasPackages;
|
|
WINDOW *w;
|
|
|
|
lists.root = root;
|
|
lists.top = top;
|
|
lists.plist = plist;
|
|
|
|
hasPackages = FALSE;
|
|
nitems = NULL;
|
|
n = maxname = 0;
|
|
|
|
/* Figure out if this menu is full of "leaves" or "branches" */
|
|
for (kp = top->kids; kp && kp->name; kp = kp->next) {
|
|
int len;
|
|
|
|
++n;
|
|
if (kp->type == PACKAGE && plist) {
|
|
hasPackages = TRUE;
|
|
if ((len = strlen(kp->name)) > maxname)
|
|
maxname = len;
|
|
}
|
|
}
|
|
if (!n && plist) {
|
|
msgConfirm("The %s menu is empty.", top->name);
|
|
return DITEM_LEAVE_MENU;
|
|
}
|
|
|
|
w = savescr();
|
|
while (1) {
|
|
n = 0;
|
|
curr = max = 0;
|
|
use_helpline(NULL);
|
|
use_helpfile(NULL);
|
|
kp = top->kids;
|
|
if (!hasPackages && plist) {
|
|
nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
|
|
nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
|
|
}
|
|
while (kp && kp->name) {
|
|
char buf[256];
|
|
IndexEntryPtr ie = kp->data;
|
|
|
|
/* Brutally adjust description to fit in menu */
|
|
if (kp->type == PACKAGE)
|
|
snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
|
|
else
|
|
SAFE_STRCPY(buf, kp->desc);
|
|
if (strlen(buf) > (_MAX_DESC - maxname))
|
|
buf[_MAX_DESC - maxname] = '\0';
|
|
nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked,
|
|
pkg_fire, pkg_selected, kp, (int *)(&lists),
|
|
&curr, &max);
|
|
++n;
|
|
kp = kp->next;
|
|
}
|
|
/* NULL delimiter so item_free() knows when to stop later */
|
|
nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&curr, &max);
|
|
|
|
recycle:
|
|
dialog_clear_norefresh();
|
|
if (hasPackages)
|
|
rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
|
|
else
|
|
rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
|
|
if (rval == -1 && plist) {
|
|
static char *cp;
|
|
PkgNodePtr menu;
|
|
|
|
/* Search */
|
|
if ((cp = msgGetInput(cp, "Search by package name. Please enter search string:")) != NULL) {
|
|
PkgNodePtr p = index_search(top, cp, &menu);
|
|
|
|
if (p) {
|
|
int pos, scroll;
|
|
|
|
/* These need to be set to point at the found item, actually. Hmmm! */
|
|
pos = scroll = 0;
|
|
index_menu(root, menu, plist, &pos, &scroll);
|
|
}
|
|
else
|
|
msgConfirm("Search string: %s yielded no hits.", cp);
|
|
}
|
|
goto recycle;
|
|
}
|
|
items_free(nitems, &curr, &max);
|
|
restorescr(w);
|
|
return rval ? DITEM_FAILURE : DITEM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
int
|
|
index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
|
|
{
|
|
int status = DITEM_SUCCESS;
|
|
PkgNodePtr tmp2;
|
|
IndexEntryPtr id = who->data;
|
|
WINDOW *w = savescr();
|
|
|
|
/*
|
|
* Short-circuit the package dependency checks. We're already
|
|
* maintaining a data structure of installed packages, so if a
|
|
* package is already installed, don't try to check to make sure
|
|
* that all of its dependencies are installed. At best this
|
|
* wastes a ton of cycles and can cause minor delays between
|
|
* package extraction. At worst it can cause an infinite loop with
|
|
* a certain faulty INDEX file.
|
|
*/
|
|
|
|
if (id->installed == 1)
|
|
return DITEM_SUCCESS;
|
|
|
|
/*
|
|
* What if the package is not available on the current media volume?
|
|
*
|
|
*/
|
|
if (id->volume != dev->volume) {
|
|
if (!msgYesNo("This is disc #%d. Package %s is on disc #%d\n"
|
|
"Would you like to switch discs now?\n", dev->volume,
|
|
id->name, id->volume)) {
|
|
DEVICE_SHUTDOWN(mediaDevice);
|
|
msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
|
|
dev->volume, id->volume);
|
|
DEVICE_INIT(mediaDevice);
|
|
/* XXX, at this point we check to see if this is the
|
|
* correct disc, and if not, we loop */
|
|
} else {
|
|
return DITEM_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (id && id->deps && strlen(id->deps)) {
|
|
char t[2048], *cp, *cp2;
|
|
|
|
SAFE_STRCPY(t, id->deps);
|
|
cp = t;
|
|
while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
|
|
if ((cp2 = index(cp, ' ')) != NULL)
|
|
*cp2 = '\0';
|
|
if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
|
|
status = index_extract(dev, top, tmp2, TRUE);
|
|
if (DITEM_STATUS(status) != DITEM_SUCCESS) {
|
|
if (variable_get(VAR_NO_CONFIRM))
|
|
msgNotify("Loading of dependent package %s failed", cp);
|
|
else
|
|
msgConfirm("Loading of dependent package %s failed", cp);
|
|
}
|
|
}
|
|
else if (!package_exists(cp)) {
|
|
if (variable_get(VAR_NO_CONFIRM))
|
|
msgNotify("Warning: %s is a required package but was not found.", cp);
|
|
else
|
|
msgConfirm("Warning: %s is a required package but was not found.", cp);
|
|
}
|
|
if (cp2)
|
|
cp = cp2 + 1;
|
|
else
|
|
cp = NULL;
|
|
}
|
|
}
|
|
/* Done with the deps? Load the real m'coy */
|
|
if (DITEM_STATUS(status) == DITEM_SUCCESS) {
|
|
status = package_extract(dev, who->name, depended);
|
|
if (DITEM_STATUS(status) == DITEM_SUCCESS)
|
|
id->installed = 1;
|
|
}
|
|
restorescr(w);
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
|
|
{
|
|
char depends[1024], *space, *todo;
|
|
PkgNodePtr found;
|
|
IndexEntryPtr found_ie;
|
|
|
|
SAFE_STRCPY(depends, ie->deps);
|
|
for (todo = depends; todo != NULL; ) {
|
|
space = index(todo, ' ');
|
|
if (space != NULL)
|
|
*space = '\0';
|
|
|
|
if (strlen(todo) > 0) { /* only non-empty dependencies */
|
|
found = index_search(root, todo, NULL);
|
|
if (found != NULL) {
|
|
found_ie = found->data;
|
|
if (add)
|
|
++found_ie->depc;
|
|
else
|
|
--found_ie->depc;
|
|
}
|
|
}
|
|
|
|
if (space != NULL)
|
|
todo = space + 1;
|
|
else
|
|
todo = NULL;
|
|
}
|
|
}
|
|
|
|
static Boolean index_initted;
|
|
|
|
/* Read and initialize global index */
|
|
int
|
|
index_initialize(char *path)
|
|
{
|
|
FILE *fp;
|
|
WINDOW *w = NULL;
|
|
|
|
if (!index_initted) {
|
|
w = savescr();
|
|
dialog_clear_norefresh();
|
|
|
|
/* Got any media? */
|
|
if (!mediaVerify()) {
|
|
restorescr(w);
|
|
return DITEM_FAILURE;
|
|
}
|
|
|
|
/* Does it move when you kick it? */
|
|
if (!DEVICE_INIT(mediaDevice)) {
|
|
restorescr(w);
|
|
return DITEM_FAILURE;
|
|
}
|
|
|
|
dialog_clear_norefresh();
|
|
msgNotify("Attempting to fetch %s file from selected media.", path);
|
|
fp = DEVICE_GET(mediaDevice, path, TRUE);
|
|
if (!fp) {
|
|
msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
|
|
"This may be because the packages collection is not available\n"
|
|
"on the distribution media you've chosen, most likely an FTP site\n"
|
|
"without the packages collection mirrored. Please verify that\n"
|
|
"your media, or your path to the media, is correct and try again.");
|
|
DEVICE_SHUTDOWN(mediaDevice);
|
|
restorescr(w);
|
|
return DITEM_FAILURE;
|
|
}
|
|
dialog_clear_norefresh();
|
|
msgNotify("Located INDEX, now reading package data from it...");
|
|
index_init(&Top, &Plist);
|
|
if (index_read(fp, &Top)) {
|
|
msgConfirm("I/O or format error on packages/INDEX file.\n"
|
|
"Please verify media (or path to media) and try again.");
|
|
fclose(fp);
|
|
restorescr(w);
|
|
return DITEM_FAILURE;
|
|
}
|
|
fclose(fp);
|
|
index_sort(&Top);
|
|
index_initted = TRUE;
|
|
restorescr(w);
|
|
}
|
|
return DITEM_SUCCESS;
|
|
}
|