Mark installed packages as not automatically installed
[mspang/inapt.git] / inapt.cc
index f968d2e..29bc06f 100644 (file)
--- a/inapt.cc
+++ b/inapt.cc
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include <sys/utsname.h>
 #include <iostream>
 #include <cstdio>
 #include <fstream>
@@ -27,6 +28,21 @@ static struct option opts[] = {
     { NULL, 0, NULL, '\0' },
 };
 
+class AwesomeRootSetFunc : public pkgDepCache::InRootSetFunc {
+    std::set<std::string> root;
+
+    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) {
+//            debug("irs %s %d", pkg.Name(), root.find(pkg.Name()) != root.end());
+            return root.find(pkg.Name()) != root.end();
+        }
+};
+
 bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
                      bool Safety = true)
 {
@@ -179,7 +195,7 @@ bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
 }
 
 static void usage() {
-    fprintf(stderr, "Usage: %s [filename]\n", prog);
+    fprintf(stderr, "Usage: %s [filename..]\n", prog);
     exit(2);
 }
 
@@ -188,21 +204,86 @@ static bool test_macro(const char *macro, set<string> *defines) {
             || (*macro == '!' && defines->find(macro + 1) == defines->end());
 }
 
-static void eval_block(inapt_block *block, set<string> *defines, std::vector<inapt_action *> *final_actions) {
-    if (!block)
-        return;
+static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cachef) {
+    pkgCache::PkgIterator pkg;
 
-    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;
+    pkgCache *cache = cachef;
+
+    if (!pkg.end()) fatal("omg"); /* TODO */
+
+    for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
+        pkg = cache->FindPkg(*i);
+
+        /* no such package */
+        if (pkg.end())
+            continue;
+
+        /* real package */
+        if (cachef[pkg].CandidateVer)
+            break;
+
+        /* virtual package */
+        if (pkg->ProvidesList) {
+            if (!pkg.ProvidesList()->NextProvides) {
+                pkgCache::PkgIterator tmp = pkg.ProvidesList().OwnerPkg();
+                if (package->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 (package->alternates.size() == 1) {
+            fatal("%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++));
             }
+            fatal("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
+        }
+    }
+
+    return pkg;
+}
+
+static bool test_macros(vector<std::string> *macros, set<string> *defines) {
+    bool ok = true;
+    for (vector<std::string>::iterator j = macros->begin(); j < macros->end(); j++) {
+        if (!test_macro((*j).c_str(), defines)) {
+            ok = false;
+            break;
         }
-        if (ok)
+    }
+    return ok;
+}
+
+static void eval_action(inapt_action *action, set<string> *defines, 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))
             final_actions->push_back(*i);
     }
+}
+
+static void eval_block(inapt_block *block, set<string> *defines, vector<inapt_package *> *final_actions) {
+    if (!block)
+        return;
+
+    for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
+        if (test_macros(&(*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))
@@ -212,12 +293,31 @@ static void eval_block(inapt_block *block, set<string> *defines, std::vector<ina
     }
 }
 
-static void exec_actions(std::vector<inapt_action *> *final_actions) {
+static void eval_profiles(inapt_block *block, set<string> *defines) {
+    if (!block)
+        return;
+
+    for (vector<inapt_profiles *>::iterator i = block->profiles.begin(); i < block->profiles.end(); i++)
+        if (test_macros(&(*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))
+            eval_profiles((*i)->then_block, defines);
+        else
+            eval_profiles((*i)->else_block, defines);
+    }
+}
+
+static void exec_actions(std::vector<inapt_package *> *final_actions) {
+    int marked = 0;
 
     pkgInitConfig(*_config);
     pkgInitSystem(*_config, _system);
 
      _config->Set("Debug::pkgProblemResolver", true);
+//     _config->Set("Debug::pkgAutoRemove", true);
 
     OpTextProgress prog;
     pkgCacheFile cachef;
@@ -230,37 +330,18 @@ static void exec_actions(std::vector<inapt_action *> *final_actions) {
     pkgCache *cache = cachef;
     pkgDepCache *DCache = cachef;
 
-    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
-        pkgCache::PkgIterator pkg = cache->FindPkg((*i)->package);
-        if (pkg.end())
-            fatal("%s:%d: No such package: %s", (*i)->filename, (*i)->linenum, (*i)->package);
-        (*i)->pkg = pkg;
-        if (!cachef[pkg].CandidateVer) {
-            if (pkg->ProvidesList) {
-                if (!pkg.ProvidesList()->NextProvides) {
-                    pkgCache::PkgIterator tmp = pkg.ProvidesList().OwnerPkg();
-                    if ((*i)->action == inapt_action::INSTALL) {
-                        debug("selecting %s instead of %s", tmp.Name(), pkg.Name());
-                        (*i)->pkg = tmp;
-                    } else {
-                        debug("will not remove %s instead of virtual package %s", tmp.Name(), pkg.Name());
-                    }
-                } else {
-                    fatal("%s is a virtual package", pkg.Name());
-                }
-            } else {
-                fatal("%s is a virtual packages with no provides", pkg.Name());
-            }
-        }
-    }
+    pkgDepCache::ActionGroup group (*cachef);
+
+    for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
+        (*i)->pkg = eval_pkg(*i, cachef);
 
-    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
-        pkgCache::PkgIterator j = (*i)->pkg;
+    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 (!j.CurrentVer() || cachef[j].Delete()) {
-                    printf("preinstall %s %s:%d\n", (*i)->package, (*i)->filename, (*i)->linenum);
-                    DCache->MarkInstall(j, true);
+                if (!k.CurrentVer() || cachef[k].Delete()) {
+                    printf("preinstall %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    DCache->MarkInstall(k, true);
                 }
                 break;
             case inapt_action::REMOVE:
@@ -270,19 +351,24 @@ static void exec_actions(std::vector<inapt_action *> *final_actions) {
         }
     }
 
-    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
-        pkgCache::PkgIterator j = (*i)->pkg;
+    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 ((!j.CurrentVer() && !cachef[j].Install()) || cachef[j].Delete()) {
-                    printf("install %s %s:%d\n", (*i)->package, (*i)->filename, (*i)->linenum);
-                    DCache->MarkInstall(j, false);
+                if ((!k.CurrentVer() && !cachef[k].Install()) || cachef[k].Delete()) {
+                    printf("install %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    DCache->MarkInstall(k, false);
+                }
+                if (cachef[k].Flags & pkgCache::Flag::Auto) {
+                    marked++;
+                    debug("marking %s as manually installed", (*i)->pkg.Name());
+                    cachef->MarkAuto(k, false);
                 }
                 break;
             case inapt_action::REMOVE:
-                if ((j.CurrentVer() && !cachef[j].Delete()) || cachef[j].Install()) {
-                    printf("remove %s %s:%d\n", (*i)->package, (*i)->filename, (*i)->linenum);
-                    DCache->MarkDelete(j, false);
+                if ((k.CurrentVer() && !cachef[k].Delete()) || cachef[k].Install()) {
+                    printf("remove %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
+                    DCache->MarkDelete(k, false);
                 }
                 break;
             default:
@@ -317,10 +403,8 @@ static void exec_actions(std::vector<inapt_action *> *final_actions) {
 
     pkgProblemResolver fix (DCache);
 
-    for (vector<inapt_action *>::iterator i = final_actions->begin(); i < final_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(cache->FindPkg((*i)->package));
+    for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
+        fix.Protect((*i)->pkg);
     fix.Resolve();
 
     fprintf(stderr, "\n");
@@ -341,23 +425,53 @@ static void exec_actions(std::vector<inapt_action *> *final_actions) {
     fprintf(stderr, "\n");
 
     run_install(cachef);
+
+    if (marked) {
+        debug("marked %d packages, writing state file", marked);
+        cachef->writeStateFile(NULL);
+    }
+
+    for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
+        cachef->MarkAuto(i, true);
+
+    AwesomeRootSetFunc root (final_actions);
+
+    DCache->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", DCache->GetCandidateVer(i).VerStr());
+       }
+    }
+}
+
+static void debug_profiles(std::set<std::string> *defines) {
+    fprintf(stderr, "defines: ");
+    for (set<string>::iterator i = defines->begin(); i != defines->end(); i++)
+        fprintf(stderr, "%s ", i->c_str());
+    fprintf(stderr, "\n");
+}
+
+static void auto_profiles(std::set<std::string> *defines) {
+    struct utsname uts;
+    if (uname(&uts))
+        fatalpe("uname");
+    defines->insert(uts.nodename);
 }
 
 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:U:", opts, NULL)) != -1) {
+    while ((opt = getopt_long(argc, argv, "p:", opts, NULL)) != -1) {
         switch (opt) {
-            case 'D':
+            case 'p':
                 defines.insert(optarg);
                 break;
-            case 'U':
-                defines.erase(optarg);
-                break;
             case '?':
                 usage();
                 break;
@@ -366,20 +480,17 @@ int main(int argc, char *argv[]) {
         }
     }
 
-    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");
+    int num_files = argc - optind;
 
     inapt_block context;
-    vector<inapt_action *> final_actions;
+    vector<inapt_package *> final_actions;
+
+    while (num_files--)
+        parser(argv[optind++], &context);
 
-    parser(filename, &context);
+    auto_profiles(&defines);
+    eval_profiles(&context, &defines);
+    debug_profiles(&defines);
     eval_block(&context, &defines, &final_actions);
     exec_actions(&final_actions);
 }