about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/download-via-ssh/download-via-ssh.cc80
-rw-r--r--src/libexpr/common-opts.cc45
-rw-r--r--src/libexpr/common-opts.hh9
-rw-r--r--src/libmain/shared.cc82
-rw-r--r--src/libmain/shared.hh35
-rw-r--r--src/nix-daemon/nix-daemon.cc33
-rw-r--r--src/nix-env/nix-env.cc292
-rw-r--r--src/nix-hash/nix-hash.cc83
-rw-r--r--src/nix-instantiate/nix-instantiate.cc202
-rw-r--r--src/nix-store/nix-store.cc185
10 files changed, 509 insertions, 537 deletions
diff --git a/src/download-via-ssh/download-via-ssh.cc b/src/download-via-ssh/download-via-ssh.cc
index 688fb523a77a..ac0f5826dd28 100644
--- a/src/download-via-ssh/download-via-ssh.cc
+++ b/src/download-via-ssh/download-via-ssh.cc
@@ -91,55 +91,49 @@ static void query(std::pair<FdSink, FdSource> & pipes)
 }
 
 
-void run(Strings args)
+int main(int argc, char * * argv)
 {
-    if (args.empty())
-        throw UsageError("download-via-ssh requires an argument");
+    return handleExceptions(argv[0], [&]() {
+        initNix();
 
-    if (settings.sshSubstituterHosts.empty())
-        return;
+        if (argc < 2)
+            throw UsageError("download-via-ssh requires an argument");
 
-    std::cout << std::endl;
-
-    /* Pass on the location of the daemon client's SSH authentication
-       socket. */
-    string sshAuthSock = settings.get("ssh-auth-sock", "");
-    if (sshAuthSock != "") setenv("SSH_AUTH_SOCK", sshAuthSock.c_str(), 1);
+        if (settings.sshSubstituterHosts.empty())
+            return;
 
-    string host = settings.sshSubstituterHosts.front();
-    std::pair<FdSink, FdSource> pipes = connect(host);
-
-    /* Exchange the greeting */
-    writeInt(SERVE_MAGIC_1, pipes.first);
-    pipes.first.flush();
-    unsigned int magic = readInt(pipes.second);
-    if (magic != SERVE_MAGIC_2)
-        throw Error("protocol mismatch");
-    readInt(pipes.second); // Server version, unused for now
-    writeInt(SERVE_PROTOCOL_VERSION, pipes.first);
-    pipes.first.flush();
+        std::cout << std::endl;
 
-    Strings::iterator i = args.begin();
-    if (*i == "--query")
-        query(pipes);
-    else if (*i == "--substitute")
-        if (args.size() != 3)
-            throw UsageError("download-via-ssh: --substitute takes exactly two arguments");
-        else {
-            Path storePath = *++i;
-            Path destPath = *++i;
+        /* Pass on the location of the daemon client's SSH
+           authentication socket. */
+        string sshAuthSock = settings.get("ssh-auth-sock", "");
+        if (sshAuthSock != "") setenv("SSH_AUTH_SOCK", sshAuthSock.c_str(), 1);
+
+        string host = settings.sshSubstituterHosts.front();
+        std::pair<FdSink, FdSource> pipes = connect(host);
+
+        /* Exchange the greeting */
+        writeInt(SERVE_MAGIC_1, pipes.first);
+        pipes.first.flush();
+        unsigned int magic = readInt(pipes.second);
+        if (magic != SERVE_MAGIC_2)
+            throw Error("protocol mismatch");
+        readInt(pipes.second); // Server version, unused for now
+        writeInt(SERVE_PROTOCOL_VERSION, pipes.first);
+        pipes.first.flush();
+
+        string arg = argv[1];
+        if (arg == "--query")
+            query(pipes);
+        else if (arg == "--substitute") {
+            if (argc != 4)
+                throw UsageError("download-via-ssh: --substitute takes exactly two arguments");
+            Path storePath = argv[2];
+            Path destPath = argv[3];
             printMsg(lvlError, format("downloading `%1%' via SSH from `%2%'...") % storePath % host);
             substitute(pipes, storePath, destPath);
         }
-    else
-        throw UsageError(format("download-via-ssh: unknown command `%1%'") % *i);
-}
-
-
-void printHelp()
-{
-    std::cerr << "Usage: download-via-ssh --query|--substitute store-path dest-path" << std::endl;
+        else
+            throw UsageError(format("download-via-ssh: unknown command `%1%'") % arg);
+    });
 }
-
-
-string programId = "download-via-ssh";
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index a3ea202e889e..2e8bb29c7dc2 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -6,40 +6,45 @@
 namespace nix {
 
 
-bool parseOptionArg(const string & arg, Strings::iterator & i,
-    const Strings::iterator & argsEnd, EvalState & state,
-    Bindings & autoArgs)
+bool parseAutoArgs(Strings::iterator & i,
+    const Strings::iterator & argsEnd, std::map<string, string> & res)
 {
+    string arg = *i;
     if (arg != "--arg" && arg != "--argstr") return false;
 
     UsageError error(format("`%1%' requires two arguments") % arg);
 
-    if (i == argsEnd) throw error;
-    string name = *i++;
-    if (i == argsEnd) throw error;
-    string value = *i++;
+    if (++i == argsEnd) throw error;
+    string name = *i;
+    if (++i == argsEnd) throw error;
+    string value = *i;
 
-    /* !!! check for duplicates! */
-    Value * v = state.allocValue();
-    autoArgs.push_back(Attr(state.symbols.create(name), v));
+    res[name] = (arg == "--arg" ? 'E' : 'S') + value;
 
-    if (arg == "--arg")
-        state.mkThunk_(*v, state.parseExprFromString(value, absPath(".")));
-    else
-        mkString(*v, value);
+    return true;
+}
 
-    autoArgs.sort(); // !!! inefficient
 
-    return true;
+void evalAutoArgs(EvalState & state, std::map<string, string> & in, Bindings & out)
+{
+    for (auto & i: in) {
+        Value * v = state.allocValue();
+        if (i.second[0] == 'E')
+            state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
+        else
+            mkString(*v, string(i.second, 1));
+        out.push_back(Attr(state.symbols.create(i.first), v));
+    }
+    out.sort();
 }
 
 
-bool parseSearchPathArg(const string & arg, Strings::iterator & i,
+bool parseSearchPathArg(Strings::iterator & i,
     const Strings::iterator & argsEnd, Strings & searchPath)
 {
-    if (arg != "-I") return false;
-    if (i == argsEnd) throw UsageError(format("`%1%' requires an argument") % arg);;
-    searchPath.push_back(*i++);
+    if (*i != "-I") return false;
+    if (++i == argsEnd) throw UsageError("`-I' requires an argument");
+    searchPath.push_back(*i);
     return true;
 }
 
diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh
index 759358950f0a..bb6d399a8a61 100644
--- a/src/libexpr/common-opts.hh
+++ b/src/libexpr/common-opts.hh
@@ -5,11 +5,12 @@
 namespace nix {
 
 /* Some common option parsing between nix-env and nix-instantiate. */
-bool parseOptionArg(const string & arg, Strings::iterator & i,
-    const Strings::iterator & argsEnd, EvalState & state,
-    Bindings & autoArgs);
+bool parseAutoArgs(Strings::iterator & i,
+    const Strings::iterator & argsEnd, std::map<string, string> & res);
 
-bool parseSearchPathArg(const string & arg, Strings::iterator & i,
+void evalAutoArgs(EvalState & state, std::map<string, string> & in, Bindings & out);
+
+bool parseSearchPathArg(Strings::iterator & i,
     const Strings::iterator & argsEnd, Strings & searchPath);
 
 Path lookupFileArg(EvalState & state, string s);
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 467a15e76023..ec05db0a6675 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -100,10 +100,16 @@ string getArg(const string & opt,
 void detectStackOverflow();
 
 
-/* Initialize and reorder arguments, then call the actual argument
-   processor. */
-static void initAndRun(int argc, char * * argv)
+void initNix()
 {
+    /* Turn on buffering for cerr. */
+#if HAVE_PUBSETBUF
+    static char buf[1024];
+    std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
+#endif
+
+    std::ios::sync_with_stdio(false);
+
     settings.processEnvironment();
     settings.loadConfFile();
 
@@ -144,6 +150,14 @@ static void initAndRun(int argc, char * * argv)
     gettimeofday(&tv, 0);
     srandom(tv.tv_usec);
 
+    if (char *pack = getenv("_NIX_OPTIONS"))
+        settings.unpack(pack);
+}
+
+
+void parseCmdLine(int argc, char * * argv,
+    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
+{
     /* Put the arguments in a vector. */
     Strings args, remaining;
     while (argc--) args.push_back(*argv++);
@@ -164,7 +178,6 @@ static void initAndRun(int argc, char * * argv)
         } else remaining.push_back(arg);
     }
     args = remaining;
-    remaining.clear();
 
     /* Process default options. */
     for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
@@ -179,14 +192,6 @@ static void initAndRun(int argc, char * * argv)
             settings.buildVerbosity = lvlVomit;
         else if (arg == "--print-build-trace")
             settings.printBuildTrace = true;
-        else if (arg == "--help") {
-            printHelp();
-            return;
-        }
-        else if (arg == "--version") {
-            std::cout << format("%1% (Nix) %2%") % programId % nixVersion << std::endl;
-            return;
-        }
         else if (arg == "--keep-failed" || arg == "-K")
             settings.keepFailed = true;
         else if (arg == "--keep-going" || arg == "-k")
@@ -216,25 +221,20 @@ static void initAndRun(int argc, char * * argv)
             string value = *i;
             settings.set(name, value);
         }
-        else if (arg == "--arg" || arg == "--argstr") {
-            remaining.push_back(arg);
-            ++i; if (i == args.end()) throw UsageError(format("`%1%' requires two arguments") % arg);
-            remaining.push_back(*i);
-            ++i; if (i == args.end()) throw UsageError(format("`%1%' requires two arguments") % arg);
-            remaining.push_back(*i);
+        else {
+            if (!parseArg(i, args.end()))
+                throw UsageError(format("unrecognised option `%1%'") % *i);
         }
-        else remaining.push_back(arg);
     }
 
-    if (char *pack = getenv("_NIX_OPTIONS"))
-        settings.unpack(pack);
-
     settings.update();
+}
 
-    run(remaining);
 
-    /* Close the Nix database. */
-    store.reset((StoreAPI *) 0);
+void printVersion(const string & programName)
+{
+    std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
+    throw Exit();
 }
 
 
@@ -246,30 +246,11 @@ void showManPage(const string & name)
 }
 
 
-int exitCode = 0;
-char * * argvSaved = 0;
-
-}
-
-
-static char buf[1024];
-
-int main(int argc, char * * argv)
+int handleExceptions(const string & programName, std::function<void()> fun)
 {
-    using namespace nix;
-
-    argvSaved = argv;
-
-    /* Turn on buffering for cerr. */
-#if HAVE_PUBSETBUF
-    std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
-#endif
-
-    std::ios::sync_with_stdio(false);
-
     try {
         try {
-            initAndRun(argc, argv);
+            fun();
         } catch (...) {
             /* Subtle: we have to make sure that any `interrupted'
                condition is discharged before we reach printMsg()
@@ -279,12 +260,14 @@ int main(int argc, char * * argv)
             _isInterrupted = 0;
             throw;
         }
+    } catch (Exit & e) {
+        return e.status;
     } catch (UsageError & e) {
         printMsg(lvlError,
             format(
                 "error: %1%\n"
                 "Try `%2% --help' for more information.")
-            % e.what() % programId);
+            % e.what() % programName);
         return 1;
     } catch (BaseError & e) {
         printMsg(lvlError, format("error: %1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
@@ -299,5 +282,8 @@ int main(int argc, char * * argv)
         return 1;
     }
 
-    return exitCode;
+    return 0;
+}
+
+
 }
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index b29b08bb597e..c74e7cbc197d 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -7,25 +7,28 @@
 #include <locale>
 
 
-/* These are not implemented here, but must be implemented by a
-   program linking against libmain. */
+namespace nix {
 
-/* Main program.  Called by main() after the ATerm library has been
-   initialised and some default arguments have been processed (and
-   removed from `args').  main() will catch all exceptions. */
-void run(nix::Strings args);
+MakeError(UsageError, nix::Error);
 
-/* Should print a help message to stdout and return. */
-void printHelp();
+class Exit : public std::exception
+{
+public:
+    int status;
+    Exit() : status(0) { }
+    Exit(int status) : status(status) { }
+};
 
-extern std::string programId;
+class StoreAPI;
 
+int handleExceptions(const string & programName, std::function<void()> fun);
 
-namespace nix {
+void initNix();
 
-MakeError(UsageError, nix::Error);
+void parseCmdLine(int argc, char * * argv,
+    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);
 
-class StoreAPI;
+void printVersion(const string & programName);
 
 /* Ugh.  No better place to put this. */
 void printGCWarning();
@@ -36,6 +39,9 @@ void printMissing(const PathSet & willBuild,
     const PathSet & willSubstitute, const PathSet & unknown,
     unsigned long long downloadSize, unsigned long long narSize);
 
+string getArg(const string & opt,
+    Strings::iterator & i, const Strings::iterator & end);
+
 template<class N> N getIntArg(const string & opt,
     Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
 {
@@ -65,9 +71,4 @@ void showManPage(const string & name);
 
 extern volatile ::sig_atomic_t blockInt;
 
-/* Exit code of the program. */
-extern int exitCode;
-
-extern char * * argvSaved;
-
 }
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index 69d5fd84e000..3e6d6b2b1170 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -645,8 +645,10 @@ bool matchUser(const string & user, const string & group, const Strings & users)
 #define SD_LISTEN_FDS_START 3
 
 
-static void daemonLoop()
+static void daemonLoop(char * * argv)
 {
+    chdir("/");
+
     /* Get rid of children automatically; don't let them become
        zombies. */
     setSigChldAction(true);
@@ -766,9 +768,9 @@ static void daemonLoop()
                 setSigChldAction(false);
 
                 /* For debugging, stuff the pid into argv[1]. */
-                if (clientPid != -1 && argvSaved[1]) {
+                if (clientPid != -1 && argv[1]) {
                     string processName = int2String(clientPid);
-                    strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
+                    strncpy(argv[1], processName.c_str(), strlen(argv[1]));
                 }
 
                 /* Handle the connection. */
@@ -792,18 +794,27 @@ void run(Strings args)
 {
     for (Strings::iterator i = args.begin(); i != args.end(); ) {
         string arg = *i++;
-        if (arg == "--daemon") /* ignored for backwards compatibility */;
     }
 
-    chdir("/");
-    daemonLoop();
 }
 
 
-void printHelp()
+int main(int argc, char * * argv)
 {
-    showManPage("nix-daemon");
+    return handleExceptions(argv[0], [&]() {
+        initNix();
+
+        parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+            if (*arg == "--daemon")
+                ; /* ignored for backwards compatibility */
+            else if (*arg == "--help")
+                showManPage("nix-daemon");
+            else if (*arg == "--version")
+                printVersion("nix-daemon");
+            else return false;
+            return true;
+        });
+
+        daemonLoop(argv);
+    });
 }
-
-
-string programId = "nix-daemon";
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 062118d8a095..e65dc4e51a64 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -52,24 +52,17 @@ struct Globals
 {
     InstallSourceInfo instSource;
     Path profile;
-    EvalState state;
+    std::shared_ptr<EvalState> state;
     bool dryRun;
     bool preserveInstalled;
     bool removeAll;
     string forceName;
     bool prebuiltOnly;
-    Globals(const Strings & searchPath) : state(searchPath) { }
 };
 
 
 typedef void (* Operation) (Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs);
-
-
-void printHelp()
-{
-    showManPage("nix-env");
-}
+    Strings opFlags, Strings opArgs);
 
 
 static string needArg(Strings::iterator & i,
@@ -467,11 +460,11 @@ static void installDerivations(Globals & globals,
 
     /* Get the set of user environment elements to be installed. */
     DrvInfos newElems, newElemsTmp;
-    queryInstSources(globals.state, globals.instSource, args, newElemsTmp, true);
+    queryInstSources(*globals.state, globals.instSource, args, newElemsTmp, true);
 
     /* If --prebuilt-only is given, filter out source-only packages. */
     foreach (DrvInfos::iterator, i, newElemsTmp)
-        if (!globals.prebuiltOnly || isPrebuilt(globals.state, *i))
+        if (!globals.prebuiltOnly || isPrebuilt(*globals.state, *i))
             newElems.push_back(*i);
 
     StringSet newNames;
@@ -494,7 +487,7 @@ static void installDerivations(Globals & globals,
         /* Add in the already installed derivations, unless they have
            the same name as a to-be-installed element. */
         if (!globals.removeAll) {
-            DrvInfos installedElems = queryInstalled(globals.state, profile);
+            DrvInfos installedElems = queryInstalled(*globals.state, profile);
 
             foreach (DrvInfos::iterator, i, installedElems) {
                 DrvName drvName(i->name);
@@ -510,18 +503,17 @@ static void installDerivations(Globals & globals,
                 printMsg(lvlInfo, format("installing `%1%'") % i->name);
         }
 
-        printMissing(globals.state, newElems);
+        printMissing(*globals.state, newElems);
 
         if (globals.dryRun) return;
 
-        if (createUserEnv(globals.state, allElems,
+        if (createUserEnv(*globals.state, allElems,
                 profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
 
-static void opInstall(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opInstall(Globals & globals, Strings opFlags, Strings opArgs)
 {
     for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
         string arg = *i++;
@@ -553,11 +545,11 @@ static void upgradeDerivations(Globals & globals,
     while (true) {
         string lockToken = optimisticLockProfile(globals.profile);
 
-        DrvInfos installedElems = queryInstalled(globals.state, globals.profile);
+        DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
 
         /* Fetch all derivations from the input file. */
         DrvInfos availElems;
-        queryInstSources(globals.state, globals.instSource, args, availElems, false);
+        queryInstSources(*globals.state, globals.instSource, args, availElems, false);
 
         /* Go through all installed derivations. */
         DrvInfos newElems;
@@ -582,7 +574,7 @@ static void upgradeDerivations(Globals & globals,
                 foreach (DrvInfos::iterator, j, availElems) {
                     DrvName newName(j->name);
                     if (newName.name == drvName.name) {
-                        int d = comparePriorities(globals.state, *i, *j);
+                        int d = comparePriorities(*globals.state, *i, *j);
                         if (d == 0) d = compareVersions(drvName.version, newName.version);
                         if ((upgradeType == utLt && d < 0) ||
                             (upgradeType == utLeq && d <= 0) ||
@@ -591,10 +583,10 @@ static void upgradeDerivations(Globals & globals,
                         {
                             int d2 = -1;
                             if (bestElem != availElems.end()) {
-                                d2 = comparePriorities(globals.state, *bestElem, *j);
+                                d2 = comparePriorities(*globals.state, *bestElem, *j);
                                 if (d2 == 0) d2 = compareVersions(bestName.version, newName.version);
                             }
-                            if (d2 < 0 && (!globals.prebuiltOnly || isPrebuilt(globals.state, *j))) {
+                            if (d2 < 0 && (!globals.prebuiltOnly || isPrebuilt(*globals.state, *j))) {
                                 bestElem = j;
                                 bestName = newName;
                             }
@@ -618,18 +610,17 @@ static void upgradeDerivations(Globals & globals,
             }
         }
 
-        printMissing(globals.state, newElems);
+        printMissing(*globals.state, newElems);
 
         if (globals.dryRun) return;
 
-        if (createUserEnv(globals.state, newElems,
+        if (createUserEnv(*globals.state, newElems,
                 globals.profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
 
-static void opUpgrade(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs)
 {
     UpgradeType upgradeType = utLt;
     for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
@@ -655,8 +646,7 @@ static void setMetaFlag(EvalState & state, DrvInfo & drv,
 }
 
 
-static void opSetFlag(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -671,7 +661,7 @@ static void opSetFlag(Globals & globals,
     while (true) {
         string lockToken = optimisticLockProfile(globals.profile);
 
-        DrvInfos installedElems = queryInstalled(globals.state, globals.profile);
+        DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
 
         /* Update all matching derivations. */
         foreach (DrvInfos::iterator, i, installedElems) {
@@ -680,7 +670,7 @@ static void opSetFlag(Globals & globals,
                 if (j->matches(drvName)) {
                     printMsg(lvlInfo, format("setting flag on `%1%'") % i->name);
                     j->hits++;
-                    setMetaFlag(globals.state, *i, flagName, flagValue);
+                    setMetaFlag(*globals.state, *i, flagName, flagValue);
                     break;
                 }
         }
@@ -688,14 +678,13 @@ static void opSetFlag(Globals & globals,
         checkSelectorUse(selectors);
 
         /* Write the new user environment. */
-        if (createUserEnv(globals.state, installedElems,
+        if (createUserEnv(*globals.state, installedElems,
                 globals.profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
 
-static void opSet(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
 {
     for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
         string arg = *i++;
@@ -704,7 +693,7 @@ static void opSet(Globals & globals,
     }
 
     DrvInfos elems;
-    queryInstSources(globals.state, globals.instSource, opArgs, elems, true);
+    queryInstSources(*globals.state, globals.instSource, opArgs, elems, true);
 
     if (elems.size() != 1)
         throw Error("--set requires exactly one derivation");
@@ -715,7 +704,7 @@ static void opSet(Globals & globals,
         PathSet paths = singleton<PathSet>(drv.queryDrvPath());
         printMissing(*store, paths);
         if (globals.dryRun) return;
-        store->buildPaths(paths, globals.state.repair ? bmRepair : bmNormal);
+        store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal);
     }
     else {
         printMissing(*store, singleton<PathSet>(drv.queryOutPath()));
@@ -735,7 +724,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
     while (true) {
         string lockToken = optimisticLockProfile(profile);
 
-        DrvInfos installedElems = queryInstalled(globals.state, profile);
+        DrvInfos installedElems = queryInstalled(*globals.state, profile);
         DrvInfos newElems;
 
         foreach (DrvInfos::iterator, i, installedElems) {
@@ -756,14 +745,13 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
 
         if (globals.dryRun) return;
 
-        if (createUserEnv(globals.state, newElems,
+        if (createUserEnv(*globals.state, newElems,
                 profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
 
-static void opUninstall(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opUninstall(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -887,15 +875,14 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
                 cout << "null";
             } else {
                 PathSet context;
-                printValueAsJSON(globals.state, true, *v, cout, context);
+                printValueAsJSON(*globals.state, true, *v, cout, context);
             }
         }
     }
 }
 
 
-static void opQuery(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
 {
     Strings remaining;
     string attrPath;
@@ -916,7 +903,7 @@ static void opQuery(Globals & globals,
 
     settings.readOnlyMode = true; /* makes evaluation a bit faster */
 
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
+    for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
         string arg = *i++;
         if (arg == "--status" || arg == "-s") printStatus = true;
         else if (arg == "--no-name") printName = false;
@@ -932,10 +919,9 @@ static void opQuery(Globals & globals,
         else if (arg == "--json") jsonOutput = true;
         else if (arg == "--attr-path" || arg == "-P") printAttrPath = true;
         else if (arg == "--attr" || arg == "-A")
-            attrPath = needArg(i, args, arg);
-        else if (arg[0] == '-')
+            attrPath = needArg(i, opFlags, arg);
+        else
             throw UsageError(format("unknown flag `%1%'") % arg);
-        else remaining.push_back(arg);
     }
 
 
@@ -943,16 +929,16 @@ static void opQuery(Globals & globals,
     DrvInfos availElems, installedElems;
 
     if (source == sInstalled || compareVersions || printStatus)
-        installedElems = queryInstalled(globals.state, globals.profile);
+        installedElems = queryInstalled(*globals.state, globals.profile);
 
     if (source == sAvailable || compareVersions)
-        loadDerivations(globals.state, globals.instSource.nixExprPath,
+        loadDerivations(*globals.state, globals.instSource.nixExprPath,
             globals.instSource.systemFilter, globals.instSource.autoArgs,
             attrPath, availElems);
 
-    DrvInfos elems_ = filterBySelector(globals.state,
+    DrvInfos elems_ = filterBySelector(*globals.state,
         source == sInstalled ? installedElems : availElems,
-        remaining, false);
+        opArgs, false);
 
     DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
 
@@ -1173,8 +1159,7 @@ static void opQuery(Globals & globals,
 }
 
 
-static void opSwitchProfile(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -1222,8 +1207,7 @@ static void switchGeneration(Globals & globals, int dstGen)
 }
 
 
-static void opSwitchGeneration(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -1238,8 +1222,7 @@ static void opSwitchGeneration(Globals & globals,
 }
 
 
-static void opRollback(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opRollback(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -1250,8 +1233,7 @@ static void opRollback(Globals & globals,
 }
 
 
-static void opListGenerations(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -1288,8 +1270,7 @@ static void deleteGeneration2(Globals & globals, unsigned int gen)
 }
 
 
-static void opDeleteGenerations(Globals & globals,
-    Strings args, Strings opFlags, Strings opArgs)
+static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opArgs)
 {
     if (opFlags.size() > 0)
         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
@@ -1348,110 +1329,115 @@ static void opDeleteGenerations(Globals & globals,
 }
 
 
-void run(Strings args)
+int main(int argc, char * * argv)
 {
-    Strings opFlags, opArgs, remaining;
-    Operation op = 0;
-
-    /* FIXME: hack. */
-    Strings searchPath;
-    Strings args2;
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
-        string arg = *i++;
-        if (!parseSearchPathArg(arg, i, args.end(), searchPath))
-            args2.push_back(arg);
-    }
-    args = args2;
-
-    Globals globals(searchPath);
+    return handleExceptions(argv[0], [&]() {
+        initNix();
+
+        Strings opFlags, opArgs, searchPath;
+        std::map<string, string> autoArgs_;
+        Operation op = 0;
+        bool repair = false;
+        string file;
+
+        Globals globals;
+
+        globals.instSource.type = srcUnknown;
+        globals.instSource.nixExprPath = getDefNixExprPath();
+        globals.instSource.systemFilter = "*";
+
+        globals.dryRun = false;
+        globals.preserveInstalled = false;
+        globals.removeAll = false;
+        globals.prebuiltOnly = false;
+
+        parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+            Operation oldOp = op;
+
+            if (*arg == "--help")
+                showManPage("nix-env");
+            else if (*arg == "--version")
+                printVersion("nix-env");
+            else if (*arg == "--install" || *arg == "-i")
+                op = opInstall;
+            else if (parseAutoArgs(arg, end, autoArgs_))
+                ;
+            else if (parseSearchPathArg(arg, end, searchPath))
+                ;
+            else if (*arg == "--force-name") // undocumented flag for nix-install-package
+                globals.forceName = getArg(*arg, arg, end);
+            else if (*arg == "--uninstall" || *arg == "-e")
+                op = opUninstall;
+            else if (*arg == "--upgrade" || *arg == "-u")
+                op = opUpgrade;
+            else if (*arg == "--set-flag")
+                op = opSetFlag;
+            else if (*arg == "--set")
+                op = opSet;
+            else if (*arg == "--query" || *arg == "-q")
+                op = opQuery;
+            else if (*arg == "--profile" || *arg == "-p")
+                globals.profile = absPath(getArg(*arg, arg, end));
+            else if (*arg == "--file" || *arg == "-f")
+                file = getArg(*arg, arg, end);
+            else if (*arg == "--switch-profile" || *arg == "-S")
+                op = opSwitchProfile;
+            else if (*arg == "--switch-generation" || *arg == "-G")
+                op = opSwitchGeneration;
+            else if (*arg == "--rollback")
+                op = opRollback;
+            else if (*arg == "--list-generations")
+                op = opListGenerations;
+            else if (*arg == "--delete-generations")
+                op = opDeleteGenerations;
+            else if (*arg == "--dry-run") {
+                printMsg(lvlInfo, "(dry run; not doing anything)");
+                globals.dryRun = true;
+            }
+            else if (*arg == "--system-filter")
+                globals.instSource.systemFilter = getArg(*arg, arg, end);
+            else if (*arg == "--prebuilt-only" || *arg == "-b")
+                globals.prebuiltOnly = true;
+            else if (*arg == "--repair")
+                repair = true;
+            else if (*arg != "" && arg->at(0) == '-') {
+                opFlags.push_back(*arg);
+                if (*arg == "--from-profile" || *arg == "--atr" || *arg == "-A") /* !!! hack */
+                    opFlags.push_back(getArg(*arg, arg, end));
+            }
+            else
+                opArgs.push_back(*arg);
 
-    globals.instSource.type = srcUnknown;
-    globals.instSource.nixExprPath = getDefNixExprPath();
-    globals.instSource.systemFilter = "*";
+            if (oldOp && oldOp != op)
+                throw UsageError("only one operation may be specified");
 
-    globals.dryRun = false;
-    globals.preserveInstalled = false;
-    globals.removeAll = false;
-    globals.prebuiltOnly = false;
+            return true;
+        });
 
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
-        string arg = *i++;
+        if (!op) throw UsageError("no operation specified");
 
-        Operation oldOp = op;
-
-        if (arg == "--install" || arg == "-i")
-            op = opInstall;
-        else if (parseOptionArg(arg, i, args.end(),
-                     globals.state, globals.instSource.autoArgs))
-            ;
-        else if (arg == "--force-name") // undocumented flag for nix-install-package
-            globals.forceName = needArg(i, args, arg);
-        else if (arg == "--uninstall" || arg == "-e")
-            op = opUninstall;
-        else if (arg == "--upgrade" || arg == "-u")
-            op = opUpgrade;
-        else if (arg == "--set-flag")
-            op = opSetFlag;
-        else if (arg == "--set")
-            op = opSet;
-        else if (arg == "--query" || arg == "-q")
-            op = opQuery;
-        else if (arg == "--profile" || arg == "-p")
-            globals.profile = absPath(needArg(i, args, arg));
-        else if (arg == "--file" || arg == "-f")
-            globals.instSource.nixExprPath = lookupFileArg(globals.state, needArg(i, args, arg));
-        else if (arg == "--switch-profile" || arg == "-S")
-            op = opSwitchProfile;
-        else if (arg == "--switch-generation" || arg == "-G")
-            op = opSwitchGeneration;
-        else if (arg == "--rollback")
-            op = opRollback;
-        else if (arg == "--list-generations")
-            op = opListGenerations;
-        else if (arg == "--delete-generations")
-            op = opDeleteGenerations;
-        else if (arg == "--dry-run") {
-            printMsg(lvlInfo, "(dry run; not doing anything)");
-            globals.dryRun = true;
-        }
-        else if (arg == "--system-filter")
-            globals.instSource.systemFilter = needArg(i, args, arg);
-        else if (arg == "--prebuilt-only" || arg == "-b")
-            globals.prebuiltOnly = true;
-        else if (arg == "--repair")
-            globals.state.repair = true;
-        else {
-            remaining.push_back(arg);
-            if (arg[0] == '-') {
-                opFlags.push_back(arg);
-                if (arg == "--from-profile") { /* !!! hack */
-                    if (i != args.end()) opFlags.push_back(*i++);
-                }
-            } else opArgs.push_back(arg);
-        }
+        globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath));
+        globals.state->repair = repair;
 
-        if (oldOp && oldOp != op)
-            throw UsageError("only one operation may be specified");
-    }
+        if (file != "")
+            globals.instSource.nixExprPath = lookupFileArg(*globals.state, file);
 
-    if (!op) throw UsageError("no operation specified");
+        evalAutoArgs(*globals.state, autoArgs_, globals.instSource.autoArgs);
 
-    if (globals.profile == "")
-        globals.profile = getEnv("NIX_PROFILE", "");
+        if (globals.profile == "")
+            globals.profile = getEnv("NIX_PROFILE", "");
 
-    if (globals.profile == "") {
-        Path profileLink = getHomeDir() + "/.nix-profile";
-        globals.profile = pathExists(profileLink)
-            ? absPath(readLink(profileLink), dirOf(profileLink))
-            : canonPath(settings.nixStateDir + "/profiles/default");
-    }
+        if (globals.profile == "") {
+            Path profileLink = getHomeDir() + "/.nix-profile";
+            globals.profile = pathExists(profileLink)
+                ? absPath(readLink(profileLink), dirOf(profileLink))
+                : canonPath(settings.nixStateDir + "/profiles/default");
+        }
 
-    store = openStore();
+        store = openStore();
 
-    op(globals, remaining, opFlags, opArgs);
+        op(globals, opFlags, opArgs);
 
-    globals.state.printStats();
+        globals.state->printStats();
+    });
 }
-
-
-string programId = "nix-env";
diff --git a/src/nix-hash/nix-hash.cc b/src/nix-hash/nix-hash.cc
index af3dda4ad7b8..b08f0b0b142b 100644
--- a/src/nix-hash/nix-hash.cc
+++ b/src/nix-hash/nix-hash.cc
@@ -3,17 +3,10 @@
 
 #include <iostream>
 
-
 using namespace nix;
 
 
-void printHelp()
-{
-    showManPage("nix-hash");
-}
-
-
-void run(Strings args)
+int main(int argc, char * * argv)
 {
     HashType ht = htMD5;
     bool flat = false;
@@ -23,42 +16,48 @@ void run(Strings args)
 
     Strings ss;
 
-    for (Strings::iterator i = args.begin();
-         i != args.end(); i++)
-    {
-        if (*i == "--flat") flat = true;
-        else if (*i == "--base32") base32 = true;
-        else if (*i == "--truncate") truncate = true;
-        else if (*i == "--type") {
-            ++i;
-            if (i == args.end()) throw UsageError("`--type' requires an argument");
-            ht = parseHashType(*i);
-            if (ht == htUnknown)
-                throw UsageError(format("unknown hash type `%1%'") % *i);
+    return handleExceptions(argv[0], [&]() {
+        initNix();
+
+        parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+            if (*arg == "--help")
+                showManPage("nix-hash");
+            else if (*arg == "--version")
+                printVersion("nix-hash");
+            else if (*arg == "--flat") flat = true;
+            else if (*arg == "--base32") base32 = true;
+            else if (*arg == "--truncate") truncate = true;
+            else if (*arg == "--type") {
+                string s = getArg(*arg, arg, end);
+                ht = parseHashType(s);
+                if (ht == htUnknown)
+                    throw UsageError(format("unknown hash type `%1%'") % s);
+            }
+            else if (*arg == "--to-base16") op = opTo16;
+            else if (*arg == "--to-base32") op = opTo32;
+            else if (*arg != "" && arg->at(0) == '-')
+                return false;
+            else
+                ss.push_back(*arg);
+            return true;
+        });
+
+        if (op == opHash) {
+            for (auto & i : ss) {
+                Hash h = flat ? hashFile(ht, i) : hashPath(ht, i).first;
+                if (truncate && h.hashSize > 20) h = compressHash(h, 20);
+                std::cout << format("%1%\n") %
+                    (base32 ? printHash32(h) : printHash(h));
+            }
         }
-        else if (*i == "--to-base16") op = opTo16;
-        else if (*i == "--to-base32") op = opTo32;
-        else ss.push_back(*i);
-    }
 
-    if (op == opHash) {
-        foreach (Strings::iterator, i, ss) {
-            Hash h = flat ? hashFile(ht, *i) : hashPath(ht, *i).first;
-            if (truncate && h.hashSize > 20) h = compressHash(h, 20);
-            std::cout << format("%1%\n") %
-                (base32 ? printHash32(h) : printHash(h));
+        else {
+            for (auto & i : ss) {
+                Hash h = parseHash16or32(ht, i);
+                std::cout << format("%1%\n") %
+                    (op == opTo16 ? printHash(h) : printHash32(h));
+            }
         }
-    }
-
-    else {
-        foreach (Strings::iterator, i, ss) {
-            Hash h = parseHash16or32(ht, *i);
-            std::cout << format("%1%\n") %
-                (op == opTo16 ? printHash(h) : printHash32(h));
-        }
-    }
+    });
 }
 
-
-string programId = "nix-hash";
-
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index a188b28fe88b..e9f942769723 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -18,12 +18,6 @@
 using namespace nix;
 
 
-void printHelp()
-{
-    showManPage("nix-instantiate");
-}
-
-
 static Expr * parseStdin(EvalState & state)
 {
     startNest(nest, lvlTalkative, format("parsing standard input"));
@@ -95,112 +89,108 @@ void processExpr(EvalState & state, const Strings & attrPaths,
 }
 
 
-void run(Strings args)
+int main(int argc, char * * argv)
 {
-    /* FIXME: hack. */
-    Strings searchPath;
-    Strings args2;
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
-        string arg = *i++;
-        if (!parseSearchPathArg(arg, i, args.end(), searchPath))
-            args2.push_back(arg);
-    }
-    args = args2;
-
-    EvalState state(searchPath);
-    Strings files;
-    bool readStdin = false;
-    bool fromArgs = false;
-    bool findFile = false;
-    bool evalOnly = false;
-    bool parseOnly = false;
-    OutputKind outputKind = okPlain;
-    bool xmlOutputSourceLocation = true;
-    bool strict = false;
-    Strings attrPaths;
-    Bindings autoArgs;
-    bool wantsReadWrite = false;
-
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
-        string arg = *i++;
-
-        if (arg == "-")
-            readStdin = true;
-        else if (arg == "--expr" || arg == "-E")
-            fromArgs = true;
-        else if (arg == "--eval" || arg == "--eval-only")
-            evalOnly = true;
-        else if (arg == "--read-write-mode")
-            wantsReadWrite = true;
-        else if (arg == "--parse" || arg == "--parse-only")
-            parseOnly = evalOnly = true;
-        else if (arg == "--find-file")
-            findFile = true;
-        else if (arg == "--attr" || arg == "-A") {
-            if (i == args.end())
-                throw UsageError("`--attr' requires an argument");
-            attrPaths.push_back(*i++);
-        }
-        else if (parseOptionArg(arg, i, args.end(), state, autoArgs))
-            ;
-        else if (arg == "--add-root") {
-            if (i == args.end())
-                throw UsageError("`--add-root' requires an argument");
-            gcRoot = absPath(*i++);
-        }
-        else if (arg == "--indirect")
-            indirectRoot = true;
-        else if (arg == "--xml")
-            outputKind = okXML;
-        else if (arg == "--json")
-            outputKind = okJSON;
-        else if (arg == "--no-location")
-            xmlOutputSourceLocation = false;
-        else if (arg == "--strict")
-            strict = true;
-        else if (arg == "--repair")
-            state.repair = true;
-        else if (arg == "--dry-run")
-            settings.readOnlyMode = true;
-        else if (arg[0] == '-')
-            throw UsageError(format("unknown flag `%1%'") % arg);
-        else
-            files.push_back(arg);
-    }
+    return handleExceptions(argv[0], [&]() {
+        initNix();
+
+        Strings files, searchPath;
+        bool readStdin = false;
+        bool fromArgs = false;
+        bool findFile = false;
+        bool evalOnly = false;
+        bool parseOnly = false;
+        OutputKind outputKind = okPlain;
+        bool xmlOutputSourceLocation = true;
+        bool strict = false;
+        Strings attrPaths;
+        bool wantsReadWrite = false;
+        std::map<string, string> autoArgs_;
+        bool repair = false;
+
+        parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+            if (*arg == "--help")
+                showManPage("nix-instantiate");
+            else if (*arg == "--version")
+                printVersion("nix-instantiate");
+            else if (*arg == "-")
+                readStdin = true;
+            else if (*arg == "--expr" || *arg == "-E")
+                fromArgs = true;
+            else if (*arg == "--eval" || *arg == "--eval-only")
+                evalOnly = true;
+            else if (*arg == "--read-write-mode")
+                wantsReadWrite = true;
+            else if (*arg == "--parse" || *arg == "--parse-only")
+                parseOnly = evalOnly = true;
+            else if (*arg == "--find-file")
+                findFile = true;
+            else if (*arg == "--attr" || *arg == "-A")
+                attrPaths.push_back(getArg(*arg, arg, end));
+            else if (parseAutoArgs(arg, end, autoArgs_))
+                ;
+            else if (parseSearchPathArg(arg, end, searchPath))
+                ;
+            else if (*arg == "--add-root")
+                gcRoot = getArg(*arg, arg, end);
+            else if (*arg == "--indirect")
+                indirectRoot = true;
+            else if (*arg == "--xml")
+                outputKind = okXML;
+            else if (*arg == "--json")
+                outputKind = okJSON;
+            else if (*arg == "--no-location")
+                xmlOutputSourceLocation = false;
+            else if (*arg == "--strict")
+                strict = true;
+            else if (*arg == "--repair")
+                repair = true;
+            else if (*arg == "--dry-run")
+                settings.readOnlyMode = true;
+            else if (*arg != "" && arg->at(0) == '-')
+                return false;
+            else
+                files.push_back(*arg);
+            return true;
+        });
 
-    if (evalOnly && !wantsReadWrite)
-        settings.readOnlyMode = true;
+        EvalState state(searchPath);
+        state.repair = repair;
 
-    if (attrPaths.empty()) attrPaths.push_back("");
+        Bindings autoArgs;
+        evalAutoArgs(state, autoArgs_, autoArgs);
 
-    if (findFile) {
-        foreach (Strings::iterator, i, files) {
-            Path p = state.findFile(*i);
-            if (p == "") throw Error(format("unable to find `%1%'") % *i);
-            std::cout << p << std::endl;
+        if (evalOnly && !wantsReadWrite)
+            settings.readOnlyMode = true;
+
+        if (attrPaths.empty()) attrPaths.push_back("");
+
+        if (findFile) {
+            foreach (Strings::iterator, i, files) {
+                Path p = state.findFile(*i);
+                if (p == "") throw Error(format("unable to find `%1%'") % *i);
+                std::cout << p << std::endl;
+            }
+            return;
         }
-        return;
-    }
 
-    store = openStore();
-
-    if (readStdin) {
-        Expr * e = parseStdin(state);
-        processExpr(state, attrPaths, parseOnly, strict, autoArgs,
-            evalOnly, outputKind, xmlOutputSourceLocation, e);
-    } else if (files.empty() && !fromArgs)
-        files.push_back("./default.nix");
-
-    foreach (Strings::iterator, i, files) {
-        Expr * e = fromArgs
-            ? state.parseExprFromString(*i, absPath("."))
-            : state.parseExprFromFile(resolveExprPath(lookupFileArg(state, *i)));
-        processExpr(state, attrPaths, parseOnly, strict, autoArgs,
-            evalOnly, outputKind, xmlOutputSourceLocation, e);
-    }
+        store = openStore();
 
-    state.printStats();
-}
+        if (readStdin) {
+            Expr * e = parseStdin(state);
+            processExpr(state, attrPaths, parseOnly, strict, autoArgs,
+                evalOnly, outputKind, xmlOutputSourceLocation, e);
+        } else if (files.empty() && !fromArgs)
+            files.push_back("./default.nix");
 
+        foreach (Strings::iterator, i, files) {
+            Expr * e = fromArgs
+                ? state.parseExprFromString(*i, absPath("."))
+                : state.parseExprFromFile(resolveExprPath(lookupFileArg(state, *i)));
+            processExpr(state, attrPaths, parseOnly, strict, autoArgs,
+                evalOnly, outputKind, xmlOutputSourceLocation, e);
+        }
 
-string programId = "nix-instantiate";
+        state.printStats();
+    });
+}
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 932789f2c071..6a297b429239 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -29,12 +29,6 @@ using std::cout;
 typedef void (* Operation) (Strings opFlags, Strings opArgs);
 
 
-void printHelp()
-{
-    showManPage("nix-store");
-}
-
-
 static Path gcRoot;
 static int rootNr = 0;
 static bool indirectRoot = false;
@@ -782,7 +776,7 @@ static void opVerify(Strings opFlags, Strings opArgs)
 
     if (ensureLocalStore().verifyStore(checkContents, repair)) {
         printMsg(lvlError, "warning: not all errors were fixed");
-        exitCode = 1;
+        throw Exit(1);
     }
 }
 
@@ -793,6 +787,8 @@ static void opVerifyPath(Strings opFlags, Strings opArgs)
     if (!opFlags.empty())
         throw UsageError("no flags expected");
 
+    int status = 0;
+
     foreach (Strings::iterator, i, opArgs) {
         Path path = followLinksToStorePath(*i);
         printMsg(lvlTalkative, format("checking path `%1%'...") % path);
@@ -802,9 +798,11 @@ static void opVerifyPath(Strings opFlags, Strings opArgs)
             printMsg(lvlError,
                 format("path `%1%' was modified! expected hash `%2%', got `%3%'")
                 % path % printHash(info.hash) % printHash(current.first));
-            exitCode = 1;
+            status = 1;
         }
     }
+
+    throw Exit(status);
 }
 
 
@@ -1017,95 +1015,96 @@ static void opServe(Strings opFlags, Strings opArgs)
 /* Scan the arguments; find the operation, set global flags, put all
    other flags in a list, and put all other arguments in another
    list. */
-void run(Strings args)
+int main(int argc, char * * argv)
 {
-    Strings opFlags, opArgs;
-    Operation op = 0;
-
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
-        string arg = *i++;
-
-        Operation oldOp = op;
-
-        if (arg == "--realise" || arg == "--realize" || arg == "-r")
-            op = opRealise;
-        else if (arg == "--add" || arg == "-A")
-            op = opAdd;
-        else if (arg == "--add-fixed")
-            op = opAddFixed;
-        else if (arg == "--print-fixed-path")
-            op = opPrintFixedPath;
-        else if (arg == "--delete")
-            op = opDelete;
-        else if (arg == "--query" || arg == "-q")
-            op = opQuery;
-        else if (arg == "--print-env")
-            op = opPrintEnv;
-        else if (arg == "--read-log" || arg == "-l")
-            op = opReadLog;
-        else if (arg == "--dump-db")
-            op = opDumpDB;
-        else if (arg == "--load-db")
-            op = opLoadDB;
-        else if (arg == "--register-validity")
-            op = opRegisterValidity;
-        else if (arg == "--check-validity")
-            op = opCheckValidity;
-        else if (arg == "--gc")
-            op = opGC;
-        else if (arg == "--dump")
-            op = opDump;
-        else if (arg == "--restore")
-            op = opRestore;
-        else if (arg == "--export")
-            op = opExport;
-        else if (arg == "--import")
-            op = opImport;
-        else if (arg == "--init")
-            op = opInit;
-        else if (arg == "--verify")
-            op = opVerify;
-        else if (arg == "--verify-path")
-            op = opVerifyPath;
-        else if (arg == "--repair-path")
-            op = opRepairPath;
-        else if (arg == "--optimise" || arg == "--optimize")
-            op = opOptimise;
-        else if (arg == "--query-failed-paths")
-            op = opQueryFailedPaths;
-        else if (arg == "--clear-failed-paths")
-            op = opClearFailedPaths;
-        else if (arg == "--add-root") {
-            if (i == args.end())
-                throw UsageError("`--add-root requires an argument");
-            gcRoot = absPath(*i++);
-        }
-        else if (arg == "--indirect")
-            indirectRoot = true;
-        else if (arg == "--no-output")
-            noOutput = true;
-        else if (arg == "--serve")
-            op = opServe;
-        else if (arg[0] == '-') {
-            opFlags.push_back(arg);
-            if (arg == "--max-freed" || arg == "--max-links" || arg == "--max-atime") { /* !!! hack */
-                if (i != args.end()) opFlags.push_back(*i++);
+    return handleExceptions(argv[0], [&]() {
+        initNix();
+
+        Strings opFlags, opArgs;
+        Operation op = 0;
+
+        parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+            Operation oldOp = op;
+
+            if (*arg == "--help")
+                showManPage("nix-store");
+            else if (*arg == "--version")
+                printVersion("nix-store");
+            else if (*arg == "--realise" || *arg == "--realize" || *arg == "-r")
+                op = opRealise;
+            else if (*arg == "--add" || *arg == "-A")
+                op = opAdd;
+            else if (*arg == "--add-fixed")
+                op = opAddFixed;
+            else if (*arg == "--print-fixed-path")
+                op = opPrintFixedPath;
+            else if (*arg == "--delete")
+                op = opDelete;
+            else if (*arg == "--query" || *arg == "-q")
+                op = opQuery;
+            else if (*arg == "--print-env")
+                op = opPrintEnv;
+            else if (*arg == "--read-log" || *arg == "-l")
+                op = opReadLog;
+            else if (*arg == "--dump-db")
+                op = opDumpDB;
+            else if (*arg == "--load-db")
+                op = opLoadDB;
+            else if (*arg == "--register-validity")
+                op = opRegisterValidity;
+            else if (*arg == "--check-validity")
+                op = opCheckValidity;
+            else if (*arg == "--gc")
+                op = opGC;
+            else if (*arg == "--dump")
+                op = opDump;
+            else if (*arg == "--restore")
+                op = opRestore;
+            else if (*arg == "--export")
+                op = opExport;
+            else if (*arg == "--import")
+                op = opImport;
+            else if (*arg == "--init")
+                op = opInit;
+            else if (*arg == "--verify")
+                op = opVerify;
+            else if (*arg == "--verify-path")
+                op = opVerifyPath;
+            else if (*arg == "--repair-path")
+                op = opRepairPath;
+            else if (*arg == "--optimise" || *arg == "--optimize")
+                op = opOptimise;
+            else if (*arg == "--query-failed-paths")
+                op = opQueryFailedPaths;
+            else if (*arg == "--clear-failed-paths")
+                op = opClearFailedPaths;
+            else if (*arg == "--add-root")
+                gcRoot = absPath(getArg(*arg, arg, end));
+            else if (*arg == "--indirect")
+                indirectRoot = true;
+            else if (*arg == "--no-output")
+                noOutput = true;
+            else if (*arg == "--serve")
+                op = opServe;
+            else if (*arg != "" && arg->at(0) == '-') {
+                opFlags.push_back(*arg);
+                if (*arg == "--max-freed" || *arg == "--max-links" || *arg == "--max-atime") /* !!! hack */
+                    opFlags.push_back(getArg(*arg, arg, end));
             }
-        }
-        else
-            opArgs.push_back(arg);
-
-        if (oldOp && oldOp != op)
-            throw UsageError("only one operation may be specified");
-    }
+            else
+                opArgs.push_back(*arg);
 
-    if (!op) throw UsageError("no operation specified");
+            if (oldOp && oldOp != op)
+                throw UsageError("only one operation may be specified");
 
-    if (op != opDump && op != opRestore) /* !!! hack */
-        store = openStore(op != opGC);
+            return true;
+        });
 
-    op(opFlags, opArgs);
-}
+        if (!op) throw UsageError("no operation specified");
 
+        if (op != opDump && op != opRestore) /* !!! hack */
+            store = openStore(op != opGC);
 
-string programId = "nix-store";
+        op(opFlags, opArgs);
+    });
+}