#include #include #include #include #include #include #include #include #include #include #include #include #include "hash.h" #include "install.h" #include "intl.h" #include "kickstart.h" #include "log.h" #include "pkgs.h" #include "windows.h" #include "smp.h" #define FILENAME_TAG 1000000 static int selectPackagesByGroup(struct pkgSet * psp); static int skipPackage(char * name); static int selectComponents(struct componentSet * csp, struct pkgSet * psp, int * doIndividual); static int strptrCmp(const void * a, const void * b); static void showPackageInfo(Header h); static int queryIndividual(int * result); char * skipList[] = { "XFree86-8514", "XFree86-AGX", "XFree86-Mach32", "XFree86-Mach64", "XFree86-Mach8", "XFree86-Mono", "XFree86-P9000", "XFree86-S3", "XFree86-S3V", "XFree86-SVGA", "XFree86-W32", "XFree86-I128", "XFree86-Sun", "XFree86-SunMono", "XFree86-Sun24", "XFree86-3DLabs", "kernel-boot", "metroess", "metrotmpl", NULL }; static int strptrCmp(const void * a, const void * b) { const char * const * one = a; const char * const * two = b; return strcmp(*one, *two); } static int psGetArchScore(Header h) { void * pkgArch; int type, count; if (!headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count) || type == RPM_INT8_TYPE) return 150; else return rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch); } int pkgCompare(void * first, void * second) { struct packageInfo ** a = first; struct packageInfo ** b = second; /* put packages w/o names at the end */ if (!(*a)->name) return 1; if (!(*b)->name) return -1; return strcasecmp((*a)->name, (*b)->name); } static int pkgCompareVer(void * first, void * second) { struct packageInfo ** a = first; struct packageInfo ** b = second; int ret, score1, score2; /* put packages w/o names at the end */ if (!(*a)->name) return 1; if (!(*b)->name) return -1; ret = strcasecmp((*a)->name, (*b)->name); if (ret) return ret; score1 = psGetArchScore((*a)->h); if (!score1) return 1; score2 = psGetArchScore((*b)->h); if (!score2) return -1; if (score1 < score2) return -1; if (score1 > score2) return 1; return rpmVersionCompare((*b)->h, (*a)->h); } static void pkgSort(struct pkgSet * psp) { int i; char *name; qsort(psp->packages, psp->numPackages, sizeof(*psp->packages), (void *) pkgCompareVer); name = psp->packages[0]->name; if (!name) { psp->numPackages = 0; return; } for (i = 1; i < psp->numPackages; i++) { if (!psp->packages[i]->name) break; if (!strcmp(psp->packages[i]->name, name)) psp->packages[i]->name = NULL; else name = psp->packages[i]->name; } qsort(psp->packages, psp->numPackages, sizeof(*psp->packages), (void *) pkgCompareVer); for (i = 0; i < psp->numPackages; i++) if (!psp->packages[i]->name) break; psp->numPackages = i; } int psUsingDirectory(char * dirname, struct pkgSet * psp) { DIR * dir; struct dirent * ent; FD_t fd; int rc, isSource; Header h; int packagesAlloced; struct pkgSet ps; int count, type; unsigned int * sizeptr; char * name, * group; char * filename; ps.numPackages = 0; packagesAlloced = 5; ps.packages = malloc(sizeof(*ps.packages) * packagesAlloced); logMessage("scanning %s for packages", dirname); dir = opendir(dirname); if (!dir) { errorWindow("error opening directory"); return INST_ERROR; } errno = 0; ent = readdir(dir); if (errno) { free(ps.packages); errorWindow("error reading from directory"); closedir(dir); return INST_ERROR; } filename = alloca(strlen(dirname) + 500); winStatus(33, 3, "Running", "Scanning available packages..."); while (ent) { if (!(ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || ((ent->d_name[1] == '.') && (ent->d_name[2] == '\0'))))) { sprintf(filename, "%s/%s", dirname, ent->d_name); fd = fdOpen(filename, O_RDONLY, 0666); if (!fd) { logMessage("failed to open %s: %s", filename, strerror(errno)); } else { rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL); fdClose(fd); if (rc) { logMessage("failed to rpmReadPackageHeader %s:", ent->d_name); } else { if (ps.numPackages == packagesAlloced) { packagesAlloced += 5; ps.packages = realloc(ps.packages, sizeof(*ps.packages) * packagesAlloced); } ps.packages[ps.numPackages] = malloc(sizeof(struct packageInfo)); ps.packages[ps.numPackages]->h = h; ps.packages[ps.numPackages]->selected = 0; ps.packages[ps.numPackages]->manuallySelected = 0; ps.packages[ps.numPackages]->data = strdup(ent->d_name); headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count); if (headerGetEntry(h, RPMTAG_SIZE, &type, (void **) &sizeptr, &count)) ps.packages[ps.numPackages]->size = *sizeptr; else ps.packages[ps.numPackages]->size = 0; if (skipPackage(name)) ps.packages[ps.numPackages]->inmenu = 0; else ps.packages[ps.numPackages]->inmenu = 1; if (!headerGetEntry(h, RPMTAG_GROUP, &type, (void **) &group, &count)) { group = "(unknown group)"; } else if (!strlen(group)) { group = "(unknown group)"; } ps.packages[ps.numPackages]->name = name; ps.packages[ps.numPackages]->group = group; ps.numPackages++; } } } errno = 0; ent = readdir(dir); if (errno) { newtPopWindow(); errorWindow("error reading from directory (2): %s"); free(ps.packages); closedir(dir); return INST_ERROR; } } pkgSort(&ps); *psp = ps; closedir(dir); newtPopWindow(); return 0; } int psReadComponentsFile(char * filespec, struct pkgSet * psp, struct componentSet * csp) { FILE * f; char buf[255]; int inComp; int line = 0; char * start; char * chptr; int compsAlloced; int packagesAlloced = 0; struct componentSet cs; struct component * currcomp = NULL; struct packageInfo packkey, ** pack; struct packageInfo * keyaddr = &packkey; int i; int baseNum = 0; int skipMissing = 0; f = fopen(filespec, "r"); if (!f) { errorWindow(_("Cannot open components file: %s")); return INST_ERROR; } /* get the version number */ line++; if (!fgets(buf, sizeof(buf), f)) { errorWindow(_("Cannot read components file: %s")); fclose(f); return INST_ERROR; } if (strcmp(buf, "0\n") && strcmp(buf, "0.1\n")) { newtWinMessage(_("Error"), _("Ok"), _("Comps file is not version 0.1 as expected")); fclose(f); return INST_ERROR; } compsAlloced = 5; cs.numComponents = 0; cs.comps = malloc(sizeof(*cs.comps) * compsAlloced); cs.base = NULL; inComp = 0; while (fgets(buf, sizeof(buf), f)) { line++; /* remove any trailing '\n', leave chptr at the end of the string */ chptr = buf + strlen(buf) - 1; if (*chptr == '\n') *chptr = '\0'; else chptr++; /* strip leading spaces */ start = buf; while (*start && isspace(*start)) start++; /* empty string */ if (!*start) continue; /* comment */ if (*start == '#') continue; if (!inComp) { /* first digit must be a zero or a one */ if (*start != '0' && *start != '1') { newtWinMessage(_("Error"), _("Ok"), _("bad comps file at line %d"), line); continue; } if (compsAlloced == cs.numComponents) { compsAlloced += 5; cs.comps = realloc(cs.comps, sizeof(*cs.comps) * compsAlloced); } currcomp = cs.comps + cs.numComponents; currcomp->selected = (*start == '1'); currcomp->inmenu = 1; start++; while (*start && isspace(*start)) start++; if (!strncmp(start, "--hide ", 7)) { start += 7; while (*start && isspace(*start)) start++; currcomp->inmenu = 0; } if (!*start) { newtWinMessage(_("comps Error"), _("Ok"), _("missing component name at line %d"), line); continue; } currcomp->name = strdup(start); currcomp->ps.numPackages = 0; packagesAlloced = 5; currcomp->ps.packages = malloc( sizeof(struct packageInfo) * packagesAlloced); inComp = 1; } else { if (!strcmp(start, "end")) { inComp = 0; if (!strcasecmp(currcomp->name, "Base")) baseNum = cs.numComponents; cs.numComponents++; } else { packkey.name = start; pack = bsearch(&keyaddr, psp->packages, psp->numPackages, sizeof(*psp->packages), (void *) pkgCompare); if (!pack) { if (!skipMissing && newtWinChoice(_("comps Error"), _("Ok"), _("Ignore all"), _("package %s at line %d does not exist"), start, line) == 2) skipMissing = 1; continue; } if (currcomp->ps.numPackages == packagesAlloced) { packagesAlloced += 5; currcomp->ps.packages = realloc(currcomp->ps.packages, sizeof(struct packageInfo) * packagesAlloced); } currcomp->ps.packages[currcomp->ps.numPackages] = *pack; currcomp->ps.numPackages++; } } } fclose(f); cs.base = cs.comps + baseNum; cs.base->inmenu = 0; cs.base->selected = 1; for (i = 0; i < cs.base->ps.numPackages; i++) { cs.base->ps.packages[i]->inmenu = 0; cs.base->ps.packages[i]->selected = 1; } *csp = cs; return 0; } struct packageCheckbox { newtComponent cb, sizeLabel; unsigned int * kSelected; unsigned int size; char state, lastState; }; #define SELECT_GNOME 1 #define SELECT_COMPONENTS 2 #define SELECT_PACKAGES 3 #define SELECT_VERIFY 4 #define SELECT_DONE 100 int psSelectPackages(struct pkgSet * psp, struct componentSet * csp, char * justComponent, int goForward, int isUpgrade) { int rc; int stage; static int doIndividual = 0; struct ksPackage * ksList; int ksListLength; int i, j, k, dir = 1; struct packageInfo key; struct packageInfo ** pack, * keyaddr = &key; int gnomeComponent = -1; /* XXX This is a hack --- install a kernel-smp package if you have a smp motherboard */ if (detectSMP() == 1) { key.name = "kernel-smp"; pack = bsearch(&keyaddr, psp->packages, psp->numPackages, sizeof(*psp->packages), (void *) pkgCompare); if (pack) (*pack)->manuallySelected = 1; } if (justComponent) { /* first off, turn on the base packages */ for (k = 0; k < csp->base->ps.numPackages; k++) csp->base->ps.packages[k]->selected = 1; csp->base->selected = 1; for (j = 0; j < csp->numComponents; j++) if (!strcasecmp(justComponent, csp->comps[j].name)) break; if (j < csp->numComponents) { for (k = 0; k < csp->comps[j].ps.numPackages; k++) csp->comps[j].ps.packages[k]->selected = 1; return 0; } } if (kickstart) { ksGetPackageList(&ksList, &ksListLength); /* first off, turn on the base packages */ for (k = 0; k < csp->base->ps.numPackages; k++) csp->base->ps.packages[k]->selected = 1; csp->base->selected = 1; for (i = 0; i < ksListLength; i++) { if (ksList[i].isComponent) { for (j = 0; j < csp->numComponents; j++) if (!strcasecmp(ksList[i].name, csp->comps[j].name)) break; if (j == csp->numComponents) { newtWinMessage(_("Kickstart Error"), _("Ok"), _("Component %s does not exist.\n"), ksList[i].name); } else { for (k = 0; k < csp->comps[j].ps.numPackages; k++) csp->comps[j].ps.packages[k]->selected = 1; } } else { key.name = ksList[i].name; pack = bsearch(&keyaddr, psp->packages, psp->numPackages, sizeof(*psp->packages), (void *) pkgCompare); if (!pack) { newtWinMessage(_("Kickstart Error"), _("Ok"), _("Package %s does not exist.\n"), key.name); } else { (*pack)->selected = 1; } } } rc = psVerifyDependencies(psp, 1); return 0; } else { if (!goForward && doIndividual) stage = SELECT_PACKAGES; else stage = SELECT_COMPONENTS; } if (isUpgrade) { /* Make sure X is around */ for (i = 0; i < psp->numPackages; i++) if (!strcmp(psp->packages[i]->name, "XFree86")) break; if (i < psp->numPackages && psp->packages[i]->selected) { /* And that gnome-core isn't already installed */ for (i = 0; i < psp->numPackages; i++) if (!strcmp(psp->packages[i]->name, "gmc")) break; if (i == psp->numPackages || !psp->packages[i]->selected) { /* now find the gnome component */ for (i = 0; i < csp->numComponents; i++) if (strcasestr(csp->comps[i].name, "gnome")) break; if (i < csp->numComponents) { gnomeComponent = i; stage = SELECT_GNOME; } } } } while (stage != SELECT_DONE) { switch (stage) { case SELECT_GNOME: rc = newtWinTernary(_("GNOME"), _("Yes"), _("No"), _("Back"), _("Would you like to have the GNOME desktop installed? " "It provides an easy to use interface, including a " "drag and drop capability and an integrated help system.")); if (rc == 3) return INST_CANCEL; if (rc != 2) { csp->comps[gnomeComponent].selected = 1; for (i = 0; i < csp->comps[gnomeComponent].ps.numPackages; i++) csp->comps[gnomeComponent].ps.packages[i]->selected = 1; } stage = SELECT_COMPONENTS; break; case SELECT_COMPONENTS: if (isUpgrade) rc = queryIndividual(&doIndividual); else rc = selectComponents(csp, psp, &doIndividual); if (rc) return rc; if (doIndividual) stage = SELECT_PACKAGES; else stage = SELECT_VERIFY; break; case SELECT_PACKAGES: if (doIndividual) { rc = selectPackagesByGroup(psp); if (rc == INST_CANCEL) stage = SELECT_COMPONENTS; else if (rc) return rc; else stage = SELECT_VERIFY; } else { if (dir == -1) stage = SELECT_COMPONENTS; else stage = SELECT_VERIFY; } break; case SELECT_VERIFY: rc = psVerifyDependencies(psp, 0); if (rc == INST_ERROR) return rc; else if (rc) stage = SELECT_PACKAGES, dir = -1; else stage = SELECT_DONE; } } return 0; } static void emptyErrorCallback(void) { } int psVerifyDependencies(struct pkgSet * psp, int fixup) { rpmdb db = NULL; rpmTransactionSet rpmdeps; int i; struct rpmDependencyConflict * conflicts; struct packageInfo * package; int numConflicts, numProblems; newtComponent okay, form, textbox, info, cancel, answer; newtGrid grid, buttons; char * text, buf[80]; char selectPackages; char * reflowedText; int width, height; rpmErrorCallBackType old; if (!access("/mnt/var/lib/rpm/packages.rpm", R_OK)) { rpmSetVerbosity(RPMMESS_FATALERROR); old = rpmErrorSetCallback(emptyErrorCallback); if (rpmdbOpen("/mnt", &db, O_RDWR | O_CREAT, 0644)) db = NULL; rpmErrorSetCallback(old); rpmSetVerbosity(RPMMESS_NORMAL); } rpmdeps = rpmtransCreateSet(db, NULL); for (i = 0; i < psp->numPackages; i++) { if (psp->packages[i]->selected) rpmtransAddPackage(rpmdeps, psp->packages[i]->h, NULL, psp->packages[i], 0, NULL); else rpmtransAvailablePackage(rpmdeps, psp->packages[i]->h, psp->packages[i]); } rpmdepCheck(rpmdeps, &conflicts, &numConflicts); rpmtransFree(rpmdeps); if (db) rpmdbClose(db); /* XXX This is a hack. rpmlib seems to have problems in the conflicts stuff */ numProblems = 0; for (i = 0; i < numConflicts; i++) { if (conflicts[i].sense == RPMDEP_SENSE_REQUIRES) numProblems++; } if (!numProblems) { return 0; } if (fixup) { for (i = 0; i < numConflicts; i++) { package = (void *) conflicts[i].suggestedPackage; if (package) package->selected = 1; } rpmdepFreeConflicts(conflicts, numConflicts); return 0; } text = malloc(80 * numConflicts); *text = '\0'; for (i = 0; i < numConflicts; i++) { /* HACK XXX */ if (conflicts[i].sense == RPMDEP_SENSE_CONFLICTS) continue; package = (void *) conflicts[i].suggestedPackage; if (package) sprintf(buf, "%-20s %-20s", conflicts[i].byName, package->name); else sprintf(buf, "%-20s %s", conflicts[i].byName, _("no suggestion")); if (i) strcat(text, "\n"); strcat(text, buf); } if (!kickstart) { form = newtForm(NULL, NULL, 0); reflowedText = newtReflowText( _("Some of the packages you have selected to install require " "packages you have not selected. If you just select Ok " "all of those required packages will be installed."), 45, 5, 5, &width, &height); info = newtTextbox(1, 1, width, height, NEWT_TEXTBOX_WRAP); newtTextboxSetText(info, reflowedText); free(reflowedText); sprintf(buf, "%-20s %-20s", _("Package"), _("Requirement")); buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL); textbox = newtTextbox(-1, -1, 45, 5, numProblems > 5 ? NEWT_FLAG_SCROLL : 0); newtTextboxSetText(textbox, text); grid = newtCreateGrid(1, 5); newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, info, 0, 0, 0, 0, 0, 0); newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, newtLabel(-1, -1, buf), 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(grid, 0, 2, NEWT_GRID_COMPONENT, textbox, 0, 0, 0, 0, 0, 0); newtGridSetField(grid, 0, 3, NEWT_GRID_COMPONENT, newtCheckbox(-1, -1, _("Install packages to " "satisfy dependencies"), '*', NULL, &selectPackages), 0, 1, 0, 0, 0, 0); newtGridSetField(grid, 0, 4, NEWT_GRID_SUBGRID, buttons, 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); newtGridAddComponentsToForm(grid, form, 1); newtFormSetCurrent(form, okay); newtGridWrappedWindow(grid, _("Unresolved Dependencies")); newtGridFree(grid, 1); answer = newtRunForm(form); newtFormDestroy(form); newtPopWindow(); if (answer == cancel) { free(conflicts); return INST_CANCEL; } } if (kickstart || selectPackages != ' ') { for (i = 0; i < numConflicts; i++) { package = (void *) conflicts[i].suggestedPackage; if (package) package->selected = 1; } } free(conflicts); return 0; } ; static int selectComponents(struct componentSet * csp, struct pkgSet * psp, int * doIndividual) { int i, j; newtComponent okay, form, checklist, checkbox, sb, cancel, answer, blank; newtGrid buttons, checkgrid, grid; char val, individualPackages, everything; char * states; int row; int numCols, numRows; newtGetScreenSize(&numCols, &numRows); individualPackages = *doIndividual ? '*' : ' '; states = alloca(sizeof(*states) * csp->numComponents); form = newtForm(NULL, NULL, 0); sb = newtVerticalScrollbar(-1, -1, numRows - 18, 9, 10); checklist = newtForm(sb, NULL, 0); newtFormSetHeight(checklist, numRows - 18); newtFormSetBackground(checklist, NEWT_COLORSET_CHECKBOX); for (i = 0, row = 0; i < csp->numComponents; i++) { if (csp->comps[i].inmenu) { if (csp->comps[i].selected) val = '*'; else val = ' '; checkbox = newtCheckbox(0, row++, csp->comps[i].name, val, NULL, &states[i]); newtFormAddComponent(checklist, checkbox); } else states[i] = ' '; } checkbox = newtCheckbox(0, row++, _("Everything"), ' ', NULL, &everything); newtFormAddComponent(checklist, checkbox); blank = newtForm(NULL, NULL, 0); newtFormSetHeight(blank, numRows - 18); newtFormSetWidth(blank, 2); newtFormSetBackground(blank, NEWT_COLORSET_CHECKBOX); checkgrid = newtGridHCloseStacked(NEWT_GRID_COMPONENT, checklist, NEWT_GRID_COMPONENT, blank, NEWT_GRID_COMPONENT, sb, NULL); checkbox = newtCheckbox(-1, -1, _("Select individual packages"), individualPackages, NULL, &individualPackages); buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL); grid = newtGridBasicWindow(newtLabel(-1, -1, _("Choose components to install:")), newtGridVStacked(NEWT_GRID_SUBGRID, checkgrid, NEWT_GRID_COMPONENT, checkbox, NULL), buttons); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindow(grid, _("Components to Install")); newtGridFree(grid, 1); answer = newtRunForm(form); newtFormDestroy(form); newtPopWindow(); if (answer == cancel) return INST_CANCEL; *doIndividual = (individualPackages != ' '); for (i = 0; i < psp->numPackages; i++) { psp->packages[i]->selected = 0; } if (everything != ' ') { for (i = 0; i < psp->numPackages; i++) { for (j = 0; skipList[j]; j++) { if (!strcmp(psp->packages[i]->name, skipList[j])) break; } if (!skipList[j]) { psp->packages[i]->selected = 1; } } } for (i = 0; i < csp->numComponents; i++) { if (!strcasecmp(csp->comps[i].name, "Base")) { csp->comps[i].selected = 1; for (j = 0; j < csp->comps[i].ps.numPackages; j++) csp->comps[i].ps.packages[j]->selected |= csp->comps[i].selected; } if (csp->comps[i].inmenu) { if (states[i] != ' ') csp->comps[i].selected = 1; else csp->comps[i].selected = 0; for (j = 0; j < csp->comps[i].ps.numPackages; j++) csp->comps[i].ps.packages[j]->selected |= csp->comps[i].selected; } } for (i = 0; i < psp->numPackages; i++) { if (psp->packages[i]->manuallySelected) psp->packages[i]->selected = 1; } return 0; } void psFreeComponentSet(struct componentSet * csp) { int i; struct component * currcomp; currcomp = csp->comps; for (i = 0; i < csp->numComponents; i++, currcomp++) { free(currcomp->ps.packages); } free(csp->comps); } int psFromHeaderListDesc(FD_t fd, struct pkgSet * psp, int noSeek) { struct pkgSet ps; int end = 0, type, count; unsigned int * sizeptr; Header h; int packagesAlloced; char * name, * group; int done = 0; ps.numPackages = 0; packagesAlloced = 5; ps.packages = malloc(sizeof(*ps.packages) * packagesAlloced); if (!noSeek) { count = fdLseek(fd, 0, SEEK_CUR); end = fdLseek(fd, 0, SEEK_END); fdLseek(fd, count, SEEK_SET); } while (!done) { h = headerRead(fd, HEADER_MAGIC_YES); if (!h && noSeek) { done = 1; } else if (!h) { newtWinMessage(_("Error"), _("Ok"), "error reading header at %d\n", (int) fdLseek(fd, 0, SEEK_CUR)); free(ps.packages); return INST_ERROR; } else { headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count); if (!headerGetEntry(h, RPMTAG_GROUP, &type, (void **) &group, &count)) { group = "(unknown group)"; } else if (!strlen(group)) group = "(unknown group)"; if (ps.numPackages == packagesAlloced) { packagesAlloced += 5; ps.packages = realloc(ps.packages, sizeof(*ps.packages) * packagesAlloced); } ps.packages[ps.numPackages] = malloc(sizeof(struct packageInfo)); ps.packages[ps.numPackages]->h = h; ps.packages[ps.numPackages]->selected = 0; ps.packages[ps.numPackages]->manuallySelected = 0; if (headerGetEntry(h, RPMTAG_SIZE, &type, (void **) &sizeptr, &count)) ps.packages[ps.numPackages]->size = *sizeptr; else ps.packages[ps.numPackages]->size = 0; if (skipPackage(name)) ps.packages[ps.numPackages]->inmenu = 0; else ps.packages[ps.numPackages]->inmenu = 1; ps.packages[ps.numPackages]->name = name; ps.packages[ps.numPackages]->group = group; headerGetEntry(h, FILENAME_TAG, &type, (void **) &ps.packages[ps.numPackages]->data, &count); ps.numPackages++; } if (!noSeek) { if (end <= fdLseek(fd, 0, SEEK_CUR)) done = 1; } } logMessage("psFromHeaderListDesc read %d headers", ps.numPackages); pkgSort(&ps); *psp = ps; return 0; } int psFromHeaderListFile(char * file, struct pkgSet * psp) { int rc; FD_t fd; fd = fdOpen(file, O_RDONLY, 0644); if (!fd < 0) { errorWindow(_("error opening header file: %s")); return INST_ERROR; } rc = psFromHeaderListDesc(fd, psp, 0); fdClose(fd); return rc; } static int skipPackage(char * name) { char ** item; for (item = skipList; *item; item++) { if (!strcmp(*item, name)) return 1; } return 0; } struct grpInfo { char *group; int totalSize; int selectedSize; int totalPkgs; int selectedPkgs; int collapsed; }; #define BYGROUP_GROUP 0x80000000 #define BYGROUP_PKG 0x40000000 #define BYGROUP_CHANGE 0x20000000 #define BYGROUP_MASK 0x0fffffff static char *printSize(int size) { static char buf[8]; if (!size) *buf = '\0'; else sprintf(buf, "%3d.%dM", size / 1024, (size * 10 / 1024) % 10); return buf; } static unsigned int printGroup(newtComponent listbox, struct grpInfo * groupInfo, char * buf, int len, unsigned int j) { unsigned int k = j & BYGROUP_MASK; sprintf(buf, "%c [%c] %-*s %s", groupInfo[k].collapsed ? '+' : '-', groupInfo[k].selectedPkgs == groupInfo[k].totalPkgs ? '*' : groupInfo[k].selectedPkgs ? 'o' : ' ', len - 15, groupInfo[k].group, printSize(groupInfo[k].selectedSize)); /* Why is there no exported way to get num from key? We could use newtListboxSetEntry then. */ if (newtListboxInsertEntry(listbox, buf, (void *)(j ^ BYGROUP_CHANGE), (void *)j)) { j ^= BYGROUP_CHANGE; newtListboxInsertEntry(listbox, buf, (void *)(j ^ BYGROUP_CHANGE), (void *)j); } newtListboxDeleteEntry(listbox, (void *)j); return j ^ BYGROUP_CHANGE; } static int printPkg(newtComponent listbox, struct pkgSet * psp, char * buf, int len, unsigned int j) { unsigned int k = j & BYGROUP_MASK; sprintf(buf, " [%c] %-*s %s", psp->packages[k]->selected ? '*' : ' ', len - 17, psp->packages[k]->name, printSize(psp->packages[k]->selected ? psp->packages[k]->size / 1024 : 0)); if (newtListboxInsertEntry(listbox, buf, (void *)(j ^ BYGROUP_CHANGE), (void *)j)) { j ^= BYGROUP_CHANGE; newtListboxInsertEntry(listbox, buf, (void *)(j ^ BYGROUP_CHANGE), (void *)j); } newtListboxDeleteEntry(listbox, (void *)j); return j ^ BYGROUP_CHANGE; } static int selectPackagesByGroup(struct pkgSet * psp) { newtComponent done, form, listbox, back, tmp = NULL; struct newtExitStruct answer; newtComponent sizeLabel; int i, row, numGroups, width, height, len; unsigned int j, k; unsigned int kSelected = 0; hashTable ht; htIterator iter; char b[200], * chptr; char * buf; char * group = NULL; newtGrid grid, subgrid, sizegrid, buttons; struct grpInfo *groupInfo; newtGetScreenSize(&width, &height); ht = htNewTable(psp->numPackages); for (i = 0, row = 0; i < psp->numPackages; i++) { if (psp->packages[i]->inmenu) { group = psp->packages[i]->group; chptr = group; while (*chptr && *chptr != '/') chptr++; if (*chptr == '/') { chptr++; while (*chptr && *chptr != '/') chptr++; if (*chptr == '/') { strncpy(b, group, chptr - group); b[chptr - group] = '\0'; group = b; } } } if (group) htAddToTable(ht, group); if (psp->packages[i]->selected) kSelected += (psp->packages[i]->size / 1024); } numGroups = htNumEntries(ht); groupInfo = alloca(sizeof(*groupInfo) * numGroups); memset(groupInfo, 0, sizeof(*groupInfo) * numGroups); htIterStart(&iter); len = 0; i = 0; j = 0; while (htIterGetNext(ht, &iter, &group)) { i = strlen(group); j += i + 1; if (i > len) len = i; } chptr = alloca(j); htIterStart(&iter); i = 0; while (htIterGetNext(ht, &iter, &group)) { groupInfo[i].group = chptr; chptr += strlen(group) + 1; strcpy(groupInfo[i].group, group); groupInfo[i].collapsed = 1; for (j = 0; j < psp->numPackages; j++) { if (psp->packages[j]->inmenu && !strcmp(psp->packages[j]->group, group)) { if (strlen (psp->packages[j]->name) > len - 2) len = strlen(psp->packages[j]->name) + 2; groupInfo[i].totalSize += (psp->packages[j]->size / 1024); groupInfo[i].totalPkgs++; if (psp->packages[j]->selected) { groupInfo[i].selectedSize += (psp->packages[j]->size / 1024); groupInfo[i].selectedPkgs++; } } } i++; } len += 15; buf = alloca(len); qsort(groupInfo, numGroups, sizeof(*groupInfo), strptrCmp); htFreeHashTable(ht); form = newtForm(NULL, NULL, 0); buttons = newtButtonBar(_("Done"), &done, _("Back"), &back, NULL); sprintf(buf, "%dM", kSelected / 1024); while (strlen(buf) < 5) strcat(buf, " "); sizeLabel = newtLabel(-1, -1, buf); sizegrid = newtCreateGrid(2, 1); newtGridSetField(sizegrid, 0, 0, NEWT_GRID_COMPONENT, newtLabel(-1, -1, _("Installed system size:")), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(sizegrid, 1, 0, NEWT_GRID_COMPONENT, sizeLabel, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); subgrid = newtCreateGrid(2, 2); newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT, newtLabel(-1, -1, _("Choose a group to examine")), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(subgrid, 0, 1, NEWT_GRID_COMPONENT, newtLabel(-1, -1, _("Press F1 for a package description")), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(subgrid, 1, 0, NEWT_GRID_SUBGRID, sizegrid, 3, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); listbox = newtListbox(3, 3, height - 16, NEWT_FLAG_RETURNEXIT | NEWT_FLAG_SCROLL); for (i = 0; i < numGroups; i++) { sprintf(buf, "+ [%c] %-*s %s", groupInfo[i].selectedPkgs == groupInfo[i].totalPkgs ? '*' : groupInfo[i].selectedPkgs ? 'o' : ' ', len - 15, groupInfo[i].group, printSize(groupInfo[i].selectedSize)); newtListboxAddEntry(listbox, buf, (void *)(BYGROUP_GROUP|i)); } grid = newtCreateGrid(1, 3); newtGridSetField(grid, 0, 0, NEWT_GRID_SUBGRID, subgrid, 0, 0, 0, 0, 0, 0); newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, listbox, 0, 1, 0, 0, 0, 0); newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindow(grid, _("Select Group")); newtGridFree(grid, 1); newtFormAddHotKey(form, ' '); newtFormAddHotKey(form, '*'); newtFormAddHotKey(form, '+'); newtFormAddHotKey(form, '-'); newtFormAddHotKey(form, NEWT_KEY_F1); /* Main loop. Handle enter/space bar on group entries (start with '+' or '-') and packages (everything else) */ do { newtFormRun(form, &answer); j = (unsigned int)newtListboxGetCurrent(listbox); k = j & BYGROUP_MASK; if (answer.reason == NEWT_EXIT_HOTKEY) { if (answer.u.key == NEWT_KEY_F1) { if (j & BYGROUP_PKG) showPackageInfo(psp->packages[k]->h); continue; } } if (answer.u.co == listbox || (answer.reason == NEWT_EXIT_HOTKEY && newtFormGetCurrent(form) == listbox)) { if ((j & BYGROUP_GROUP)) { group = groupInfo[k].group; switch (answer.u.key) { case '*': case '+': /* Select the whole group */ if (groupInfo[k].selectedPkgs == groupInfo[k].totalPkgs) continue; kSelected += groupInfo[k].totalSize - groupInfo[k].selectedSize; sprintf(buf, "%dM", kSelected / 1024); newtLabelSetText(sizeLabel, buf); groupInfo[k].selectedPkgs = groupInfo[k].totalPkgs; groupInfo[k].selectedSize = groupInfo[k].totalSize; newtListboxSetCurrentByKey(listbox, (void *)printGroup(listbox, groupInfo, buf, len, j)); for (i = 0; i < psp->numPackages; i++) { if (psp->packages[i]->inmenu && !strcmp(psp->packages[i]->group, group)) { psp->packages[i]->selected = 1; psp->packages[i]->manuallySelected = 1; if (!groupInfo[k].collapsed) printPkg(listbox, psp, buf, len, BYGROUP_PKG | i); } } continue; case '-': /* Deselect the whole group */ if (!groupInfo[k].selectedPkgs) continue; kSelected -= groupInfo[k].selectedSize; sprintf(buf, "%dM", kSelected / 1024); newtLabelSetText(sizeLabel, buf); groupInfo[k].selectedPkgs = 0; groupInfo[k].selectedSize = 0; newtListboxSetCurrentByKey(listbox, (void *)printGroup(listbox, groupInfo, buf, len, j)); for (i = 0; i < psp->numPackages; i++) { if (psp->packages[i]->inmenu && !strcmp(psp->packages[i]->group, group)) { psp->packages[i]->selected = 0; psp->packages[i]->manuallySelected = 0; if (!groupInfo[k].collapsed) printPkg(listbox, psp, buf, len, BYGROUP_PKG | i); } } continue; default: break; } } if ((j & BYGROUP_GROUP) && !groupInfo[k].collapsed) { /* collapse the tree */ group = groupInfo[k].group; for (i = psp->numPackages - 1; i > 0; i--) { if (psp->packages[i]->inmenu && !strcmp(psp->packages[i]->group, group)) { if (newtListboxDeleteEntry(listbox, (void *)(BYGROUP_PKG | i)) < 0) newtListboxDeleteEntry(listbox, (void *)(BYGROUP_PKG | BYGROUP_CHANGE | i)); } } groupInfo[k].collapsed = 1; newtListboxSetCurrentByKey(listbox, (void *)printGroup(listbox, groupInfo, buf, len, j)); } else if ((j & BYGROUP_GROUP) && groupInfo[k].collapsed) { /* expand the tree */ /* Go from the end to the beginning of the package list so we insert in alphabetical order */ group = groupInfo[k].group; for (i = psp->numPackages - 1; i > 0; i--) { if (psp->packages[i]->inmenu && !strcmp(psp->packages[i]->group, group)) { sprintf(buf, " [%c] %-*s %s", psp->packages[i]->selected ? '*' : ' ', len - 17, psp->packages[i]->name, printSize(psp->packages[i]->selected ? psp->packages[i]->size / 1024 : 0)); newtListboxInsertEntry(listbox, buf, (void *)(BYGROUP_PKG | i), (void *)j); } } groupInfo[k].collapsed = 0; newtListboxSetCurrentByKey(listbox, (void *)printGroup(listbox, groupInfo, buf, len, j)); } else if (j & BYGROUP_PKG) { /* This is a package. */ if ((answer.u.key == '+' || answer.u.key == '*') && psp->packages[k]->selected) continue; if (answer.u.key == '-' && !psp->packages[k]->selected) continue; for (i = 0; i < numGroups; i++) if (!strcmp(groupInfo[i].group, psp->packages[k]->group)) break; psp->packages[k]->selected = !psp->packages[k]->selected; psp->packages[k]->manuallySelected = psp->packages[k]->selected; if (psp->packages[k]->selected) { kSelected += (psp->packages[k]->size / 1024); groupInfo[i].selectedPkgs++; groupInfo[i].selectedSize += (psp->packages[k]->size / 1024); } else { kSelected -= (psp->packages[k]->size / 1024); groupInfo[i].selectedPkgs--; groupInfo[i].selectedSize -= (psp->packages[k]->size / 1024); } printGroup(listbox, groupInfo, buf, len, BYGROUP_GROUP | i); newtListboxSetCurrentByKey(listbox, (void *)printPkg(listbox, psp, buf, len, j)); } sprintf(buf, "%dM", kSelected / 1024); newtLabelSetText(sizeLabel, buf); } tmp = newtFormGetCurrent(form); } while ((tmp != done && tmp != back) && (answer.u.co == listbox || (answer.reason == NEWT_EXIT_HOTKEY && (answer.u.key == ' ' || answer.u.key == NEWT_KEY_F1 || answer.u.key == '*' || answer.u.key == '+' || answer.u.key == '-')))); newtFormDestroy(form); newtPopWindow(); if (tmp == back) return INST_CANCEL; return 0; } static void showPackageInfo(Header h) { newtComponent form, textbox, okay; newtGrid subgrid, grid; char * name, * version, * description, * release; uint_32 * size; int type, count; char infostr[255]; char * buf, * from, * to; headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count); headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version, &count); headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &count); if (!headerGetEntry(h, RPMTAG_SIZE, &type, (void **) &size, &count)) size = 0; if (!headerGetEntry(h, RPMTAG_DESCRIPTION, &type, (void **) &description, &count)) { if (!headerGetEntry(h, RPMTAG_SUMMARY, &type, (void **) &description, &count)) { description = _("(none available)"); } } subgrid = newtCreateGrid(2, 2); newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT, newtLabel(-1, 1, _("Package:")), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(subgrid, 0, 1, NEWT_GRID_COMPONENT, newtLabel(-1, -1, _("Size :")), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); sprintf(infostr, "%s-%s-%s", name, version, release); newtGridSetField(subgrid, 1, 0, NEWT_GRID_COMPONENT, newtLabel(-1, -1, infostr), 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); sprintf(infostr, "%d", *size); newtGridSetField(subgrid, 1, 1, NEWT_GRID_COMPONENT, newtLabel(-1, -1, infostr), 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); to = buf = alloca(strlen(description) + 1); from = description; /* Rip out all '\n' that don't start new paragraphs */ while (*from) { if (*from == '\n') { if ((*(from + 1) && isspace(*(from + 1))) || (from > description && *(from - 1) == '\n')) *to++ = '\n'; else *to++ = ' '; } else *to++ = *from; from++; } *to = '\0'; textbox = newtTextbox(-1, -1, 43, 6, NEWT_TEXTBOX_WRAP | NEWT_TEXTBOX_SCROLL); newtTextboxSetText(textbox, buf); okay = newtButton(20, 11, _("Ok")); grid = newtCreateGrid(1, 3); newtGridSetField(grid, 0, 0, NEWT_GRID_SUBGRID, subgrid, 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, textbox, 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(grid, 0, 2, NEWT_GRID_COMPONENT, okay, 0, 1, 0, 0, 0, 0); form = newtForm(NULL, NULL, 0); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindowAt(grid, name, 31, 6); newtGridFree(grid, 1); newtRunForm(form); newtFormDestroy(form); newtPopWindow(); } static int queryIndividual(int * result) { int rc = 0; *result = 0; rc = newtWinTernary(_("Upgrade Packages"), _("Yes"), _("No"), _("Back"), _("The packages you have installed, and any other packages which are " "needed to satisfy their dependencies, have been selected for " "installation. Would you like to customize the set of packages that " "will be upgraded?")); if (rc == 3) rc = INST_CANCEL; else if (rc == 2) { *result = 0; rc = 0; } else { *result = 1; rc = 0; } return rc; }