Logo Search packages:      
Sourcecode: debfoster version File versions  Download package

debfoster.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <locale.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include "config.h"
#include "AVLTree.h"
#include "error.h"
#include "conffile.h"
#include "keepers.h"
#include "status.h"
#include "xargs.h"
#include "asktty.h"
#include "intl.h"

struct undo_record {
      symbol_t name;
      struct undo_record* prev;
};

static AVLTree *keeplist, *postpone;
static struct undo_record *undo_stack = NULL;

static struct option long_options[] = {
      {"help", 0, 0, 'h'},
      {"verbose", 0, 0, 'v'},
      {"version", 0, 0, 'V'},
      {"quiet", 0, 0, 'q'},
      {"force", 0, 0, 'f'},
      {"mark-only", 0, 0, 'm'},
      {"upgrade", 0, 0, 'u'},
      {"keeperfile", 1, 0, 'k'},
      {"no-keeperfile", 0, 0, 'n'},
      {"show-keepers", 0, 0, 'a'},
      {"show-orphans", 0, 0, 's'},
      {"show-depends", 1, 0, 'd'},
      {"show-dependents", 1, 0, 'e'},
      {"show-providers", 1, 0, 'p'},
      {"show-related", 1, 0, 'r'},
      {"use-tasks", 0, 0, 't'},
      {"option", 1, 0, 'o'},
      {"config", 1, 0, 'c'},
      {"ignore-default-rules", 0, 0, 'i'},
      {0, 0, 0, 0}
};

enum {
      EAllDepends,
      EOrphansOnly
};

enum {
      ENoAlternatives,
      ECommittedAlternatives,
      EAllAlternatives
};

/* Find out the number of columns supported by the tty.
 * For non-tty's the default is 80 columns.
 */
int columns(void) {
#ifdef TIOCGWINSZ
      struct winsize w;
      if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&w) && w.ws_col)
            return w.ws_col;
#endif
      return 80;
}

/* Print a message if Verbose mode is on. */
int Vprintf(char *fmt, ...) {
      va_list ap;
      int n = 0;

      if(Verbose) {
            va_start(ap, fmt);
            n = vprintf(fmt, ap);
            va_end(ap);
      }
      return n;
}


/* Print a title and a set of symbols nicely on tty.
 * If the set is empty, nothing is printed.
 */
int show_set(AVLTree *set, char *fmt, ...) {
      va_list ap;
      AVLNode *p;
      int width = columns() - 2, n = width;

      if(set->head) {
            va_start(ap, fmt);
            vprintf(fmt, ap);
            va_end(ap);

            for(p = set->head; p; p = p->next) {
                  if(n + strlen((symbol_t)p->item) >= width)
                        n = printf("\n ");
                  n += printf(" %s", (symbol_t)p->item);
            }
            printf("\n");
            return 1;
      }
      return 0;
}

static void showinfo(symbol_t pkg) {
      AVLTree t;
      AVLNode n;
      AVLInitTree(&t, (AVLCompare)strcmp);
      AVLInitNode(&n, pkg);
      AVLInsertTopNode(&t, &n);
      xargs(&t, InfoCmd);
}

/* A two-key sort function for optimizing packet processing
 * order for the interactive engine.
 */
int pkgsortcmp(struct package *a, struct package *b) {
      int k = b->orphan_depends - a->orphan_depends;
      return k ? k : strcasecmp(a->name, b->name);
}

/* Add a package and all its dependents to the keep list. */
void add_keeplist(struct package *pkg) {
        AVLNode *p;

      if(!AVLInsert(keeplist, pkg->name))
            return;

      /* Add all direct depends on keeper list */
      for(p = pkg->depends.head; p; p = p->next)
            add_keeplist(pkg_find(p->item));

      /* If this packge is a meta package and there is exactly one
       * installed package providing it, add the provider to the
       * keeper list. If there are multiple providers of the meta
       * package, the user will have to answer some questions.
       * In Force-mode we add all providers, since we don't know
       * which one(s) the user would like to keep.
       */
      if(!pkg->installed && (Force || pkg->provider_count == 1))
            for(p = pkg->providers.head; p; p = p->next)
                  add_keeplist(pkg_find(p->item));
}

/* Add all packages on the keepers and postpone lists, including their
 * dependencies, to the keeplist.
 */
void build_keeplist(void) {
      AVLNode *c;
      struct package *pkg;

      AVLFreeNodes(keeplist);
      for(c = packages->head; c; c = c->next) {
            pkg = (struct package *)c->item;
            if(pkg->keep || AVLSearch(keepers, pkg->name) || AVLSearch(postpone, pkg->name))
                  add_keeplist(pkg);
      }
}

/* Remove packages from keeper list if they are no longer installed. */
void prune_keepers(void) {
      AVLNode *c, *p;
      struct package *pkg;

      for(c = keepers->head; c; c = p) {
            p = c->next;
            pkg = pkg_find(c->item);
            if(!pkg || !(pkg->installed || pkg->task)) {
                  Vprintf(_("Package was removed: %s\n"), (symbol_t)c->item);
                  AVLDeleteNode(keepers, c);
            }
      }
}

/* Do a graph search for all dependencies of package <name>. The
 * <visited> list has a dual purpose. It serves as a loop detection
 * mechanism in the graph search. In the end it contains the result
 * of the search. Initially <visited> should be empty.
 *
 *  <include_providers> indicates how to deal with meta packages:
 *    ENoAlternatives           Don't include providers of meta packages
 *                              as dependencies.
 *    ECommittedAlternatives    If there is a single package that provides
 *                              a meta package, include it as a dependency.
 *    EAllAlternatives          Include all installed providers of a meta
 *                              package as dependencies.
 *
 *  <orphans_only> indicates whether to include all dependecies or only
 *  those that are potentially orphans:
 *    EAllDepends               Include all dependencies.
 *    EOrphansOnly              Include only potential orphans.
 *
 */
int all_depends(symbol_t name, AVLTree *visited, int include_providers, int orphans_only) {
      struct package *pkg;
      AVLNode *p;
      int count;

      if((orphans_only && AVLSearch(keeplist, name)) || !AVLInsert(visited, name))
            return 0;

      pkg = pkg_find(name);
      if(!pkg)
            return 0;
      count = pkg->installed ? 1 : 0;

      for(p = pkg->depends.head; p; p = p->next)
            count += all_depends(p->item, visited, include_providers, orphans_only);

      if(include_providers && (include_providers == EAllAlternatives
      || pkg->provider_count == 1))
            for(p = pkg->providers.head; p; p = p->next)
                  count += all_depends(p->item, visited, include_providers, orphans_only);

      return count;
}

/* Do a graph search for all dependents of package <name>. The
 * <visited> list has a dual purpose. It serves as a loop detection
 * mechanism in the graph search. In the end it contains the result
 * of the search. Initially <visited> should be empty.
 *
 *  <include_provides> indicates how to deal with meta packages:
 *    ENoAlternatives           Don't include meta packages as dependents.
 *    ECommittedAlternatives    If the package is the only one that provides
 *                              a meta package, include the meta package as
 *                              a dependent.
 *    EAllAlternatives          Always include meta packages as dependents
 *                              regardles of how many other packages provide
 *                              them.
 *
 */
void all_dependents(symbol_t name, AVLTree *visited, int include_provides) {
      AVLNode *p;
      struct package *pkg;
      if(AVLInsert(visited, name)) {
            pkg = pkg_find(name);
            for(p = pkg->dependents.head; p; p = p->next)
                  all_dependents(p->item, visited, include_provides);
            if(include_provides)
                  for(p = pkg->provides.head; p; p = p->next)
                        if(include_provides == EAllAlternatives
                        || pkg_find(p->item)->provider_count == 1)
                              all_dependents(p->item, visited, include_provides);
      }
}

/* add_provider(pkg, pkg) adds package <pkg> to the providers lists of
 * all meta packages that it provides. The routine traverses the graph
 * of provides links and adds the package to each node.
 *
 * We collapse any provider chains so that the providers list of
 * package X is guaranteed to contain only installed packages that
 * really provide X.  This simplifies the dependency handling for
 * alternative packages.
 *
 * We also detect problems such as the following:
 *
 *     A     Installed packages B and C depend on each other and both
 *    / \    claim to provide A. A now appears to have two alternative
 *   B===C   dependencies, whereas in truth both B and C are required.
 *
 * We break the above into one of the following (non-deterministic):
 *
 *     A       A
 *    /         \
 *   B===C   B===C
 *
 */
void add_provider(struct package *pkg, struct package *ppkg) {
        AVLNode *c, *p;
      AVLTree depends, dependents;

      if(AVLSearch(&ppkg->providers, pkg->name))
            return;

      if(ppkg->provider_count) {
            /* Remove multiple alternative dependencies to a dependency loop */
            AVLInitTree(&depends, (AVLCompare)symcmp);
            AVLInitTree(&dependents, (AVLCompare)symcmp);
            all_depends(pkg->name, &depends, ENoAlternatives, EAllDepends);
            all_dependents(pkg->name, &dependents, ENoAlternatives);
            for(c = ppkg->providers.head; c; c = p) {
                  p = c->next;
                  if(AVLSearch(&depends, c->item)) {
                        /* pkg depends on something already on provider list. Don't add pkg. */
                          pkg = NULL;
                        break;
                  }
                  if(AVLSearch(&dependents, c->item)) {
                        /* Something on the provider list depends on pkg. Remove it. */
                        AVLDeleteNode(&ppkg->providers, c);
                        ppkg->provider_count--;
                  }
            }
      }

      if (pkg) {
            AVLInsert(&ppkg->providers, pkg->name);
            ppkg->provider_count++;

            for(c = ppkg->provides.head; c; c = c->next)
                  add_provider(pkg, pkg_find(c->item));
      }
}

/* remove_provider(pkg, pkg) removes package <pkg> from the providers
 * lists of all meta packages that it provides. The routine traverses
 * the graph of "provides" links and removes the package from each
 * node.
 *
 * This routine gets called when debfoster has decided to remove a
 * particular package. If, as a result of removing the package, any
 * other package X becomes the last remaining alternative that
 * provides a dependency Y that is on keeplist, X and all its
 * dependencies will also be added to the keeplist. This locks them as
 * required dependencies.
 */
void remove_provider(struct package *pkg, struct package *ppkg) {
        AVLNode *p;

      if(!(p = AVLSearch(&ppkg->providers, pkg->name)))
            return;

      AVLDeleteNode(&ppkg->providers, p);
      ppkg->provider_count--;

      if(!ppkg->installed && ppkg->provider_count == 1
      && AVLSearch(keeplist, ppkg->name))
            add_keeplist(pkg_find(ppkg->providers.head->item));

      for(p = ppkg->provides.head; p; p = p->next)
              remove_provider(pkg, pkg_find(p->item));
}

/* Initialize list of installed packages. Reads the dpkg status file
 * and builds the dependents and providers lists for each package.
 */
void init_packages(int ignore_defaults) {
      AVLNode *c, *p;
      struct package *pkg, *ppkg;

      /* Read dpkg status */
      readstatus();

      /* build provider and dependent lists for each package */
      for(c = packages->head; c; c = c->next) {
            pkg = (struct package *)c->item;
            if(pkg->installed) {
                  if(ignore_defaults) { /* clear keep and nokeep hints */
                        pkg->keep = 0;
                        pkg->nokeep = 0;
                  }
                  add_provider(pkg, pkg);
                  for(p = pkg->depends.head; p; p = p->next) {
                        ppkg = pkg_find(p->item);
                        AVLInsert(&ppkg->dependents, pkg->name);
                  }
            }
      }
}

/* Count the number of orphans that would be committed to keeplist if
 * <pkg> were to be marked as a keeper.
 */
void count_orphan_depends(struct package *pkg) {
      AVLTree visited;
      AVLInitTree(&visited, (AVLCompare)symcmp);
      pkg->orphan_depends = all_depends(pkg->name, &visited, ECommittedAlternatives, EOrphansOnly);
}

/* --show-providers option */
void show_providers(symbol_t name) {
      struct package *pkg;

      if(!(pkg = pkg_find(name))) {
            printf(_("Not a dependency: \"%s\"\n"), name);
            return;
      }

      if(pkg->installed)
            printf(_("%s is an installed package.\n"), pkg->name);
      else if(pkg->providers.head)
            show_set(&pkg->providers, _("Dependency %s is met by:"), name);
      else
            printf(_("Dependency %s is not met by any package.\n"), pkg->name);
}

/* --show-depends option */
void show_depends(symbol_t name) {
      struct package *pkg;
      AVLTree visited;
      AVLNode *c, *p;

      if(!(pkg = pkg_find(name)) || !(pkg->installed || pkg->task)) {
            printf(_("Not an installed package: \"%s\"\n"), name);
            return;
      }

      AVLInitTree(&visited, (AVLCompare)strcasecmp);
      all_depends(name, &visited, ECommittedAlternatives, EAllDepends);

      for(c = visited.head; c; c = p) {
            p = c->next;
            if(c->item == name || !pkg_find(c->item)->installed)
                  AVLDeleteNode(&visited, c);
      }
      if(!show_set(&visited, _("Package %s depends on:"), name))
            printf(_("Package %s has no depends.\n"), name);
}

/* --show-dependents option */
void show_dependents(symbol_t name) {
      struct package *pkg;
      AVLTree dependents;
      AVLNode *c, *p;
      int others = 0, count = 0;

      if(!(pkg = pkg_find(name))) {
            printf(_("Not an installed package: \"%s\"\n"), name);
            return;
      }

      AVLInitTree(&dependents, (AVLCompare)symcmp);
      all_dependents(name, &dependents, ECommittedAlternatives);

      for(c = dependents.head; c; c = p) {
            p = c->next;
            if (!AVLSearch(keepers, c->item)) {
                  others |= pkg_find(c->item)->keep;
                  AVLDeleteNode(&dependents, c);
            } else
                  count++;
      }
      if (!show_set(&dependents, _("The following %d packages on keeper list rely on %s:"), count, name))
            printf(_("Packages on keeper list do not rely on %s.\n"), name);
      printf(_("Packages kept by default rules %s %s.\n"), others ? _("rely on") : _("do not rely on"), name);
}

/* Find a set of orphan packages that are dependencies of orphan
 * package <name> and not dependencies of any other package that is
 * not also a dependency of <name>. The set consists with a high
 * probability of packages that are closely related together and can
 * be removed as a group if the user so wishes.
 */
int related_set(symbol_t name, AVLTree *set) {
      AVLTree ancestors, eliminate;
      AVLNode *c, *p;
      struct package *pkg, *ppkg;
      int count = 0;

      AVLInitTree(&ancestors, (AVLCompare)symcmp);
      AVLInitTree(&eliminate, (AVLCompare)symcmp);

      all_depends(name, set, ECommittedAlternatives, EOrphansOnly);
      AVLInsert(&ancestors, name);
      for(c = set->head; c; c = c->next)
            all_dependents(c->item, &ancestors, ECommittedAlternatives);
      AVLDelete(&ancestors, name);

      for(c = ancestors.head; c; c = c->next) {
            if(!AVLSearch(set, c->item) && !AVLSearch(nokeepers, c->item)) {
                  ppkg = pkg_find(c->item);
                  if(ppkg->installed && !ppkg->nokeep)
                        all_depends(c->item, &eliminate, ECommittedAlternatives, EOrphansOnly);
            }
      }

      for(c = set->head; c; c = p) {
            p = c->next;
            if(c->item == name
            || AVLSearch(&eliminate, c->item)
            || AVLSearch(nokeepers, c->item)
            || (pkg = pkg_find(c->item),
                  !pkg->installed || pkg->keep || pkg->nokeep))
                  AVLDeleteNode(set, c);
            else
                  count++;
      }

      return count;
}

/* --show-related option */
void show_related(symbol_t name) {
      struct package *pkg;
      AVLTree related;
      int count;

      if(!(pkg = pkg_find(name)) || !(pkg->installed || pkg->task)) {
            printf(_("Not an installed package: \"%s\"\n"), name);
            return;
      }

      AVLInitTree(&related, (AVLCompare)strcasecmp);
      count = related_set(name, &related);
      if(!show_set(&related, _("The following %d packages are brought in by %s:"), count, name))
            printf(_("No packages are brought in by %s.\n"), name);
}

/* Push a symbol onto undo stack */
void push_undo(symbol_t name) {
      struct undo_record *undo = xmalloc(sizeof(struct undo_record));
      undo->name = name;
      undo->prev = undo_stack;
      undo_stack = undo;
}

/* Pop a symbol from the undo stack. Returns zero if stack is empty. */
symbol_t pop_undo(void) {
      struct undo_record *undo = undo_stack;
      if(undo) {
            symbol_t name = undo->name;
            undo_stack = undo->prev;
            return name;
      }
      return 0;
}

int main(int argc, char **argv) {
      AVLTree *installs, *removes, *orphans, *related;
      AVLNode *c, *p;
      struct package *pkg, *ppkg;
      int i, option_index, ignore_defaults = 0, show_option = 0, nokeeperfile = 0, upgrade = 0, mark_only = getuid();
      int related_count;
      symbol_t show_param = 0, base, undo_entry;

      setlocale (LC_ALL, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      /* global trees */
      packages  = AVLAllocTree((AVLCompare)pkgcmp);
      keepers   = AVLAllocTree((AVLCompare)strcasecmp);
      nokeepers = AVLAllocTree((AVLCompare)strcasecmp);

      /* local trees */
      postpone  = AVLAllocTree((AVLCompare)strcasecmp);
      keeplist  = AVLAllocTree((AVLCompare)symcmp);
      installs  = AVLAllocTree((AVLCompare)strcasecmp);
      removes   = AVLAllocTree((AVLCompare)strcasecmp);
      orphans   = AVLAllocTree((AVLCompare)pkgsortcmp);
      related   = AVLAllocTree((AVLCompare)strcasecmp);

      readconfig(NULL);

      while((i = getopt_long(argc, argv, "vVhqfmuc:k:niasd:e:p:r:to:", long_options, &option_index)) != EOF) {
            option_t opt;
            char *s;
            switch(i) {
                  case '?':
                  case 'h':
                        printf(_("Usage: %s [-ck FILE] [-adefhinopqrsvV] package1 package2-\n"), argv[0]);
                        printf(_("Installs package1, deinstalls package2\n\n"));
                        printf(_("-v, --verbose                  Be a loudmouth\n"));
                        printf(_("-V, --version                  Show version and copyright information\n"));
                        printf(_("-h, --help                     Show this message\n"));
                        printf(_("-q, --quiet                    Silently build keeper file\n"));
                        printf(_("-f, --force                    Force system to conform to keeper file\n"));
                        printf(_("-m, --mark-only                Do not install or delete packages\n"));
                        printf(_("-u, --upgrade                  Try to upgrade dependencies\n"));
                        printf(_("-c, --config FILE              Specify configuration file\n"));
                        printf(_("-k, --keeperfile FILE          Specify keeper file\n"));
                        printf(_("-n, --no-keeperfile            Don't read keeper file\n"));
                        printf(_("-i, --ignore-default-rules     Ignore default rules\n"));
                        printf(_("-a, --show-keepers             Show packages on keeper list\n"));
                        printf(_("-s, --show-orphans             Show orphaned packages\n"));
                        printf(_("-d, --show-depends PACKAGE     Show all depends of PACKAGE\n"));
                        printf(_("-e, --show-dependents PACKAGE  Show dependents of PACKAGE\n"));
                        printf(_("-p, --show-providers PACKAGE   Show packages providing PACKAGE\n"));
                        printf(_("-r, --show-related PACKAGE     Show packages brought in by PACKAGE\n"));
                        printf(_("-t, --use-tasks                Make tasks visible as packages\n"));
                        printf(_("-o, --option OPT=VAL           Override any configuration option\n"));
                        printf(_("\nSee also: debfoster(8)\n"));
                  case 'V':
                        printf(_("debfoster %s -- Copyright (C) 2000,2001 Wessel Dankers.\n"), VERSION);
                                printf(_("Distributed under the GNU General Public License.\n"));
                        exit(i=='?');
                  case 'v':
                        Verbose = !Verbose;
                  break;
                  case 'q':
                        Quiet = !Quiet;
                  break;
                  case 'f':
                        Force = !Force;
                  break;
                  case 'm':
                        mark_only = 1;
                  break;
                  case 'u':
                        upgrade = 1;
                  break;
                  case 'k':
                        find_option("KeeperFile")->set(optarg);
                  break;
                  case 'c':
                        readconfig(optarg); /* Overrides any previous config options */
                  break;
                  case 'i':
                        ignore_defaults = 1; /* Ignore keep and nokeep hints in packages */
                  break;
                  case 'n':
                        nokeeperfile = 1; /* Start clean, don't read keeper file */
                  break;
                  case 'o':
                        s = strchr(optarg, '=');
                        if(s > optarg) {
                              *s++ = '\0';
                              if((opt = find_option(optarg))) {
                                    opt->set(s);
                                    break;
                              }
                        }
                        error_exit(ERROR_USER, _("%s: Invalid config option -- \"%s\"\n"), argv[0], optarg);
                  break;
                  case 'd':
                  case 'e':
                  case 'p':
                  case 'r':
                        show_param = symbol(optarg);
                  case 'a':
                  case 's':
                        show_option = i;
                        mark_only = 1;
                  break;
                  case 't':
                        UseTasks = !UseTasks;
                  break;
            }
      }

      openkeepers(show_option && argc == optind); /* Readonly, if --show-xxx option and no extra args */
      if(!nokeeperfile)
            readkeepers();

      for(i=optind; i<argc; i++) {
            char *e;
            int add;
            symbol_t name;

            if(*argv[i]) {
                  e = strchr(argv[i], '\0');
                  if(*--e == '-') {
                        *e = '\0';
                        add = 0;
                  } else {
                        add = 1;
                  }
                  if(*argv[i]) {
                        name = symbol(argv[i]);
                        if (add) {
                              AVLInsert(keepers, name);
                              AVLInsert(installs, name);
                              AVLDelete(nokeepers, name);
                              AVLDelete(removes, name);
                        } else {
                              AVLDelete(keepers, name);
                              AVLDelete(installs, name);
                              AVLInsert(nokeepers, name);
                              AVLInsert(removes, name);
                        }
                        continue;
                  }
            }
            error_exit(ERROR_USER, _("Not a package: \"%s\"\n"), argv[i]);
      }

      /* Sync keeper file */
      if(optind < argc)
            writekeepers();

      /* Force option will attempt to make the system conform to the keepers file.
       * This may include installing new packages as well as removing packages.
       */
      if(Force) {
            for(c = keepers->head; c; c = c->next)
                  AVLInsert(installs, c->item);
      }

      tty_init();

      init_packages(ignore_defaults);

      /* Print information */
      switch(show_option) {
      case 'a': /* --show-keepers option */
            if(!show_set(keepers, _("The following packages are on keeper list:")))
                  printf(_("The keeper list is empty.\n"));
            show_set(nokeepers, _("The following packages are marked as non-keepers:"));
            return 0;
      case 'd': /* --show-depends option */
            show_depends(show_param);
            return 0;
      case 'e': /* --show-dependents option */
            show_dependents(show_param);
            return 0;
      case 'p': /* --show-providers option */
            show_providers(show_param);
            return 0;
      case 'r': /* --show-related option */
            AVLDelete(keepers, show_param);
            build_keeplist();
            show_related(show_param);
            return 0;
      }

      if(installs->head && !mark_only) {
            /* Prune install list */
            for(c = installs->head; c; c = p) {
                  p = c->next;
                  pkg = pkg_find(c->item);
                  if(pkg && pkg->installed) {
                        if(!upgrade)
                              AVLDeleteNode(installs, c);
                  } else 
                        Vprintf(_("Installing package: %s\n"), (symbol_t)c->item);
            }

            /* Add all dependencies to install list (--upgrade) */
            if(upgrade) {
                  AVLTree install_depends;
                  AVLInitTree(&install_depends, (AVLCompare)symcmp);
                  for(c = installs->head; c; c = c->next)
                        if(pkg_find(c->item))
                              all_depends(c->item, &install_depends, ECommittedAlternatives, EAllDepends);
                  while((c = install_depends.head)) {
                        if(pkg_find(c->item)->installed)
                              AVLInsert(installs, c->item);
                        AVLDeleteNode(&install_depends, c);
                  }
            }

            /* Install packages  */
            if(installs->head) {
                  /* Save a list of packages installed prior to invoking InstallCmd */
                  for(c = packages->head; c; c = c->next) {
                        pkg = (struct package *)c->item;
                        if(pkg->installed)
                              AVLInsert(keeplist, pkg->name);
                  }

                  i = xargs(installs, InstallCmd);
                  if(i) return i;

                  /* Read dpkg status */
                  init_packages(ignore_defaults);

                  /* find out which packages actually were installed */
                  AVLFreeNodes(installs);
                  for(c = packages->head; c; c = c->next) {
                        pkg = (struct package *)c->item;
                        if(!AVLSearch(keeplist, pkg->name)) {
                              AVLInsert(installs, pkg->name);
                              AVLDelete(removes, pkg->name);
                        }
                  }
            }
      }

      if(!mark_only)
            prune_keepers();

      build_keeplist();

      for(;;) {
            /* Build a list of all packages that are not anchored either
             * by the default rules or the keeper list and sort it so that
             * the package with the most orphans dangling from it comes
             * up first.
             */
            AVLFreeNodes(orphans);
            for(c = packages->head; c; c = c->next) {
                  pkg = (struct package *)c->item;
                  if ((pkg->installed || pkg->task) && !AVLSearch(keeplist, pkg->name)) {
                        count_orphan_depends(pkg);
                        AVLInsert(orphans, pkg);
                  }
            }

            i = 0;
            while((c = orphans->head)) {
                  pkg = (struct package *)c->item;

                  /* Has this one found a home? */
                  if(AVLSearch(keeplist, pkg->name)) {
                        AVLDeleteNode(orphans, c);
                        continue;
                  }

                  /* Re-shuffle the pack for optimal ordering. */
                  AVLUnlinkNode(orphans, c);
                  AVLInitNode(c, pkg);

                  /* Handle GuessDepends here. Guesses don't go into keeper file. */
                  if(!ignore_defaults && (base = guessbase(pkg->name)) &&
                              (ppkg = pkg_find(base)) && ppkg->installed) {
                        if(AVLSearch(keeplist, ppkg->name)) {
                              add_keeplist(pkg);
                              continue;
                        }
                        if(AVLSearch(removes, ppkg->name)) {
                              /* Base package was deleted. Set nokeep hint. */
                              pkg->nokeep = 1;
                        } else {
                              /* Don't know yet. Reposition after the base package. */
                              pkg->orphan_depends = ppkg->orphan_depends;
                        }
                  } else
                        count_orphan_depends(pkg); /* Count may change due to user choises */

                  AVLInsertNode(orphans, c);
                  if(orphans->head != c)
                        continue;

                  /* Re-shuffle complete. We're still go. */
                  AVLDeleteNode(orphans, c);

                  /* If the package was just installed by debfoster it's a keeper.
                   * This catches packages that were installed by apt because the user
                   * gave a regular expression on the command line.
                   */
                  if(AVLSearch(installs, pkg->name)) {
                        AVLInsert(keepers, pkg->name);
                        add_keeplist(pkg);
                        AVLDelete(nokeepers, pkg->name);
                        continue;
                  }

                  if(Force || pkg->nokeep || AVLSearch(nokeepers, pkg->name)) {
                        AVLInsert(removes, pkg->name);
                        remove_provider(pkg, pkg);
                        continue;
                  }

                  /* Ignore a task if it doesn't bring at least two packages in */
                  if(pkg->task && !pkg->installed) {
                        if(related_set(pkg->name, related) < 2)
                              continue;
                        AVLFreeNodes(related);
                  }

                  if(Quiet) {
                        AVLInsert(keepers, pkg->name);
                        add_keeplist(pkg);
                        Vprintf(_("Keeping package: %s\n"), pkg->name);
                        continue;
                  }

                  if(show_option == 's') {  /* Treat --show-orphans option as skip */
                        AVLInsert(postpone, pkg->name);
                        add_keeplist(pkg);
                        continue;
                  }

                  related_count = related_set(pkg->name, related);
                  show_set(related, _("\n%s is keeping the following %d packages installed:"), pkg->name, related_count);

                  do {
                        switch((i = tty_ask(_("ynpshiuqx?"), _("Keep %s? [Ynpsiuqx?], [H]elp: "), pkg->name))) {
                              case 4: /* h */
                                                printf("\n");
                                    printf(_("  Yes        Keep %s. [default]\n"), pkg->name);
                                    printf(_("  No         Delete %s.\n"), pkg->name);
                                    printf(_("  Prune      Delete %s and the packages it is keeping installed.\n"), pkg->name);
                                    printf(_("  Skip       Skip this question.\n"));
                                    printf(_("  Help       Print this message.\n"));
                                    printf(_("  Info or ?  Show information about %s.\n"), pkg->name);
                                    printf(_("  Undo       Undo last response.\n"));
                                    printf(_("  Quit       Exit without removing packages.\n"));
                                    printf(_("  Exit       Remove unwanted packages and exit.\n"));
                                                printf("\n");
                                                i = -1;
                              break;
                              case 9: /* ? */
                                        case 5: /* i */
                                    if(pkg->installed)
                                          showinfo(pkg->name);
                                    else if(pkg->task) {
                                          /* Tasksel has no command line option for printing task info
                                           * so we just show the packages that belong to it.
                                           */
                                          show_set(&pkg->depends,
                                                _("This is a task containing the following installed packages:"));
                                    }
                                                i = -1;
                              break;
                              case 2: /* p */
                                    for(c = related->head; c; c = c->next)
                                          AVLInsert(nokeepers, c->item);
                              case 1: /* n */
                                    AVLInsert(nokeepers, pkg->name);
                                    AVLInsert(removes, pkg->name);
                                    remove_provider(pkg, pkg);
                              break;
                              case 0: /* y */
                                    AVLInsert(keepers, pkg->name);
                                    add_keeplist(pkg);
                              break;
                              case 3: /* s */
                                    AVLInsert(postpone, pkg->name);
                                    add_keeplist(pkg);
                              break;
                              case 6: /* u */
                                    if(!undo_stack) {
                                          printf(_("\aNothing to undo.\n"));
                                          i = -1;
                                          break;
                                    } 
                                    while((undo_entry = pop_undo())
                                    && undo_entry != symbol("MARK")) {
                                          AVLDelete(keepers, undo_entry);
                                          AVLDelete(nokeepers, undo_entry);
                                          AVLDelete(removes, undo_entry);
                                          AVLDelete(postpone, undo_entry);
                                    }
                                    AVLFreeNodes(orphans);
                              break;
                              case 7: /* q */
                                    closekeepers();
                                    return 0;
                              case 8: /* x */
                                    AVLFreeNodes(orphans);
                              break;
                        }
                  } while(i < 0);

                  if(i < 4) { /* Yes, No, Purge or Skip */
                        push_undo(symbol("MARK"));
                        push_undo(pkg->name);
                        if(i == 2) { /* Purge */
                              for(c = related->head; c; c = c->next)
                                    push_undo(c->item);
                        }
                  }
                  AVLFreeNodes(related);
            }

            if(show_option == 's') { /* --show-orphans option */
                  if(!show_set(postpone, _("The following packages have been orphaned:")))
                        printf(_("There are no orphaned packages.\n"));
                  return 0;
            }

            /* Undo? Start over. */
            if(i == 6) { /* Undo */
                  init_packages(ignore_defaults);
                  build_keeplist();
                  continue;
            }

            /* Save keeper file */
            writekeepers();

            if(!mark_only) {
                  for(c = removes->head; c; c = p) {
                        p = c->next;
                        pkg = pkg_find(c->item);
                        if (!pkg || !pkg->installed) {
                              if(!pkg || !pkg->task)
                                    Vprintf(_("Package is not installed: %s\n"), (symbol_t)c->item);
                              AVLDeleteNode(removes, c);
                        } else if(AVLSearch(keeplist, pkg->name)) {
                              Vprintf(_("Package is a dependency: %s\n"), pkg->name);
                              AVLDeleteNode(removes, c);
                        } else
                              Vprintf(_("Removing package: %s\n"), pkg->name);
                  }
                  /* Remove packages */
                  if(removes->head) {
                        i = xargs(removes, RemoveCmd);
                        if(i) return i;
                        AVLFreeNodes(removes);
                  }
            }

            break; /* done! */
      }

      closekeepers();

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index