Remove cruft from Makefile
[mspang/inapt.git] / inapt.cc
index a755e2d..6e235de 100644 (file)
--- a/inapt.cc
+++ b/inapt.cc
 
 #include "inapt.h"
 #include "util.h"
-#include "acqprogress.h"
+#include "contrib/acqprogress.h"
 
 char *prog = NULL;
 
 static struct option opts[] = {
-    { "auto-remove", 0, NULL, 'z' },
     { "simulate", 0, NULL, 's' },
     { "purge", 0, NULL, 'u' },
     { NULL, 0, NULL, '\0' },
 };
 
-class AwesomeRootSetFunc : public pkgDepCache::InRootSetFunc {
-    std::set<std::string> root;
+static bool run_install(pkgCacheFile &cache) {
+   if (_config->FindB("Inapt::Purge", false))
+      for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
+         if (!i.Purge() && cache[i].Mode == pkgDepCache::ModeDelete)
+            cache->MarkDelete(i, true);
 
-    public:
-        AwesomeRootSetFunc(std::vector<inapt_package *> *final_actions) {
-            for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
-                if ((*i)->action == inapt_action::INSTALL)
-                    root.insert((*i)->pkg.Name());
-        }
-        bool InRootSet(const pkgCache::PkgIterator &pkg) {
-            return root.find(pkg.Name()) != root.end();
-        }
-};
-
-bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
-                     bool Safety = true)
-{
-   if (_config->FindB("APT::Get::Purge", false) == true)
-   {
-      pkgCache::PkgIterator I = Cache->PkgBegin();
-      for (; I.end() == false; I++)
-      {
-         if (I.Purge() == false && Cache[I].Mode == pkgDepCache::ModeDelete)
-            Cache->MarkDelete(I, true);
-      }
-   }
-
-   if (Cache->BrokenCount())
+   if (cache->BrokenCount())
        fatal("broken packages during install");
 
-   if (!Cache->DelCount() && !Cache->InstCount() && !Cache->BadCount())
+   if (!cache->DelCount() && !cache->InstCount() && !cache->BadCount())
       return true;
 
-   pkgRecords Recs(Cache);
-
-   if (_error->PendingError() == true)
+   pkgRecords Recs (cache);
+   if (_error->PendingError())
       return false;
 
    FileFd Lock;
    Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
-   if (_error->PendingError() == true)
-       return _error->Error(("Unable to lock the download directory"));
+   if (_error->PendingError())
+       return _error->Error("Unable to lock the download directory");
 
    unsigned int width = 80;
    AcqTextStatus status (width, 0);
@@ -78,24 +55,20 @@ bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
 
    pkgSourceList List;
    if (List.ReadMainList() == false)
-      return _error->Error(("The list of sources could not be read."));
+      return _error->Error("The list of sources could not be read");
 
-   SPtr<pkgPackageManager> PM= _system->CreatePM(Cache);
+   SPtr<pkgPackageManager> PM = _system->CreatePM(cache);
    if (PM->GetArchives(&Fetcher, &List, &Recs) == false ||
-       _error->PendingError() == true)
+       _error->PendingError())
       return false;
 
   if (Fetcher.Run() == pkgAcquire::Failed)
      return false;
 
   bool Failed = false;
-  for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I != Fetcher.ItemsEnd(); I++)
-  {
-     if ((*I)->Status != pkgAcquire::Item::StatDone || (*I)->Complete != true) {
-         fprintf(stderr,("Failed to fetch %s  %s\n"),(*I)->DescURI().c_str(),
-                 (*I)->ErrorText.c_str());
+  for (pkgAcquire::ItemIterator i = Fetcher.ItemsBegin(); i != Fetcher.ItemsEnd(); i++) {
+     if ((*i)->Status != pkgAcquire::Item::StatDone || (*i)->Complete != true)
          Failed = true;
-     }
   }
 
   if (Failed)
@@ -110,88 +83,105 @@ bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
   return false;
 }
 
-void run_autoremove(pkgCacheFile &cache)
-{
+static void run_autoremove(pkgCacheFile &cache) {
+    bool purge = _config->FindB("Inapt::Purge", false);
+
     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
         if (cache[i].Garbage) {
-            debug("garbage: %s", i.Name());
-            cache->MarkDelete(i, 0);
+            debug("autoremove: %s", i.Name());
+            cache->MarkDelete(i, purge);
         }
     }
 
     if (cache->BrokenCount())
-        fatal("ogawd");
+        fatal("automatic removal broke packages");
 }
 
 static void usage() {
-    fprintf(stderr, "Usage: %s [filename..]\n", prog);
+    fprintf(stderr, "Usage: %s [options] [filename..]\n", prog);
     exit(2);
 }
 
-static bool test_macro(const char *macro, std::set<std::string> *defines) {
-    return (*macro != '!' && defines->find(macro) != defines->end())
-            || (*macro == '!' && defines->find(macro + 1) == defines->end());
+static bool test_profile(const char *profile, std::set<std::string> *defines) {
+    return (*profile != '!' && defines->find(profile) != defines->end())
+            || (*profile == '!' && defines->find(profile + 1) == defines->end());
 }
 
-static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cachef) {
-    pkgCache::PkgIterator pkg;
+static bool test_anyprofile(std::string &profile, std::set<std::string> *defines) {
+    char *s = xstrdup(profile.c_str());
+    const char *c = strtok(s, "/");
 
-    pkgCache *cache = cachef;
+    if (test_profile(c, defines)) {
+        free(s);
+        return true;
+    }
 
-    if (!pkg.end()) fatal("omg"); /* TODO */
+    while ((c = strtok(NULL, "/")) != NULL) {
+        if (test_profile(c, defines)) {
+            free(s);
+            return true;
+        }
+    }
+
+    free(s);
+    return false;
+}
+
+static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cache) {
+    pkgCache::PkgIterator pkg;
 
     for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
-        pkg = cache->FindPkg(*i);
+        pkgCache::PkgIterator tmp = cache->FindPkg(*i);
 
         /* no such package */
-        if (pkg.end())
+        if (tmp.end())
             continue;
 
         /* real package */
-        if (cachef[pkg].CandidateVer)
+        if (cache[tmp].CandidateVer) {
+            pkg = tmp;
             break;
+        }
 
         /* virtual package */
-        if (pkg->ProvidesList) {
-            if (!pkg.ProvidesList()->NextProvides) {
-                pkgCache::PkgIterator tmp = pkg.ProvidesList().OwnerPkg();
+        if (tmp->ProvidesList) {
+            if (!tmp.ProvidesList()->NextProvides) {
+                pkgCache::PkgIterator provide = tmp.ProvidesList().OwnerPkg();
                 if (package->action == inapt_action::INSTALL) {
-                    debug("selecting %s instead of %s", tmp.Name(), pkg.Name());
-                    pkg = tmp;
+                    debug("selecting %s instead of %s", provide.Name(), tmp.Name());
+                    pkg = provide;
                     break;
                 } else {
-                    debug("will not remove %s instead of virtual package %s", tmp.Name(), pkg.Name());
+                    debug("will not remove %s instead of virtual package %s", provide.Name(), tmp.Name());
                 }
             } else {
-                debug("%s is a virtual package", pkg.Name());
+                debug("%s is a virtual package", tmp.Name());
             }
         } else {
-            debug("%s is a virtual packages with no provides", pkg.Name());
+            debug("%s is a virtual packages with no provides", tmp.Name());
         }
     }
 
     if (pkg.end()) {
-        /* todo: report all errors at the end */
         if (package->alternates.size() == 1) {
-            fatal("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
+            _error->Error("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
         } else {
             std::vector<std::string>::iterator i = package->alternates.begin();
             std::string message = *(i++);
             while (i != package->alternates.end()) {
-                message.append(", ");
-                message.append(*(i++));
+                message.append(", ").append(*(i++));
             }
-            fatal("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
+            _error->Error("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
         }
     }
 
     return pkg;
 }
 
-static bool test_macros(vector<std::string> *macros, std::set<std::string> *defines) {
+static bool test_profiles(vector<std::string> *profiles, std::set<std::string> *defines) {
     bool ok = true;
-    for (vector<std::string>::iterator j = macros->begin(); j < macros->end(); j++) {
-        if (!test_macro((*j).c_str(), defines)) {
+    for (vector<std::string>::iterator j = profiles->begin(); j < profiles->end(); j++) {
+        if (!test_anyprofile(*j, defines)) {
             ok = false;
             break;
         }
@@ -201,7 +191,7 @@ static bool test_macros(vector<std::string> *macros, std::set<std::string> *defi
 
 static void eval_action(inapt_action *action, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
     for (vector<inapt_package *>::iterator i = action->packages.begin(); i < action->packages.end(); i++) {
-        if (test_macros(&(*i)->predicates, defines))
+        if (test_profiles(&(*i)->predicates, defines))
             final_actions->push_back(*i);
     }
 }
@@ -211,11 +201,11 @@ static void eval_block(inapt_block *block, std::set<std::string> *defines, std::
         return;
 
     for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
-        if (test_macros(&(*i)->predicates, defines))
+        if (test_profiles(&(*i)->predicates, defines))
             eval_action(*i, defines, final_actions);
 
     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
-        if (test_macro((*i)->condition, defines))
+        if (test_profiles(&(*i)->predicates, defines))
             eval_block((*i)->then_block, defines, final_actions);
         else
             eval_block((*i)->else_block, defines, final_actions);
@@ -227,12 +217,12 @@ static void eval_profiles(inapt_block *block, std::set<std::string> *defines) {
         return;
 
     for (vector<inapt_profiles *>::iterator i = block->profiles.begin(); i < block->profiles.end(); i++)
-        if (test_macros(&(*i)->predicates, defines))
+        if (test_profiles(&(*i)->predicates, defines))
             for (vector<std::string>::iterator j = (*i)->profiles.begin(); j != (*i)->profiles.end(); j++)
                 defines->insert(*j);
 
     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
-        if (test_macro((*i)->condition, defines))
+        if (test_profiles(&(*i)->predicates, defines))
             eval_profiles((*i)->then_block, defines);
         else
             eval_profiles((*i)->else_block, defines);
@@ -261,35 +251,70 @@ static void dump_actions(pkgCacheFile &cache) {
     }
 }
 
+static bool sanity_check(std::vector<inapt_package *> *final_actions, pkgCacheFile &cache) {
+    bool okay = true;
+    std::map<std::string, inapt_package *> packages;
+
+    for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++) {
+        if (packages.find((*i)->pkg.Name()) != packages.end()) {
+            inapt_package *first = packages[(*i)->pkg.Name()];
+            inapt_package *current = *i;
+            _error->Error("Multiple directives for package %s at %s:%d and %s:%d",
+                    (*i)->pkg.Name(), first->filename, first->linenum, current->filename, current->linenum);
+            okay = false;
+            continue;
+        }
+        packages[(*i)->pkg.Name()] = *i;
+    }
+
+    for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
+        if (cache[i].Delete() && (i->Flags & pkgCache::Flag::Essential || i->Flags & pkgCache::Flag::Important)) {
+            _error->Error("Removing essential package %s", i.Name());
+            okay = false;
+        }
+    }
+
+    return okay;
+}
+
+static void show_breakage(pkgCacheFile &cache) {
+    std::string broken;
+    for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
+        if (cache[i].NowBroken() || cache[i].InstBroken())
+            broken.append(" ").append(i.Name());
+
+    _error->Error("Broken packages:%s", broken.c_str());
+}
+
 static void exec_actions(std::vector<inapt_package *> *final_actions) {
     int marked = 0;
+    bool purge = _config->FindB("Inapt::Purge", false);
 
     pkgInitConfig(*_config);
     pkgInitSystem(*_config, _system);
 
-//     _config->Set("Debug::pkgProblemResolver", true);
-//     _config->Set("Debug::pkgAutoRemove", true);
-
     OpTextProgress prog;
-    pkgCacheFile cachef;
+    pkgCacheFile cache;
 
-    if (cachef.Open(prog) == false) {
-       _error->DumpErrors();
-        exit(1);
-    }
+    if (cache.Open(prog) == false)
+        return;
 
-    pkgDepCache::ActionGroup group (*cachef);
+    pkgDepCache::ActionGroup group (cache);
 
     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
-        (*i)->pkg = eval_pkg(*i, cachef);
+        (*i)->pkg = eval_pkg(*i, cache);
 
+    if (_error->PendingError())
+        return;
+
+    // preliminary loop (auto-installs, includes recommends - could do this manually)
     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
         pkgCache::PkgIterator k = (*i)->pkg;
         switch ((*i)->action) {
             case inapt_action::INSTALL:
-                if (!k.CurrentVer() || cachef[k].Delete()) {
+                if (!k.CurrentVer() || cache[k].Delete()) {
                     debug("install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
-                    cachef->MarkInstall(k, true);
+                    cache->MarkInstall(k, true);
                 }
                 break;
             case inapt_action::REMOVE:
@@ -299,81 +324,86 @@ static void exec_actions(std::vector<inapt_package *> *final_actions) {
         }
     }
 
+    // secondary loop (removes package and reinstalls auto-removed packages)
     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
         pkgCache::PkgIterator k = (*i)->pkg;
         switch ((*i)->action) {
             case inapt_action::INSTALL:
-                if ((!k.CurrentVer() && !cachef[k].Install()) || cachef[k].Delete()) {
-                    debug("install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
-                    cachef->MarkInstall(k, false);
+                if ((!k.CurrentVer() && !cache[k].Install()) || cache[k].Delete()) {
+                    debug("force install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    cache->MarkInstall(k, false);
                 }
-                if (cachef[k].Flags & pkgCache::Flag::Auto) {
-                    marked++;
+                if (cache[k].Flags & pkgCache::Flag::Auto) {
                     debug("marking %s as manually installed", (*i)->pkg.Name());
-                    cachef->MarkAuto(k, false);
+                    cache->MarkAuto(k, false);
+                    marked++;
                 }
                 break;
             case inapt_action::REMOVE:
-                if ((k.CurrentVer() && !cachef[k].Delete()) || cachef[k].Install()) {
+                if ((k.CurrentVer() && !cache[k].Delete()) || cache[k].Install())
                     debug("remove %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
-                    cachef->MarkDelete(k, false);
-                }
+
+                /* always mark so purge works */
+                cache->MarkDelete(k, purge);
                 break;
             default:
                 fatal("uninitialized action");
         }
     }
 
-    dump_nondownloadable(cachef);
-    dump_actions(cachef);
+    if (_error->PendingError())
+        return;
+
+    dump_nondownloadable(cache);
+    dump_actions(cache);
 
-    if (cachef->BrokenCount()) {
-        pkgProblemResolver fix (cachef);
+    if (cache->BrokenCount()) {
+        pkgProblemResolver fix (cache);
         for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
             fix.Protect((*i)->pkg);
         fix.Resolve();
 
-        dump_actions(cachef);
+        if (cache->BrokenCount()) {
+            show_breakage(cache);
+            return;
+        }
     }
 
-    if (_config->FindB("Inapt::AutomaticRemove", false)) {
-        cachef->MarkAndSweep();
-        run_autoremove(cachef);
-    }
+    cache->MarkAndSweep();
+    run_autoremove(cache);
 
-    if (!run_install(cachef)) {
-       _error->DumpErrors();
-        fatal("errors");
-    }
+    if (!sanity_check(final_actions, cache))
+        return;
 
-    if (marked) {
-        debug("marked %d packages, writing state file", marked);
-        cachef->writeStateFile(NULL);
+    if (_config->FindB("Inapt::Simulate", false)) {
+        pkgSimulate PM (cache);
+        PM.DoInstall(-1);
+        return;
     }
 
-    /*
-    for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
-        cachef->MarkAuto(i, true);
-
-    AwesomeRootSetFunc root (final_actions);
+    run_install(cache);
+    if (_error->PendingError())
+        return;
 
-    cachef->MarkAndSweep(root);
-    fprintf(stderr, "\n");
-    fprintf(stderr, "garbage packages:\n");
-    for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
-       if (i.CurrentVer() && cachef[i].Garbage) {
-              fprintf(stderr, "%s ", i.Name());
-              fprintf(stderr, "%s\n", cachef->GetCandidateVer(i).VerStr());
-       }
+    if (marked) {
+        if (_config->FindB("Inapt::Simulate", false)) {
+            debug("marked %d packages", marked);
+        } else {
+            debug("marked %d packages, writing state file", marked);
+            cache->writeStateFile(NULL);
+        }
     }
-    */
 }
 
 static void debug_profiles(std::set<std::string> *defines) {
-    fprintf(stderr, "debug: defines: ");
-    for (std::set<std::string>::iterator i = defines->begin(); i != defines->end(); i++)
-        fprintf(stderr, "%s ", i->c_str());
-    fprintf(stderr, "\n");
+    std::string profiles = "profiles:";
+
+    for (std::set<std::string>::iterator i = defines->begin(); i != defines->end(); i++) {
+        profiles.append(" ");
+        profiles.append(*i);
+    }
+
+    debug("%s", profiles.c_str());
 }
 
 static void auto_profiles(std::set<std::string> *defines) {
@@ -383,13 +413,24 @@ static void auto_profiles(std::set<std::string> *defines) {
     defines->insert(uts.nodename);
 }
 
+static void set_option(char *opt) {
+    char *eq = strchr(opt, '=');
+    if (!eq)
+        fatal("invalid syntax for '%s': must be <option>=<value>", opt);
+
+    std::string option (opt, eq - opt);
+    std::string value (eq + 1);
+
+    _config->Set(option, value);
+}
+
 int main(int argc, char *argv[]) {
     int opt;
 
     std::set<std::string> defines;
 
     prog = xstrdup(basename(argv[0]));
-    while ((opt = getopt_long(argc, argv, "p:", opts, NULL)) != -1) {
+    while ((opt = getopt_long(argc, argv, "p:o:sd", opts, NULL)) != -1) {
         switch (opt) {
             case 'p':
                 defines.insert(optarg);
@@ -397,15 +438,18 @@ int main(int argc, char *argv[]) {
             case '?':
                 usage();
                 break;
-            case 'z':
-                _config->Set("Inapt::AutomaticRemove", true);
-                break;
             case 's':
                 _config->Set("Inapt::Simulate", true);
                 break;
             case 'u':
                 _config->Set("Inapt::Purge", true);
                 break;
+            case 'd':
+                debug_level++;
+                break;
+            case 'o':
+                set_option(optarg);
+                break;
             default:
                 fatal("error parsing arguments");
         }
@@ -428,10 +472,10 @@ int main(int argc, char *argv[]) {
     eval_block(&context, &defines, &final_actions);
     exec_actions(&final_actions);
 
-    /* TODO: remove this */
     if (_error->PendingError()) {
-        warn("uncaught errors:");
        _error->DumpErrors();
         exit(1);
     }
+
+    return 0;
 }