Allow alternates to be listed for packages
[mspang/inapt.git] / inapt.cc
index 92e9c15..f2edb46 100644 (file)
--- a/inapt.cc
+++ b/inapt.cc
@@ -1,8 +1,10 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <getopt.h>
 #include <iostream>
 #include <cstdio>
 #include <fstream>
+#include <set>
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/cachefile.h>
 #include <apt-pkg/dpkgdb.h>
 
 using namespace std;
 
-bool InstallPackages(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
+char *prog = NULL;
+
+static struct option opts[] = {
+    { NULL, 0, NULL, '\0' },
+};
+
+bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
                      bool Safety = true)
 {
-   if (_config->FindB("APT::Get::Purge",false) == 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);
+            Cache->MarkDelete(I, true);
       }
    }
 
    if (Cache->BrokenCount() != 0)
    {
-      return _error->Error("Internal error, InstallPackages was called with broken packages!");
+      return _error->Error("Internal error, run_install was called with broken packages!");
    }
 
    if (Cache->DelCount() == 0 && Cache->InstCount() == 0 &&
@@ -170,9 +178,96 @@ bool InstallPackages(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
    }
 }
 
-int main(int argc, char *argv[]) {
+static void usage() {
+    fprintf(stderr, "Usage: %s [filename]\n", prog);
+    exit(2);
+}
+
+static bool test_macro(const char *macro, set<string> *defines) {
+    return (*macro != '!' && defines->find(macro) != defines->end())
+            || (*macro == '!' && defines->find(macro + 1) == defines->end());
+}
+
+static pkgCache::PkgIterator eval_pkg(inapt_action *action, pkgCacheFile &cachef) {
+    pkgCache::PkgIterator pkg;
+
+    pkgCache *cache = cachef;
+
+    if (!pkg.end()) fatal("omg"); /* TODO */
+
+    for (std::vector<std::string>::iterator i = action->alternates.begin(); i != action->alternates.end(); i++) {
+        pkg = cache->FindPkg(*i);
+
+        /* no such package */
+        if (pkg.end())
+            continue;
 
-    vector<inapt_action> actions;
+        /* real package */
+        if (cachef[pkg].CandidateVer)
+            break;
+
+        /* virtual package */
+        if (pkg->ProvidesList) {
+            if (!pkg.ProvidesList()->NextProvides) {
+                pkgCache::PkgIterator tmp = pkg.ProvidesList().OwnerPkg();
+                if (action->action == inapt_action::INSTALL) {
+                    debug("selecting %s instead of %s", tmp.Name(), pkg.Name());
+                    pkg = tmp;
+                    break;
+                } else {
+                    debug("will not remove %s instead of virtual package %s", tmp.Name(), pkg.Name());
+                }
+            } else {
+                debug("%s is a virtual package", pkg.Name());
+            }
+        } else {
+            debug("%s is a virtual packages with no provides", pkg.Name());
+        }
+    }
+
+    if (pkg.end()) {
+        /* todo: report all errors at the end */
+        if (action->alternates.size() == 1) {
+            fatal("%s:%d: No such package: %s", action->filename, action->linenum, action->alternates[0].c_str());
+        } else {
+            std::vector<std::string>::iterator i = action->alternates.begin();
+            std::string message = *(i++);
+            while (i != action->alternates.end()) {
+                message.append(", ");
+                message.append(*(i++));
+            }
+            fatal("%s:%d: No alternative available: %s", action->filename, action->linenum, message.c_str());
+        }
+    }
+
+    return pkg;
+}
+
+static void eval_block(inapt_block *block, set<string> *defines, std::vector<inapt_action *> *final_actions) {
+    if (!block)
+        return;
+
+    for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++) {
+        bool ok = true;
+        for (vector<const char *>::iterator j = (*i)->predicates.begin(); j < (*i)->predicates.end(); j++) {
+            if (!test_macro(*j, defines)) {
+                ok = false;
+                break;
+            }
+        }
+        if (ok)
+            final_actions->push_back(*i);
+    }
+
+    for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
+        if (test_macro((*i)->condition, defines))
+            eval_block((*i)->then_block, defines, final_actions);
+        else
+            eval_block((*i)->else_block, defines, final_actions);
+    }
+}
+
+static void exec_actions(std::vector<inapt_action *> *final_actions) {
 
     pkgInitConfig(*_config);
     pkgInitSystem(*_config, _system);
@@ -190,22 +285,17 @@ int main(int argc, char *argv[]) {
     pkgCache *cache = cachef;
     pkgDepCache *DCache = cachef;
 
-    scanner(&actions);
+    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
+        (*i)->pkg = eval_pkg(*i, cachef);
 
-    for (vector<inapt_action>::iterator i = actions.begin(); i < actions.end(); i++) {
-        debug("finding %s", i->package);
-        pkgCache::PkgIterator pkg = cache->FindPkg(i->package);
-        if (pkg.end())
-            fatal("%s:%d: No such package: %s", i->filename, i->linenum, i->package);
-        i->obj = &pkg;
-    }
-
-    for (vector<inapt_action>::iterator i = actions.begin(); i < actions.end(); i++) {
-        pkgCache::PkgIterator j = *(pkgCache::PkgIterator *)i->obj;
-        switch (i->action) {
+    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
+        pkgCache::PkgIterator j = (*i)->pkg;
+        switch ((*i)->action) {
             case inapt_action::INSTALL:
-                printf("preinstall %s %s:%d\n", i->package, i->filename, i->linenum);
-                DCache->MarkInstall(j, true);
+                if (!j.CurrentVer() || cachef[j].Delete()) {
+                    printf("preinstall %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    DCache->MarkInstall(j, true);
+                }
                 break;
             case inapt_action::REMOVE:
                 break;
@@ -214,16 +304,20 @@ int main(int argc, char *argv[]) {
         }
     }
 
-    for (vector<inapt_action>::iterator i = actions.begin(); i < actions.end(); i++) {
-        pkgCache::PkgIterator j = *(pkgCache::PkgIterator *)i->obj;
-        switch (i->action) {
+    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
+        pkgCache::PkgIterator j = (*i)->pkg;
+        switch ((*i)->action) {
             case inapt_action::INSTALL:
-                printf("install %s %s:%d\n", i->package, i->filename, i->linenum);
-                DCache->MarkInstall(j, false);
+                if ((!j.CurrentVer() && !cachef[j].Install()) || cachef[j].Delete()) {
+                    printf("install %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    DCache->MarkInstall(j, false);
+                }
                 break;
             case inapt_action::REMOVE:
-                printf("remove %s %s:%d\n", i->package, i->filename, i->linenum);
-                DCache->MarkDelete(j, false);
+                if ((j.CurrentVer() && !cachef[j].Delete()) || cachef[j].Install()) {
+                    printf("remove %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    DCache->MarkDelete(j, false);
+                }
                 break;
             default:
                 fatal("uninitialized action");
@@ -257,10 +351,8 @@ int main(int argc, char *argv[]) {
 
     pkgProblemResolver fix (DCache);
 
-    for (vector<inapt_action>::iterator i = actions.begin(); i < actions.end(); i++)
-           fix.Protect(cache->FindPkg(i->package));
-    for (vector<inapt_action>::iterator i = actions.begin(); i < actions.end(); i++)
-           fix.Protect(cache->FindPkg(i->package));
+    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
+           fix.Protect((*i)->pkg);
     fix.Resolve();
 
     fprintf(stderr, "\n");
@@ -280,5 +372,43 @@ int main(int argc, char *argv[]) {
 
     fprintf(stderr, "\n");
 
-    InstallPackages(cachef);
+    run_install(cachef);
+}
+
+int main(int argc, char *argv[]) {
+    int opt;
+    char *filename = NULL;
+
+    set<string> defines;
+
+    prog = xstrdup(basename(argv[0]));
+    while ((opt = getopt_long(argc, argv, "D:", opts, NULL)) != -1) {
+        switch (opt) {
+            case 'D':
+                defines.insert(optarg);
+                break;
+            case '?':
+                usage();
+                break;
+            default:
+                fatal("error parsing arguments");
+        }
+    }
+
+    if (argc - optind == 1)
+        filename = argv[optind++];
+    else if (argc - optind > 0)
+        usage();
+
+    fprintf(stderr, "defines: ");
+    for (set<string>::iterator i = defines.begin(); i != defines.end(); i++)
+        fprintf(stderr, "%s  ", i->c_str());
+    fprintf(stderr, "\n");
+
+    inapt_block context;
+    vector<inapt_action *> final_actions;
+
+    parser(filename, &context);
+    eval_block(&context, &defines, &final_actions);
+    exec_actions(&final_actions);
 }