about summary refs log tree commit diff
path: root/src/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/affinity.cc6
-rw-r--r--src/libutil/archive.cc6
-rw-r--r--src/libutil/args.hh2
-rw-r--r--src/libutil/finally.hh2
-rw-r--r--src/libutil/json.cc10
-rw-r--r--src/libutil/json.hh2
-rw-r--r--src/libutil/logging.cc14
-rw-r--r--src/libutil/logging.hh11
-rw-r--r--src/libutil/regex.cc50
-rw-r--r--src/libutil/regex.hh29
-rw-r--r--src/libutil/serialise.cc2
-rw-r--r--src/libutil/sync.hh8
-rw-r--r--src/libutil/thread-pool.cc2
-rw-r--r--src/libutil/types.hh69
-rw-r--r--src/libutil/util.cc125
-rw-r--r--src/libutil/util.hh75
16 files changed, 230 insertions, 183 deletions
diff --git a/src/libutil/affinity.cc b/src/libutil/affinity.cc
index 3cbdf878617a..98f8287ada67 100644
--- a/src/libutil/affinity.cc
+++ b/src/libutil/affinity.cc
@@ -20,12 +20,12 @@ void setAffinityTo(int cpu)
 #if __linux__
     if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
     didSaveAffinity = true;
-    printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
+    debug(format("locking this thread to CPU %1%") % cpu);
     cpu_set_t newAffinity;
     CPU_ZERO(&newAffinity);
     CPU_SET(cpu, &newAffinity);
     if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
-        printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu);
+        printError(format("failed to lock thread to CPU %1%") % cpu);
 #endif
 }
 
@@ -47,7 +47,7 @@ void restoreAffinity()
 #if __linux__
     if (!didSaveAffinity) return;
     if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
-        printMsg(lvlError, "failed to restore affinity %1%");
+        printError("failed to restore affinity %1%");
 #endif
 }
 
diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc
index edd4a881b485..fbba7f853f95 100644
--- a/src/libutil/archive.cc
+++ b/src/libutil/archive.cc
@@ -1,5 +1,3 @@
-#define _XOPEN_SOURCE 600
-
 #include "config.h"
 
 #include <cerrno>
@@ -84,7 +82,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
                 string name(i.name);
                 size_t pos = i.name.find(caseHackSuffix);
                 if (pos != string::npos) {
-                    printMsg(lvlDebug, format("removing case hack suffix from ‘%1%’") % (path + "/" + i.name));
+                    debug(format("removing case hack suffix from ‘%1%’") % (path + "/" + i.name));
                     name.erase(pos);
                 }
                 if (unhacked.find(name) != unhacked.end())
@@ -248,7 +246,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
                     if (useCaseHack) {
                         auto i = names.find(name);
                         if (i != names.end()) {
-                            printMsg(lvlDebug, format("case collision between ‘%1%’ and ‘%2%’") % i->first % name);
+                            debug(format("case collision between ‘%1%’ and ‘%2%’") % i->first % name);
                             name += caseHackSuffix;
                             name += std::to_string(++i->second);
                         } else
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index 6aa08aacac9e..ac12f8be633a 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -8,7 +8,7 @@
 
 namespace nix {
 
-MakeError(UsageError, nix::Error);
+MakeError(UsageError, Error);
 
 enum HashType : char;
 
diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh
index 47c64deaecea..7760cfe9a410 100644
--- a/src/libutil/finally.hh
+++ b/src/libutil/finally.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <functional>
+
 /* A trivial class to run a function at the end of a scope. */
 class Finally
 {
diff --git a/src/libutil/json.cc b/src/libutil/json.cc
index ecc3fdfe514e..6023d1d4fb84 100644
--- a/src/libutil/json.cc
+++ b/src/libutil/json.cc
@@ -44,6 +44,16 @@ void toJSON(std::ostream & str, long n)
     str << n;
 }
 
+void toJSON(std::ostream & str, unsigned int n)
+{
+    str << n;
+}
+
+void toJSON(std::ostream & str, int n)
+{
+    str << n;
+}
+
 void toJSON(std::ostream & str, double f)
 {
     str << f;
diff --git a/src/libutil/json.hh b/src/libutil/json.hh
index aec456845056..03eecb732586 100644
--- a/src/libutil/json.hh
+++ b/src/libutil/json.hh
@@ -12,6 +12,8 @@ void toJSON(std::ostream & str, const char * s);
 void toJSON(std::ostream & str, unsigned long long n);
 void toJSON(std::ostream & str, unsigned long n);
 void toJSON(std::ostream & str, long n);
+void toJSON(std::ostream & str, unsigned int n);
+void toJSON(std::ostream & str, int n);
 void toJSON(std::ostream & str, double f);
 void toJSON(std::ostream & str, bool b);
 
diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc
index 15bb1e175da6..d9e8d22d7685 100644
--- a/src/libutil/logging.cc
+++ b/src/libutil/logging.cc
@@ -52,7 +52,7 @@ Verbosity verbosity = lvlInfo;
 void warnOnce(bool & haveWarned, const FormatOrString & fs)
 {
     if (!haveWarned) {
-        printMsg(lvlError, format("warning: %1%") % fs.s);
+        printError(format("warning: %1%") % fs.s);
         haveWarned = true;
     }
 }
@@ -60,14 +60,12 @@ void warnOnce(bool & haveWarned, const FormatOrString & fs)
 void writeToStderr(const string & s)
 {
     try {
-        writeFull(STDERR_FILENO, s);
+        writeFull(STDERR_FILENO, s, false);
     } catch (SysError & e) {
-        /* Ignore failing writes to stderr if we're in an exception
-           handler, otherwise throw an exception.  We need to ignore
-           write errors in exception handlers to ensure that cleanup
-           code runs to completion if the other side of stderr has
-           been closed unexpectedly. */
-        if (!std::uncaught_exception()) throw;
+        /* Ignore failing writes to stderr.  We need to ignore write
+           errors to ensure that cleanup code that logs to stderr runs
+           to completion if the other side of stderr has been closed
+           unexpectedly. */
     }
 }
 
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index 277dff280053..ba99a81c3826 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -66,14 +66,19 @@ Logger * makeDefaultLogger();
 
 extern Verbosity verbosity; /* suppress msgs > this */
 
-#define printMsg(level, f) \
+/* Print a message if the current log level is at least the specified
+   level. Note that this has to be implemented as a macro to ensure
+   that the arguments are evaluated lazily. */
+#define printMsg(level, args...) \
     do { \
         if (level <= nix::verbosity) { \
-            logger->log(level, (f)); \
+            logger->log(level, fmt(args)); \
         } \
     } while (0)
 
-#define debug(f) printMsg(lvlDebug, f)
+#define printError(args...) printMsg(lvlError, args)
+#define printInfo(args...) printMsg(lvlInfo, args)
+#define debug(args...) printMsg(lvlDebug, args)
 
 void warnOnce(bool & haveWarned, const FormatOrString & fs);
 
diff --git a/src/libutil/regex.cc b/src/libutil/regex.cc
deleted file mode 100644
index 84274b3e1da9..000000000000
--- a/src/libutil/regex.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "regex.hh"
-#include "types.hh"
-
-#include <algorithm>
-
-namespace nix {
-
-Regex::Regex(const string & pattern, bool subs)
-{
-    /* Patterns must match the entire string. */
-    int err = regcomp(&preg, ("^(" + pattern + ")$").c_str(), (subs ? 0 : REG_NOSUB) | REG_EXTENDED);
-    if (err) throw RegexError(format("compiling pattern ‘%1%’: %2%") % pattern % showError(err));
-    nrParens = subs ? std::count(pattern.begin(), pattern.end(), '(') : 0;
-}
-
-Regex::~Regex()
-{
-    regfree(&preg);
-}
-
-bool Regex::matches(const string & s)
-{
-    int err = regexec(&preg, s.c_str(), 0, 0, 0);
-    if (err == 0) return true;
-    else if (err == REG_NOMATCH) return false;
-    throw Error(format("matching string ‘%1%’: %2%") % s % showError(err));
-}
-
-bool Regex::matches(const string & s, Subs & subs)
-{
-    regmatch_t pmatch[nrParens + 2];
-    int err = regexec(&preg, s.c_str(), nrParens + 2, pmatch, 0);
-    if (err == 0) {
-        for (unsigned int n = 2; n < nrParens + 2; ++n)
-            if (pmatch[n].rm_eo != -1)
-                subs[n - 2] = string(s, pmatch[n].rm_so, pmatch[n].rm_eo - pmatch[n].rm_so);
-        return true;
-    }
-    else if (err == REG_NOMATCH) return false;
-    throw Error(format("matching string ‘%1%’: %2%") % s % showError(err));
-}
-
-string Regex::showError(int err)
-{
-    char buf[256];
-    regerror(err, &preg, buf, sizeof(buf));
-    return string(buf);
-}
-
-}
diff --git a/src/libutil/regex.hh b/src/libutil/regex.hh
deleted file mode 100644
index 53e31f4edc4a..000000000000
--- a/src/libutil/regex.hh
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-#include "types.hh"
-
-#include <sys/types.h>
-#include <regex.h>
-
-#include <map>
-
-namespace nix {
-
-MakeError(RegexError, Error)
-
-class Regex
-{
-public:
-    Regex(const string & pattern, bool subs = false);
-    ~Regex();
-    bool matches(const string & s);
-    typedef std::map<unsigned int, string> Subs;
-    bool matches(const string & s, Subs & subs);
-
-private:
-    unsigned nrParens;
-    regex_t preg;
-    string showError(int err);
-};
-
-}
diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc
index 776308cdf321..24c6d107359e 100644
--- a/src/libutil/serialise.cc
+++ b/src/libutil/serialise.cc
@@ -49,7 +49,7 @@ size_t threshold = 256 * 1024 * 1024;
 
 static void warnLargeDump()
 {
-    printMsg(lvlError, "warning: dumping very large path (> 256 MiB); this may run out of memory");
+    printError("warning: dumping very large path (> 256 MiB); this may run out of memory");
 }
 
 
diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh
index ebe64ffbdab7..2aa074299b23 100644
--- a/src/libutil/sync.hh
+++ b/src/libutil/sync.hh
@@ -54,6 +54,14 @@ public:
             cv.wait(lk);
         }
 
+        template<class Rep, class Period>
+        void wait_for(std::condition_variable & cv,
+            const std::chrono::duration<Rep, Period> & duration)
+        {
+            assert(s);
+            cv.wait_for(lk, duration);
+        }
+
         template<class Rep, class Period, class Predicate>
         bool wait_for(std::condition_variable & cv,
             const std::chrono::duration<Rep, Period> & duration,
diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc
index 696ecd6c38c8..0a3a407240f7 100644
--- a/src/libutil/thread-pool.cc
+++ b/src/libutil/thread-pool.cc
@@ -87,7 +87,7 @@ void ThreadPool::workerEntry()
             if (state->exception) {
                 if (!dynamic_cast<Interrupted*>(&e) &&
                     !dynamic_cast<ThreadPoolShutDown*>(&e))
-                    printMsg(lvlError, format("error: %s") % e.what());
+                    printError(format("error: %s") % e.what());
             } else {
                 state->exception = std::current_exception();
                 work.notify_all();
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index bd192b8506b2..b9a93d27d2ad 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -41,6 +41,45 @@ struct FormatOrString
 };
 
 
+/* A helper for formatting strings. ‘fmt(format, a_0, ..., a_n)’ is
+   equivalent to ‘boost::format(format) % a_0 % ... %
+   ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion
+   takes place). */
+
+inline void formatHelper(boost::format & f)
+{
+}
+
+template<typename T, typename... Args>
+inline void formatHelper(boost::format & f, T x, Args... args)
+{
+    formatHelper(f % x, args...);
+}
+
+inline std::string fmt(const std::string & s)
+{
+    return s;
+}
+
+inline std::string fmt(const char * s)
+{
+    return s;
+}
+
+inline std::string fmt(const FormatOrString & fs)
+{
+    return fs.s;
+}
+
+template<typename... Args>
+inline std::string fmt(const std::string & fs, Args... args)
+{
+    boost::format f(fs);
+    formatHelper(f, args...);
+    return f.str();
+}
+
+
 /* BaseError should generally not be caught, as it has Interrupted as
    a subclass. Catch Error instead. */
 class BaseError : public std::exception
@@ -49,14 +88,28 @@ protected:
     string prefix_; // used for location traces etc.
     string err;
 public:
-    unsigned int status; // exit status
-    BaseError(const FormatOrString & fs, unsigned int status = 1);
+    unsigned int status = 1; // exit status
+
+    template<typename... Args>
+    BaseError(unsigned int status, Args... args)
+        : err(fmt(args...))
+        , status(status)
+    {
+    }
+
+    template<typename... Args>
+    BaseError(Args... args)
+        : err(fmt(args...))
+    {
+    }
+
 #ifdef EXCEPTION_NEEDS_THROW_SPEC
     ~BaseError() throw () { };
     const char * what() const throw () { return err.c_str(); }
 #else
     const char * what() const noexcept { return err.c_str(); }
 #endif
+
     const string & msg() const { return err; }
     const string & prefix() const { return prefix_; }
     BaseError & addPrefix(const FormatOrString & fs);
@@ -66,7 +119,7 @@ public:
     class newClass : public superClass                  \
     {                                                   \
     public:                                             \
-        newClass(const FormatOrString & fs, unsigned int status = 1) : superClass(fs, status) { }; \
+        using superClass::superClass;                   \
     };
 
 MakeError(Error, BaseError)
@@ -75,7 +128,15 @@ class SysError : public Error
 {
 public:
     int errNo;
-    SysError(const FormatOrString & fs);
+
+    template<typename... Args>
+    SysError(Args... args)
+        : Error(addErrno(fmt(args...)))
+    { }
+
+private:
+
+    std::string addErrno(const std::string & s);
 };
 
 
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index f1e714a664a5..ce16cc30a5c7 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -9,6 +9,7 @@
 #include <cstdlib>
 #include <sstream>
 #include <cstring>
+#include <cctype>
 
 #include <sys/wait.h>
 #include <unistd.h>
@@ -30,13 +31,6 @@ extern char * * environ;
 namespace nix {
 
 
-BaseError::BaseError(const FormatOrString & fs, unsigned int status)
-    : status(status)
-{
-    err = fs.s;
-}
-
-
 BaseError & BaseError::addPrefix(const FormatOrString & fs)
 {
     prefix_ = fs.s + prefix_;
@@ -44,10 +38,10 @@ BaseError & BaseError::addPrefix(const FormatOrString & fs)
 }
 
 
-SysError::SysError(const FormatOrString & fs)
-    : Error(format("%1%: %2%") % fs.s % strerror(errno))
-    , errNo(errno)
+std::string SysError::addErrno(const std::string & s)
 {
+    errNo = errno;
+    return s + ": " + strerror(errNo);
 }
 
 
@@ -58,6 +52,21 @@ string getEnv(const string & key, const string & def)
 }
 
 
+std::map<std::string, std::string> getEnv()
+{
+    std::map<std::string, std::string> env;
+    for (size_t i = 0; environ[i]; ++i) {
+        auto s = environ[i];
+        auto eq = strchr(s, '=');
+        if (!eq)
+            // invalid env, just keep going
+            continue;
+        env.emplace(std::string(s, eq), std::string(eq + 1));
+    }
+    return env;
+}
+
+
 Path absPath(Path path, Path dir)
 {
     if (path[0] != '/') {
@@ -330,10 +339,11 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
         bytesFreed += st.st_blocks * 512;
 
     if (S_ISDIR(st.st_mode)) {
-        /* Make the directory writable. */
-        if (!(st.st_mode & S_IWUSR)) {
-            if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
-                throw SysError(format("making ‘%1%’ writable") % path);
+        /* Make the directory accessible. */
+        const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
+        if ((st.st_mode & PERM_MASK) != PERM_MASK) {
+            if (chmod(path.c_str(), st.st_mode | PERM_MASK) == -1)
+                throw SysError(format("chmod ‘%1%’") % path);
         }
 
         for (auto & i : readDirectory(path))
@@ -473,24 +483,24 @@ void readFull(int fd, unsigned char * buf, size_t count)
 }
 
 
-void writeFull(int fd, const unsigned char * buf, size_t count)
+void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts)
 {
     while (count) {
-        checkInterrupt();
         ssize_t res = write(fd, (char *) buf, count);
-        if (res == -1) {
-            if (errno == EINTR) continue;
+        if (res == -1 && errno != EINTR)
             throw SysError("writing to file");
+        if (res > 0) {
+            count -= res;
+            buf += res;
         }
-        count -= res;
-        buf += res;
+        if (allowInterrupts) checkInterrupt();
     }
 }
 
 
-void writeFull(int fd, const string & s)
+void writeFull(int fd, const string & s, bool allowInterrupts)
 {
-    writeFull(fd, (const unsigned char *) s.data(), s.size());
+    writeFull(fd, (const unsigned char *) s.data(), s.size(), allowInterrupts);
 }
 
 
@@ -715,20 +725,20 @@ void Pid::kill(bool quiet)
     if (pid == -1 || pid == 0) return;
 
     if (!quiet)
-        printMsg(lvlError, format("killing process %1%") % pid);
+        printError(format("killing process %1%") % pid);
 
     /* Send the requested signal to the child.  If it has its own
        process group, send the signal to every process in the child
        process group (which hopefully includes *all* its children). */
     if (::kill(separatePG ? -pid : pid, killSignal) != 0)
-        printMsg(lvlError, (SysError(format("killing process %1%") % pid).msg()));
+        printError((SysError(format("killing process %1%") % pid).msg()));
 
     /* Wait until the child dies, disregarding the exit status. */
     int status;
     while (waitpid(pid, &status, 0) == -1) {
         checkInterrupt();
         if (errno != EINTR) {
-            printMsg(lvlError,
+            printError(
                 (SysError(format("waiting for process %1%") % pid).msg()));
             break;
         }
@@ -768,6 +778,14 @@ void Pid::setKillSignal(int signal)
 }
 
 
+pid_t Pid::release()
+{
+    pid_t p = pid;
+    pid = -1;
+    return p;
+}
+
+
 void killUser(uid_t uid)
 {
     debug(format("killing all processes running under uid ‘%1%’") % uid);
@@ -918,7 +936,7 @@ string runProgram(Path program, bool searchPath, const Strings & args,
     /* Wait for the child to finish. */
     int status = pid.wait(true);
     if (!statusOk(status))
-        throw ExecError(format("program ‘%1%’ %2%")
+        throw ExecError(status, format("program ‘%1%’ %2%")
             % program % statusToString(status));
 
     return result;
@@ -1087,44 +1105,12 @@ bool hasSuffix(const string & s, const string & suffix)
 }
 
 
-void expect(std::istream & str, const string & s)
+std::string toLower(const std::string & s)
 {
-    char s2[s.size()];
-    str.read(s2, s.size());
-    if (string(s2, s.size()) != s)
-        throw FormatError(format("expected string ‘%1%’") % s);
-}
-
-
-string parseString(std::istream & str)
-{
-    string res;
-    expect(str, "\"");
-    int c;
-    while ((c = str.get()) != '"')
-        if (c == '\\') {
-            c = str.get();
-            if (c == 'n') res += '\n';
-            else if (c == 'r') res += '\r';
-            else if (c == 't') res += '\t';
-            else res += c;
-        }
-        else res += c;
-    return res;
-}
-
-
-bool endOfList(std::istream & str)
-{
-    if (str.peek() == ',') {
-        str.get();
-        return false;
-    }
-    if (str.peek() == ']') {
-        str.get();
-        return true;
-    }
-    return false;
+    std::string r(s);
+    for (auto & c : r)
+        c = std::tolower(c);
+    return r;
 }
 
 
@@ -1148,7 +1134,7 @@ void ignoreException()
     try {
         throw;
     } catch (std::exception & e) {
-        printMsg(lvlError, format("error (ignored): %1%") % e.what());
+        printError(format("error (ignored): %1%") % e.what());
     }
 }
 
@@ -1246,4 +1232,15 @@ string base64Decode(const string & s)
 }
 
 
+void callFailure(const std::function<void(std::exception_ptr exc)> & failure, std::exception_ptr exc)
+{
+    try {
+        failure(exc);
+    } catch (std::exception & e) {
+        printError(format("uncaught exception: %s") % e.what());
+        abort();
+    }
+}
+
+
 }
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 819921dfff1e..50b96f7ed92c 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -8,9 +8,11 @@
 #include <dirent.h>
 #include <unistd.h>
 #include <signal.h>
+
 #include <functional>
 #include <limits>
 #include <cstdio>
+#include <map>
 
 #ifndef HAVE_STRUCT_DIRENT_D_TYPE
 #define DT_UNKNOWN 0
@@ -25,6 +27,9 @@ namespace nix {
 /* Return an environment variable. */
 string getEnv(const string & key, const string & def = "");
 
+/* Get the entire environment. */
+std::map<std::string, std::string> getEnv();
+
 /* Return an absolutized path, resolving paths relative to the
    specified directory, or the current directory otherwise.  The path
    is also canonicalised. */
@@ -120,8 +125,8 @@ void replaceSymlink(const Path & target, const Path & link);
 /* Wrappers arount read()/write() that read/write exactly the
    requested number of bytes. */
 void readFull(int fd, unsigned char * buf, size_t count);
-void writeFull(int fd, const unsigned char * buf, size_t count);
-void writeFull(int fd, const string & s);
+void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts = true);
+void writeFull(int fd, const string & s, bool allowInterrupts = true);
 
 MakeError(EndOfFile, Error)
 
@@ -215,6 +220,7 @@ public:
     int wait(bool block);
     void setSeparatePG(bool separatePG);
     void setKillSignal(int signal);
+    pid_t release();
 };
 
 
@@ -242,7 +248,16 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P
 string runProgram(Path program, bool searchPath = false,
     const Strings & args = Strings(), const string & input = "");
 
-MakeError(ExecError, Error)
+class ExecError : public Error
+{
+public:
+    int status;
+
+    template<typename... Args>
+    ExecError(int status, Args... args)
+        : Error(args...), status(status)
+    { }
+};
 
 /* Convert a list of strings to a null-terminated vector of char
    *'s. The result must not be accessed beyond the lifetime of the
@@ -334,18 +349,8 @@ bool hasPrefix(const string & s, const string & prefix);
 bool hasSuffix(const string & s, const string & suffix);
 
 
-/* Read string `s' from stream `str'. */
-void expect(std::istream & str, const string & s);
-
-MakeError(FormatError, Error)
-
-
-/* Read a C-style string from stream `str'. */
-string parseString(std::istream & str);
-
-
-/* Utility function used to parse legacy ATerms. */
-bool endOfList(std::istream & str);
+/* Convert a string to lower case. */
+std::string toLower(const std::string & s);
 
 
 /* Escape a string that contains octal-encoded escape codes such as
@@ -386,4 +391,44 @@ string get(const T & map, const string & key, const string & def = "")
 }
 
 
+/* Call ‘failure’ with the current exception as argument. If ‘failure’
+   throws an exception, abort the program. */
+void callFailure(const std::function<void(std::exception_ptr exc)> & failure,
+    std::exception_ptr exc = std::current_exception());
+
+
+/* Evaluate the function ‘f’. If it returns a value, call ‘success’
+   with that value as its argument. If it or ‘success’ throws an
+   exception, call ‘failure’. If ‘failure’ throws an exception, abort
+   the program. */
+template<class T>
+void sync2async(
+    const std::function<void(T)> & success,
+    const std::function<void(std::exception_ptr exc)> & failure,
+    const std::function<T()> & f)
+{
+    try {
+        success(f());
+    } catch (...) {
+        callFailure(failure);
+    }
+}
+
+
+/* Call the function ‘success’. If it throws an exception, call
+   ‘failure’. If that throws an exception, abort the program. */
+template<class T>
+void callSuccess(
+    const std::function<void(T)> & success,
+    const std::function<void(std::exception_ptr exc)> & failure,
+    T && arg)
+{
+    try {
+        success(arg);
+    } catch (...) {
+        callFailure(failure);
+    }
+}
+
+
 }