diff options
Diffstat (limited to 'third_party/nix/src/libutil')
33 files changed, 4412 insertions, 5224 deletions
diff --git a/third_party/nix/src/libutil/affinity.cc b/third_party/nix/src/libutil/affinity.cc index 98f8287ada67..faee4926d579 100644 --- a/third_party/nix/src/libutil/affinity.cc +++ b/third_party/nix/src/libutil/affinity.cc @@ -1,6 +1,6 @@ +#include "affinity.hh" #include "types.hh" #include "util.hh" -#include "affinity.hh" #if __linux__ #include <sched.h> @@ -8,48 +8,40 @@ namespace nix { - #if __linux__ static bool didSaveAffinity = false; static cpu_set_t savedAffinity; #endif - -void setAffinityTo(int cpu) -{ +void setAffinityTo(int cpu) { #if __linux__ - if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return; - didSaveAffinity = true; - 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) - printError(format("failed to lock thread to CPU %1%") % cpu); + if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return; + didSaveAffinity = true; + 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) + printError(format("failed to lock thread to CPU %1%") % cpu); #endif } - -int lockToCurrentCPU() -{ +int lockToCurrentCPU() { #if __linux__ - int cpu = sched_getcpu(); - if (cpu != -1) setAffinityTo(cpu); - return cpu; + int cpu = sched_getcpu(); + if (cpu != -1) setAffinityTo(cpu); + return cpu; #else - return -1; + return -1; #endif } - -void restoreAffinity() -{ +void restoreAffinity() { #if __linux__ - if (!didSaveAffinity) return; - if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) - printError("failed to restore affinity %1%"); + if (!didSaveAffinity) return; + if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) + printError("failed to restore affinity %1%"); #endif } - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/affinity.hh b/third_party/nix/src/libutil/affinity.hh index c1bd28e1367a..5e5ef9b0de0d 100644 --- a/third_party/nix/src/libutil/affinity.hh +++ b/third_party/nix/src/libutil/affinity.hh @@ -6,4 +6,4 @@ void setAffinityTo(int cpu); int lockToCurrentCPU(); void restoreAffinity(); -} +} // namespace nix diff --git a/third_party/nix/src/libutil/archive.cc b/third_party/nix/src/libutil/archive.cc index 3aa120270970..9ae5c76db751 100644 --- a/third_party/nix/src/libutil/archive.cc +++ b/third_party/nix/src/libutil/archive.cc @@ -1,32 +1,31 @@ -#include <cerrno> -#include <algorithm> -#include <vector> -#include <map> - -#include <strings.h> // for strcasecmp - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> +#include "archive.hh" #include <dirent.h> #include <fcntl.h> - -#include "archive.hh" -#include "util.hh" +#include <strings.h> // for strcasecmp +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <algorithm> +#include <cerrno> +#include <map> +#include <vector> #include "config.hh" +#include "util.hh" namespace nix { -struct ArchiveSettings : Config -{ - Setting<bool> useCaseHack{this, - #if __APPLE__ - true, - #else - false, - #endif +struct ArchiveSettings : Config { + Setting<bool> useCaseHack { + this, +#if __APPLE__ + true, +#else + false, +#endif "use-case-hack", - "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; + "Whether to enable a Darwin-specific hack for dealing with file name " + "collisions." + }; }; static ArchiveSettings archiveSettings; @@ -37,105 +36,105 @@ const std::string narVersionMagic1 = "nix-archive-1"; static string caseHackSuffix = "~nix~case~hack~"; -PathFilter defaultPathFilter = [](const Path &) { return true; }; - +PathFilter defaultPathFilter = [](const Path&) { return true; }; -static void dumpContents(const Path & path, size_t size, - Sink & sink) -{ - sink << "contents" << size; +static void dumpContents(const Path& path, size_t size, Sink& sink) { + sink << "contents" << size; - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) throw SysError(format("opening file '%1%'") % path); + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + if (!fd) throw SysError(format("opening file '%1%'") % path); - std::vector<unsigned char> buf(65536); - size_t left = size; + std::vector<unsigned char> buf(65536); + size_t left = size; - while (left > 0) { - auto n = std::min(left, buf.size()); - readFull(fd.get(), buf.data(), n); - left -= n; - sink(buf.data(), n); - } + while (left > 0) { + auto n = std::min(left, buf.size()); + readFull(fd.get(), buf.data(), n); + left -= n; + sink(buf.data(), n); + } - writePadding(size, sink); + writePadding(size, sink); } - -static void dump(const Path & path, Sink & sink, PathFilter & filter) -{ - checkInterrupt(); - - struct stat st; - if (lstat(path.c_str(), &st)) - throw SysError(format("getting attributes of path '%1%'") % path); - - sink << "("; - - if (S_ISREG(st.st_mode)) { - sink << "type" << "regular"; - if (st.st_mode & S_IXUSR) - sink << "executable" << ""; - dumpContents(path, (size_t) st.st_size, sink); - } - - else if (S_ISDIR(st.st_mode)) { - sink << "type" << "directory"; - - /* If we're on a case-insensitive system like macOS, undo - the case hack applied by restorePath(). */ - std::map<string, string> unhacked; - for (auto & i : readDirectory(path)) - if (archiveSettings.useCaseHack) { - string name(i.name); - size_t pos = i.name.find(caseHackSuffix); - if (pos != string::npos) { - debug(format("removing case hack suffix from '%1%'") % (path + "/" + i.name)); - name.erase(pos); - } - if (unhacked.find(name) != unhacked.end()) - throw Error(format("file name collision in between '%1%' and '%2%'") - % (path + "/" + unhacked[name]) % (path + "/" + i.name)); - unhacked[name] = i.name; - } else - unhacked[i.name] = i.name; - - for (auto & i : unhacked) - if (filter(path + "/" + i.first)) { - sink << "entry" << "(" << "name" << i.first << "node"; - dump(path + "/" + i.second, sink, filter); - sink << ")"; - } - } - - else if (S_ISLNK(st.st_mode)) - sink << "type" << "symlink" << "target" << readLink(path); - - else throw Error(format("file '%1%' has an unsupported type") % path); - - sink << ")"; +static void dump(const Path& path, Sink& sink, PathFilter& filter) { + checkInterrupt(); + + struct stat st; + if (lstat(path.c_str(), &st)) + throw SysError(format("getting attributes of path '%1%'") % path); + + sink << "("; + + if (S_ISREG(st.st_mode)) { + sink << "type" + << "regular"; + if (st.st_mode & S_IXUSR) + sink << "executable" + << ""; + dumpContents(path, (size_t)st.st_size, sink); + } + + else if (S_ISDIR(st.st_mode)) { + sink << "type" + << "directory"; + + /* If we're on a case-insensitive system like macOS, undo + the case hack applied by restorePath(). */ + std::map<string, string> unhacked; + for (auto& i : readDirectory(path)) + if (archiveSettings.useCaseHack) { + string name(i.name); + size_t pos = i.name.find(caseHackSuffix); + if (pos != string::npos) { + debug(format("removing case hack suffix from '%1%'") % + (path + "/" + i.name)); + name.erase(pos); + } + if (unhacked.find(name) != unhacked.end()) + throw Error(format("file name collision in between '%1%' and '%2%'") % + (path + "/" + unhacked[name]) % (path + "/" + i.name)); + unhacked[name] = i.name; + } else + unhacked[i.name] = i.name; + + for (auto& i : unhacked) + if (filter(path + "/" + i.first)) { + sink << "entry" + << "(" + << "name" << i.first << "node"; + dump(path + "/" + i.second, sink, filter); + sink << ")"; + } + } + + else if (S_ISLNK(st.st_mode)) + sink << "type" + << "symlink" + << "target" << readLink(path); + + else + throw Error(format("file '%1%' has an unsupported type") % path); + + sink << ")"; } - -void dumpPath(const Path & path, Sink & sink, PathFilter & filter) -{ - sink << narVersionMagic1; - dump(path, sink, filter); +void dumpPath(const Path& path, Sink& sink, PathFilter& filter) { + sink << narVersionMagic1; + dump(path, sink, filter); } - -void dumpString(const std::string & s, Sink & sink) -{ - sink << narVersionMagic1 << "(" << "type" << "regular" << "contents" << s << ")"; +void dumpString(const std::string& s, Sink& sink) { + sink << narVersionMagic1 << "(" + << "type" + << "regular" + << "contents" << s << ")"; } - -static SerialisationError badArchive(string s) -{ - return SerialisationError("bad archive: " + s); +static SerialisationError badArchive(string s) { + return SerialisationError("bad archive: " + s); } - #if 0 static void skipGeneric(Source & source) { @@ -146,233 +145,212 @@ static void skipGeneric(Source & source) } #endif +static void parseContents(ParseSink& sink, Source& source, const Path& path) { + unsigned long long size = readLongLong(source); -static void parseContents(ParseSink & sink, Source & source, const Path & path) -{ - unsigned long long size = readLongLong(source); + sink.preallocateContents(size); - sink.preallocateContents(size); + unsigned long long left = size; + std::vector<unsigned char> buf(65536); - unsigned long long left = size; - std::vector<unsigned char> buf(65536); + while (left) { + checkInterrupt(); + auto n = buf.size(); + if ((unsigned long long)n > left) n = left; + source(buf.data(), n); + sink.receiveContents(buf.data(), n); + left -= n; + } + + readPadding(size, source); +} - while (left) { - checkInterrupt(); - auto n = buf.size(); - if ((unsigned long long)n > left) n = left; - source(buf.data(), n); - sink.receiveContents(buf.data(), n); - left -= n; - } +struct CaseInsensitiveCompare { + bool operator()(const string& a, const string& b) const { + return strcasecmp(a.c_str(), b.c_str()) < 0; + } +}; - readPadding(size, source); -} +static void parse(ParseSink& sink, Source& source, const Path& path) { + string s; + s = readString(source); + if (s != "(") throw badArchive("expected open tag"); -struct CaseInsensitiveCompare -{ - bool operator() (const string & a, const string & b) const - { - return strcasecmp(a.c_str(), b.c_str()) < 0; - } -}; + enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown; + std::map<Path, int, CaseInsensitiveCompare> names; -static void parse(ParseSink & sink, Source & source, const Path & path) -{ - string s; + while (1) { + checkInterrupt(); s = readString(source); - if (s != "(") throw badArchive("expected open tag"); - enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown; + if (s == ")") { + break; + } - std::map<Path, int, CaseInsensitiveCompare> names; + else if (s == "type") { + if (type != tpUnknown) throw badArchive("multiple type fields"); + string t = readString(source); - while (1) { - checkInterrupt(); + if (t == "regular") { + type = tpRegular; + sink.createRegularFile(path); + } - s = readString(source); + else if (t == "directory") { + sink.createDirectory(path); + type = tpDirectory; + } - if (s == ")") { - break; - } - - else if (s == "type") { - if (type != tpUnknown) - throw badArchive("multiple type fields"); - string t = readString(source); + else if (t == "symlink") { + type = tpSymlink; + } - if (t == "regular") { - type = tpRegular; - sink.createRegularFile(path); - } + else + throw badArchive("unknown file type " + t); - else if (t == "directory") { - sink.createDirectory(path); - type = tpDirectory; - } + } - else if (t == "symlink") { - type = tpSymlink; - } + else if (s == "contents" && type == tpRegular) { + parseContents(sink, source, path); + } - else throw badArchive("unknown file type " + t); + else if (s == "executable" && type == tpRegular) { + auto s = readString(source); + if (s != "") throw badArchive("executable marker has non-empty value"); + sink.isExecutable(); + } - } + else if (s == "entry" && type == tpDirectory) { + string name, prevName; - else if (s == "contents" && type == tpRegular) { - parseContents(sink, source, path); - } + s = readString(source); + if (s != "(") throw badArchive("expected open tag"); - else if (s == "executable" && type == tpRegular) { - auto s = readString(source); - if (s != "") throw badArchive("executable marker has non-empty value"); - sink.isExecutable(); - } + while (1) { + checkInterrupt(); - else if (s == "entry" && type == tpDirectory) { - string name, prevName; - - s = readString(source); - if (s != "(") throw badArchive("expected open tag"); - - while (1) { - checkInterrupt(); - - s = readString(source); - - if (s == ")") { - break; - } else if (s == "name") { - name = readString(source); - if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos) - throw Error(format("NAR contains invalid file name '%1%'") % name); - if (name <= prevName) - throw Error("NAR directory is not sorted"); - prevName = name; - if (archiveSettings.useCaseHack) { - auto i = names.find(name); - if (i != names.end()) { - debug(format("case collision between '%1%' and '%2%'") % i->first % name); - name += caseHackSuffix; - name += std::to_string(++i->second); - } else - names[name] = 0; - } - } else if (s == "node") { - if (s.empty()) throw badArchive("entry name missing"); - parse(sink, source, path + "/" + name); - } else - throw badArchive("unknown field " + s); - } - } + s = readString(source); - else if (s == "target" && type == tpSymlink) { - string target = readString(source); - sink.createSymlink(path, target); - } + if (s == ")") { + break; + } else if (s == "name") { + name = readString(source); + if (name.empty() || name == "." || name == ".." || + name.find('/') != string::npos || + name.find((char)0) != string::npos) + throw Error(format("NAR contains invalid file name '%1%'") % name); + if (name <= prevName) throw Error("NAR directory is not sorted"); + prevName = name; + if (archiveSettings.useCaseHack) { + auto i = names.find(name); + if (i != names.end()) { + debug(format("case collision between '%1%' and '%2%'") % + i->first % name); + name += caseHackSuffix; + name += std::to_string(++i->second); + } else + names[name] = 0; + } + } else if (s == "node") { + if (s.empty()) throw badArchive("entry name missing"); + parse(sink, source, path + "/" + name); + } else + throw badArchive("unknown field " + s); + } + } - else - throw badArchive("unknown field " + s); + else if (s == "target" && type == tpSymlink) { + string target = readString(source); + sink.createSymlink(path, target); } -} + else + throw badArchive("unknown field " + s); + } +} -void parseDump(ParseSink & sink, Source & source) -{ - string version; - try { - version = readString(source, narVersionMagic1.size()); - } catch (SerialisationError & e) { - /* This generally means the integer at the start couldn't be - decoded. Ignore and throw the exception below. */ - } - if (version != narVersionMagic1) - throw badArchive("input doesn't look like a Nix archive"); - parse(sink, source, ""); +void parseDump(ParseSink& sink, Source& source) { + string version; + try { + version = readString(source, narVersionMagic1.size()); + } catch (SerialisationError& e) { + /* This generally means the integer at the start couldn't be + decoded. Ignore and throw the exception below. */ + } + if (version != narVersionMagic1) + throw badArchive("input doesn't look like a Nix archive"); + parse(sink, source, ""); } +struct RestoreSink : ParseSink { + Path dstPath; + AutoCloseFD fd; -struct RestoreSink : ParseSink -{ - Path dstPath; - AutoCloseFD fd; - - void createDirectory(const Path & path) - { - Path p = dstPath + path; - if (mkdir(p.c_str(), 0777) == -1) - throw SysError(format("creating directory '%1%'") % p); - }; - - void createRegularFile(const Path & path) - { - Path p = dstPath + path; - fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); - if (!fd) throw SysError(format("creating file '%1%'") % p); - } + void createDirectory(const Path& path) { + Path p = dstPath + path; + if (mkdir(p.c_str(), 0777) == -1) + throw SysError(format("creating directory '%1%'") % p); + }; - void isExecutable() - { - struct stat st; - if (fstat(fd.get(), &st) == -1) - throw SysError("fstat"); - if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) - throw SysError("fchmod"); - } + void createRegularFile(const Path& path) { + Path p = dstPath + path; + fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); + if (!fd) throw SysError(format("creating file '%1%'") % p); + } + + void isExecutable() { + struct stat st; + if (fstat(fd.get(), &st) == -1) throw SysError("fstat"); + if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) + throw SysError("fchmod"); + } - void preallocateContents(unsigned long long len) - { + void preallocateContents(unsigned long long len) { #if HAVE_POSIX_FALLOCATE - if (len) { - errno = posix_fallocate(fd.get(), 0, len); - /* Note that EINVAL may indicate that the underlying - filesystem doesn't support preallocation (e.g. on - OpenSolaris). Since preallocation is just an - optimisation, ignore it. */ - if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) - throw SysError(format("preallocating file of %1% bytes") % len); - } -#endif + if (len) { + errno = posix_fallocate(fd.get(), 0, len); + /* Note that EINVAL may indicate that the underlying + filesystem doesn't support preallocation (e.g. on + OpenSolaris). Since preallocation is just an + optimisation, ignore it. */ + if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) + throw SysError(format("preallocating file of %1% bytes") % len); } +#endif + } - void receiveContents(unsigned char * data, unsigned int len) - { - writeFull(fd.get(), data, len); - } + void receiveContents(unsigned char* data, unsigned int len) { + writeFull(fd.get(), data, len); + } - void createSymlink(const Path & path, const string & target) - { - Path p = dstPath + path; - nix::createSymlink(target, p); - } + void createSymlink(const Path& path, const string& target) { + Path p = dstPath + path; + nix::createSymlink(target, p); + } }; - -void restorePath(const Path & path, Source & source) -{ - RestoreSink sink; - sink.dstPath = path; - parseDump(sink, source); +void restorePath(const Path& path, Source& source) { + RestoreSink sink; + sink.dstPath = path; + parseDump(sink, source); } +void copyNAR(Source& source, Sink& sink) { + // FIXME: if 'source' is the output of dumpPath() followed by EOF, + // we should just forward all data directly without parsing. -void copyNAR(Source & source, Sink & sink) -{ - // FIXME: if 'source' is the output of dumpPath() followed by EOF, - // we should just forward all data directly without parsing. - - ParseSink parseSink; /* null sink; just parse the NAR */ + ParseSink parseSink; /* null sink; just parse the NAR */ - LambdaSource wrapper([&](unsigned char * data, size_t len) { - auto n = source.read(data, len); - sink(data, n); - return n; - }); + LambdaSource wrapper([&](unsigned char* data, size_t len) { + auto n = source.read(data, len); + sink(data, n); + return n; + }); - parseDump(parseSink, wrapper); + parseDump(parseSink, wrapper); } - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/archive.hh b/third_party/nix/src/libutil/archive.hh index 25be426c1a4d..9a656edae4a3 100644 --- a/third_party/nix/src/libutil/archive.hh +++ b/third_party/nix/src/libutil/archive.hh @@ -1,12 +1,10 @@ #pragma once -#include "types.hh" #include "serialise.hh" - +#include "types.hh" namespace nix { - /* dumpPath creates a Nix archive of the specified path. The format is as follows: @@ -44,41 +42,36 @@ namespace nix { `+' denotes string concatenation. */ +void dumpPath(const Path& path, Sink& sink, + PathFilter& filter = defaultPathFilter); -void dumpPath(const Path & path, Sink & sink, - PathFilter & filter = defaultPathFilter); - -void dumpString(const std::string & s, Sink & sink); +void dumpString(const std::string& s, Sink& sink); /* FIXME: fix this API, it sucks. */ -struct ParseSink -{ - virtual void createDirectory(const Path & path) { }; +struct ParseSink { + virtual void createDirectory(const Path& path){}; - virtual void createRegularFile(const Path & path) { }; - virtual void isExecutable() { }; - virtual void preallocateContents(unsigned long long size) { }; - virtual void receiveContents(unsigned char * data, unsigned int len) { }; + virtual void createRegularFile(const Path& path){}; + virtual void isExecutable(){}; + virtual void preallocateContents(unsigned long long size){}; + virtual void receiveContents(unsigned char* data, unsigned int len){}; - virtual void createSymlink(const Path & path, const string & target) { }; + virtual void createSymlink(const Path& path, const string& target){}; }; -struct TeeSink : ParseSink -{ - TeeSource source; +struct TeeSink : ParseSink { + TeeSource source; - TeeSink(Source & source) : source(source) { } + TeeSink(Source& source) : source(source) {} }; -void parseDump(ParseSink & sink, Source & source); +void parseDump(ParseSink& sink, Source& source); -void restorePath(const Path & path, Source & source); +void restorePath(const Path& path, Source& source); /* Read a NAR from 'source' and write it to 'sink'. */ -void copyNAR(Source & source, Sink & sink); - +void copyNAR(Source& source, Sink& sink); extern const std::string narVersionMagic1; - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/args.cc b/third_party/nix/src/libutil/args.cc index 7af2a1bf731a..b5bfb0e6a746 100644 --- a/third_party/nix/src/libutil/args.cc +++ b/third_party/nix/src/libutil/args.cc @@ -3,201 +3,184 @@ namespace nix { -Args::FlagMaker Args::mkFlag() -{ - return FlagMaker(*this); -} +Args::FlagMaker Args::mkFlag() { return FlagMaker(*this); } -Args::FlagMaker::~FlagMaker() -{ - assert(flag->longName != ""); - args.longFlags[flag->longName] = flag; - if (flag->shortName) args.shortFlags[flag->shortName] = flag; +Args::FlagMaker::~FlagMaker() { + assert(flag->longName != ""); + args.longFlags[flag->longName] = flag; + if (flag->shortName) args.shortFlags[flag->shortName] = flag; } -void Args::parseCmdline(const Strings & _cmdline) -{ - Strings pendingArgs; - bool dashDash = false; - - Strings cmdline(_cmdline); - - for (auto pos = cmdline.begin(); pos != cmdline.end(); ) { - - auto arg = *pos; - - /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f', - `-j3` -> `-j 3`). */ - if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && isalpha(arg[1])) { - *pos = (string) "-" + arg[1]; - auto next = pos; ++next; - for (unsigned int j = 2; j < arg.length(); j++) - if (isalpha(arg[j])) - cmdline.insert(next, (string) "-" + arg[j]); - else { - cmdline.insert(next, string(arg, j)); - break; - } - arg = *pos; - } - - if (!dashDash && arg == "--") { - dashDash = true; - ++pos; - } - else if (!dashDash && std::string(arg, 0, 1) == "-") { - if (!processFlag(pos, cmdline.end())) - throw UsageError(format("unrecognised flag '%1%'") % arg); - } +void Args::parseCmdline(const Strings& _cmdline) { + Strings pendingArgs; + bool dashDash = false; + + Strings cmdline(_cmdline); + + for (auto pos = cmdline.begin(); pos != cmdline.end();) { + auto arg = *pos; + + /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f', + `-j3` -> `-j 3`). */ + if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && + isalpha(arg[1])) { + *pos = (string) "-" + arg[1]; + auto next = pos; + ++next; + for (unsigned int j = 2; j < arg.length(); j++) + if (isalpha(arg[j])) + cmdline.insert(next, (string) "-" + arg[j]); else { - pendingArgs.push_back(*pos++); - if (processArgs(pendingArgs, false)) - pendingArgs.clear(); + cmdline.insert(next, string(arg, j)); + break; } + arg = *pos; } - processArgs(pendingArgs, true); + if (!dashDash && arg == "--") { + dashDash = true; + ++pos; + } else if (!dashDash && std::string(arg, 0, 1) == "-") { + if (!processFlag(pos, cmdline.end())) + throw UsageError(format("unrecognised flag '%1%'") % arg); + } else { + pendingArgs.push_back(*pos++); + if (processArgs(pendingArgs, false)) pendingArgs.clear(); + } + } + + processArgs(pendingArgs, true); } -void Args::printHelp(const string & programName, std::ostream & out) -{ - std::cout << "Usage: " << programName << " <FLAGS>..."; - for (auto & exp : expectedArgs) { - std::cout << renderLabels({exp.label}); - // FIXME: handle arity > 1 - if (exp.arity == 0) std::cout << "..."; - if (exp.optional) std::cout << "?"; - } - std::cout << "\n"; +void Args::printHelp(const string& programName, std::ostream& out) { + std::cout << "Usage: " << programName << " <FLAGS>..."; + for (auto& exp : expectedArgs) { + std::cout << renderLabels({exp.label}); + // FIXME: handle arity > 1 + if (exp.arity == 0) std::cout << "..."; + if (exp.optional) std::cout << "?"; + } + std::cout << "\n"; - auto s = description(); - if (s != "") - std::cout << "\nSummary: " << s << ".\n"; + auto s = description(); + if (s != "") std::cout << "\nSummary: " << s << ".\n"; - if (longFlags.size()) { - std::cout << "\n"; - std::cout << "Flags:\n"; - printFlags(out); - } + if (longFlags.size()) { + std::cout << "\n"; + std::cout << "Flags:\n"; + printFlags(out); + } } -void Args::printFlags(std::ostream & out) -{ - Table2 table; - for (auto & flag : longFlags) { - if (hiddenCategories.count(flag.second->category)) continue; - table.push_back(std::make_pair( - (flag.second->shortName ? std::string("-") + flag.second->shortName + ", " : " ") - + "--" + flag.first + renderLabels(flag.second->labels), - flag.second->description)); - } - printTable(out, table); +void Args::printFlags(std::ostream& out) { + Table2 table; + for (auto& flag : longFlags) { + if (hiddenCategories.count(flag.second->category)) continue; + table.push_back(std::make_pair( + (flag.second->shortName + ? std::string("-") + flag.second->shortName + ", " + : " ") + + "--" + flag.first + renderLabels(flag.second->labels), + flag.second->description)); + } + printTable(out, table); } -bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) -{ - assert(pos != end); - - auto process = [&](const std::string & name, const Flag & flag) -> bool { - ++pos; - std::vector<std::string> args; - for (size_t n = 0 ; n < flag.arity; ++n) { - if (pos == end) { - if (flag.arity == ArityAny) break; - throw UsageError(format("flag '%1%' requires %2% argument(s)") - % name % flag.arity); - } - args.push_back(*pos++); - } - flag.handler(std::move(args)); - return true; - }; - - if (string(*pos, 0, 2) == "--") { - auto i = longFlags.find(string(*pos, 2)); - if (i == longFlags.end()) return false; - return process("--" + i->first, *i->second); +bool Args::processFlag(Strings::iterator& pos, Strings::iterator end) { + assert(pos != end); + + auto process = [&](const std::string& name, const Flag& flag) -> bool { + ++pos; + std::vector<std::string> args; + for (size_t n = 0; n < flag.arity; ++n) { + if (pos == end) { + if (flag.arity == ArityAny) break; + throw UsageError(format("flag '%1%' requires %2% argument(s)") % name % + flag.arity); + } + args.push_back(*pos++); } - - if (string(*pos, 0, 1) == "-" && pos->size() == 2) { - auto c = (*pos)[1]; - auto i = shortFlags.find(c); - if (i == shortFlags.end()) return false; - return process(std::string("-") + c, *i->second); - } - - return false; + flag.handler(std::move(args)); + return true; + }; + + if (string(*pos, 0, 2) == "--") { + auto i = longFlags.find(string(*pos, 2)); + if (i == longFlags.end()) return false; + return process("--" + i->first, *i->second); + } + + if (string(*pos, 0, 1) == "-" && pos->size() == 2) { + auto c = (*pos)[1]; + auto i = shortFlags.find(c); + if (i == shortFlags.end()) return false; + return process(std::string("-") + c, *i->second); + } + + return false; } -bool Args::processArgs(const Strings & args, bool finish) -{ - if (expectedArgs.empty()) { - if (!args.empty()) - throw UsageError(format("unexpected argument '%1%'") % args.front()); - return true; - } +bool Args::processArgs(const Strings& args, bool finish) { + if (expectedArgs.empty()) { + if (!args.empty()) + throw UsageError(format("unexpected argument '%1%'") % args.front()); + return true; + } - auto & exp = expectedArgs.front(); + auto& exp = expectedArgs.front(); - bool res = false; + bool res = false; - if ((exp.arity == 0 && finish) || - (exp.arity > 0 && args.size() == exp.arity)) - { - std::vector<std::string> ss; - for (auto & s : args) ss.push_back(s); - exp.handler(std::move(ss)); - expectedArgs.pop_front(); - res = true; - } + if ((exp.arity == 0 && finish) || + (exp.arity > 0 && args.size() == exp.arity)) { + std::vector<std::string> ss; + for (auto& s : args) ss.push_back(s); + exp.handler(std::move(ss)); + expectedArgs.pop_front(); + res = true; + } - if (finish && !expectedArgs.empty() && !expectedArgs.front().optional) - throw UsageError("more arguments are required"); + if (finish && !expectedArgs.empty() && !expectedArgs.front().optional) + throw UsageError("more arguments are required"); - return res; + return res; } -Args::FlagMaker & Args::FlagMaker::mkHashTypeFlag(HashType * ht) -{ - arity(1); - label("type"); - description("hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')"); - handler([ht](std::string s) { - *ht = parseHashType(s); - if (*ht == htUnknown) - throw UsageError("unknown hash type '%1%'", s); - }); - return *this; +Args::FlagMaker& Args::FlagMaker::mkHashTypeFlag(HashType* ht) { + arity(1); + label("type"); + description("hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')"); + handler([ht](std::string s) { + *ht = parseHashType(s); + if (*ht == htUnknown) throw UsageError("unknown hash type '%1%'", s); + }); + return *this; } -Strings argvToStrings(int argc, char * * argv) -{ - Strings args; - argc--; argv++; - while (argc--) args.push_back(*argv++); - return args; +Strings argvToStrings(int argc, char** argv) { + Strings args; + argc--; + argv++; + while (argc--) args.push_back(*argv++); + return args; } -std::string renderLabels(const Strings & labels) -{ - std::string res; - for (auto label : labels) { - for (auto & c : label) c = std::toupper(c); - res += " <" + label + ">"; - } - return res; +std::string renderLabels(const Strings& labels) { + std::string res; + for (auto label : labels) { + for (auto& c : label) c = std::toupper(c); + res += " <" + label + ">"; + } + return res; } -void printTable(std::ostream & out, const Table2 & table) -{ - size_t max = 0; - for (auto & row : table) - max = std::max(max, row.first.size()); - for (auto & row : table) { - out << " " << row.first - << std::string(max - row.first.size() + 2, ' ') - << row.second << "\n"; - } +void printTable(std::ostream& out, const Table2& table) { + size_t max = 0; + for (auto& row : table) max = std::max(max, row.first.size()); + for (auto& row : table) { + out << " " << row.first << std::string(max - row.first.size() + 2, ' ') + << row.second << "\n"; + } } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/args.hh b/third_party/nix/src/libutil/args.hh index ad5fcca39418..dc41a6849b40 100644 --- a/third_party/nix/src/libutil/args.hh +++ b/third_party/nix/src/libutil/args.hh @@ -3,7 +3,6 @@ #include <iostream> #include <map> #include <memory> - #include "util.hh" namespace nix { @@ -12,190 +11,206 @@ MakeError(UsageError, Error); enum HashType : char; -class Args -{ -public: +class Args { + public: + /* Parse the command line, throwing a UsageError if something goes + wrong. */ + void parseCmdline(const Strings& cmdline); - /* Parse the command line, throwing a UsageError if something goes - wrong. */ - void parseCmdline(const Strings & cmdline); + virtual void printHelp(const string& programName, std::ostream& out); - virtual void printHelp(const string & programName, std::ostream & out); + virtual std::string description() { return ""; } - virtual std::string description() { return ""; } + protected: + static const size_t ArityAny = std::numeric_limits<size_t>::max(); -protected: + /* Flags. */ + struct Flag { + typedef std::shared_ptr<Flag> ptr; + std::string longName; + char shortName = 0; + std::string description; + Strings labels; + size_t arity = 0; + std::function<void(std::vector<std::string>)> handler; + std::string category; + }; - static const size_t ArityAny = std::numeric_limits<size_t>::max(); + std::map<std::string, Flag::ptr> longFlags; + std::map<char, Flag::ptr> shortFlags; - /* Flags. */ - struct Flag - { - typedef std::shared_ptr<Flag> ptr; - std::string longName; - char shortName = 0; - std::string description; - Strings labels; - size_t arity = 0; - std::function<void(std::vector<std::string>)> handler; - std::string category; - }; + virtual bool processFlag(Strings::iterator& pos, Strings::iterator end); + + virtual void printFlags(std::ostream& out); + + /* Positional arguments. */ + struct ExpectedArg { + std::string label; + size_t arity; // 0 = any + bool optional; + std::function<void(std::vector<std::string>)> handler; + }; - std::map<std::string, Flag::ptr> longFlags; - std::map<char, Flag::ptr> shortFlags; + std::list<ExpectedArg> expectedArgs; - virtual bool processFlag(Strings::iterator & pos, Strings::iterator end); + virtual bool processArgs(const Strings& args, bool finish); - virtual void printFlags(std::ostream & out); + std::set<std::string> hiddenCategories; + + public: + class FlagMaker { + Args& args; + Flag::ptr flag; + friend class Args; + FlagMaker(Args& args) : args(args), flag(std::make_shared<Flag>()){}; + + public: + ~FlagMaker(); + FlagMaker& longName(const std::string& s) { + flag->longName = s; + return *this; + }; + FlagMaker& shortName(char s) { + flag->shortName = s; + return *this; + }; + FlagMaker& description(const std::string& s) { + flag->description = s; + return *this; + }; + FlagMaker& label(const std::string& l) { + flag->arity = 1; + flag->labels = {l}; + return *this; + }; + FlagMaker& labels(const Strings& ls) { + flag->arity = ls.size(); + flag->labels = ls; + return *this; + }; + FlagMaker& arity(size_t arity) { + flag->arity = arity; + return *this; + }; + FlagMaker& handler(std::function<void(std::vector<std::string>)> handler) { + flag->handler = handler; + return *this; + }; + FlagMaker& handler(std::function<void()> handler) { + flag->handler = [handler](std::vector<std::string>) { handler(); }; + return *this; + }; + FlagMaker& handler(std::function<void(std::string)> handler) { + flag->arity = 1; + flag->handler = [handler](std::vector<std::string> ss) { + handler(std::move(ss[0])); + }; + return *this; + }; + FlagMaker& category(const std::string& s) { + flag->category = s; + return *this; + }; - /* Positional arguments. */ - struct ExpectedArg - { - std::string label; - size_t arity; // 0 = any - bool optional; - std::function<void(std::vector<std::string>)> handler; + template <class T> + FlagMaker& dest(T* dest) { + flag->arity = 1; + flag->handler = [=](std::vector<std::string> ss) { *dest = ss[0]; }; + return *this; }; - std::list<ExpectedArg> expectedArgs; - - virtual bool processArgs(const Strings & args, bool finish); - - std::set<std::string> hiddenCategories; - -public: - - class FlagMaker - { - Args & args; - Flag::ptr flag; - friend class Args; - FlagMaker(Args & args) : args(args), flag(std::make_shared<Flag>()) { }; - public: - ~FlagMaker(); - FlagMaker & longName(const std::string & s) { flag->longName = s; return *this; }; - FlagMaker & shortName(char s) { flag->shortName = s; return *this; }; - FlagMaker & description(const std::string & s) { flag->description = s; return *this; }; - FlagMaker & label(const std::string & l) { flag->arity = 1; flag->labels = {l}; return *this; }; - FlagMaker & labels(const Strings & ls) { flag->arity = ls.size(); flag->labels = ls; return *this; }; - FlagMaker & arity(size_t arity) { flag->arity = arity; return *this; }; - FlagMaker & handler(std::function<void(std::vector<std::string>)> handler) { flag->handler = handler; return *this; }; - FlagMaker & handler(std::function<void()> handler) { flag->handler = [handler](std::vector<std::string>) { handler(); }; return *this; }; - FlagMaker & handler(std::function<void(std::string)> handler) { - flag->arity = 1; - flag->handler = [handler](std::vector<std::string> ss) { handler(std::move(ss[0])); }; - return *this; - }; - FlagMaker & category(const std::string & s) { flag->category = s; return *this; }; - - template<class T> - FlagMaker & dest(T * dest) - { - flag->arity = 1; - flag->handler = [=](std::vector<std::string> ss) { *dest = ss[0]; }; - return *this; - }; - - template<class T> - FlagMaker & set(T * dest, const T & val) - { - flag->arity = 0; - flag->handler = [=](std::vector<std::string> ss) { *dest = val; }; - return *this; - }; - - FlagMaker & mkHashTypeFlag(HashType * ht); + template <class T> + FlagMaker& set(T* dest, const T& val) { + flag->arity = 0; + flag->handler = [=](std::vector<std::string> ss) { *dest = val; }; + return *this; }; - FlagMaker mkFlag(); - - /* Helper functions for constructing flags / positional - arguments. */ - - void mkFlag1(char shortName, const std::string & longName, - const std::string & label, const std::string & description, - std::function<void(std::string)> fun) - { - mkFlag() - .shortName(shortName) - .longName(longName) - .labels({label}) - .description(description) - .arity(1) - .handler([=](std::vector<std::string> ss) { fun(ss[0]); }); - } - - void mkFlag(char shortName, const std::string & name, - const std::string & description, bool * dest) - { - mkFlag(shortName, name, description, dest, true); - } - - template<class T> - void mkFlag(char shortName, const std::string & longName, const std::string & description, - T * dest, const T & value) - { - mkFlag() - .shortName(shortName) - .longName(longName) - .description(description) - .handler([=](std::vector<std::string> ss) { *dest = value; }); - } - - template<class I> - void mkIntFlag(char shortName, const std::string & longName, - const std::string & description, I * dest) - { - mkFlag<I>(shortName, longName, description, [=](I n) { - *dest = n; + FlagMaker& mkHashTypeFlag(HashType* ht); + }; + + FlagMaker mkFlag(); + + /* Helper functions for constructing flags / positional + arguments. */ + + void mkFlag1(char shortName, const std::string& longName, + const std::string& label, const std::string& description, + std::function<void(std::string)> fun) { + mkFlag() + .shortName(shortName) + .longName(longName) + .labels({label}) + .description(description) + .arity(1) + .handler([=](std::vector<std::string> ss) { fun(ss[0]); }); + } + + void mkFlag(char shortName, const std::string& name, + const std::string& description, bool* dest) { + mkFlag(shortName, name, description, dest, true); + } + + template <class T> + void mkFlag(char shortName, const std::string& longName, + const std::string& description, T* dest, const T& value) { + mkFlag() + .shortName(shortName) + .longName(longName) + .description(description) + .handler([=](std::vector<std::string> ss) { *dest = value; }); + } + + template <class I> + void mkIntFlag(char shortName, const std::string& longName, + const std::string& description, I* dest) { + mkFlag<I>(shortName, longName, description, [=](I n) { *dest = n; }); + } + + template <class I> + void mkFlag(char shortName, const std::string& longName, + const std::string& description, std::function<void(I)> fun) { + mkFlag() + .shortName(shortName) + .longName(longName) + .labels({"N"}) + .description(description) + .arity(1) + .handler([=](std::vector<std::string> ss) { + I n; + if (!string2Int(ss[0], n)) + throw UsageError("flag '--%s' requires a integer argument", + longName); + fun(n); }); - } - - template<class I> - void mkFlag(char shortName, const std::string & longName, - const std::string & description, std::function<void(I)> fun) - { - mkFlag() - .shortName(shortName) - .longName(longName) - .labels({"N"}) - .description(description) - .arity(1) - .handler([=](std::vector<std::string> ss) { - I n; - if (!string2Int(ss[0], n)) - throw UsageError("flag '--%s' requires a integer argument", longName); - fun(n); - }); - } - - /* Expect a string argument. */ - void expectArg(const std::string & label, string * dest, bool optional = false) - { - expectedArgs.push_back(ExpectedArg{label, 1, optional, [=](std::vector<std::string> ss) { - *dest = ss[0]; - }}); - } - - /* Expect 0 or more arguments. */ - void expectArgs(const std::string & label, std::vector<std::string> * dest) - { - expectedArgs.push_back(ExpectedArg{label, 0, false, [=](std::vector<std::string> ss) { - *dest = std::move(ss); - }}); - } - - friend class MultiCommand; + } + + /* Expect a string argument. */ + void expectArg(const std::string& label, string* dest, + bool optional = false) { + expectedArgs.push_back( + ExpectedArg{label, 1, optional, + [=](std::vector<std::string> ss) { *dest = ss[0]; }}); + } + + /* Expect 0 or more arguments. */ + void expectArgs(const std::string& label, std::vector<std::string>* dest) { + expectedArgs.push_back(ExpectedArg{ + label, 0, false, + [=](std::vector<std::string> ss) { *dest = std::move(ss); }}); + } + + friend class MultiCommand; }; -Strings argvToStrings(int argc, char * * argv); +Strings argvToStrings(int argc, char** argv); /* Helper function for rendering argument labels. */ -std::string renderLabels(const Strings & labels); +std::string renderLabels(const Strings& labels); /* Helper function for printing 2-column tables. */ typedef std::vector<std::pair<std::string, std::string>> Table2; -void printTable(std::ostream & out, const Table2 & table); +void printTable(std::ostream& out, const Table2& table); -} +} // namespace nix diff --git a/third_party/nix/src/libutil/compression.cc b/third_party/nix/src/libutil/compression.cc index 0dd84e32034a..39bde37e0fd4 100644 --- a/third_party/nix/src/libutil/compression.cc +++ b/third_party/nix/src/libutil/compression.cc @@ -1,432 +1,379 @@ #include "compression.hh" -#include "util.hh" -#include "finally.hh" -#include "logging.hh" - -#include <lzma.h> +#include <brotli/decode.h> +#include <brotli/encode.h> #include <bzlib.h> +#include <lzma.h> #include <cstdio> #include <cstring> - -#include <brotli/decode.h> -#include <brotli/encode.h> - #include <iostream> +#include "finally.hh" +#include "logging.hh" +#include "util.hh" namespace nix { // Don't feed brotli too much at once. -struct ChunkedCompressionSink : CompressionSink -{ - uint8_t outbuf[32 * 1024]; - - void write(const unsigned char * data, size_t len) override - { - const size_t CHUNK_SIZE = sizeof(outbuf) << 2; - while (len) { - size_t n = std::min(CHUNK_SIZE, len); - writeInternal(data, n); - data += n; - len -= n; - } +struct ChunkedCompressionSink : CompressionSink { + uint8_t outbuf[32 * 1024]; + + void write(const unsigned char* data, size_t len) override { + const size_t CHUNK_SIZE = sizeof(outbuf) << 2; + while (len) { + size_t n = std::min(CHUNK_SIZE, len); + writeInternal(data, n); + data += n; + len -= n; } + } - virtual void writeInternal(const unsigned char * data, size_t len) = 0; + virtual void writeInternal(const unsigned char* data, size_t len) = 0; }; -struct NoneSink : CompressionSink -{ - Sink & nextSink; - NoneSink(Sink & nextSink) : nextSink(nextSink) { } - void finish() override { flush(); } - void write(const unsigned char * data, size_t len) override { nextSink(data, len); } +struct NoneSink : CompressionSink { + Sink& nextSink; + NoneSink(Sink& nextSink) : nextSink(nextSink) {} + void finish() override { flush(); } + void write(const unsigned char* data, size_t len) override { + nextSink(data, len); + } }; -struct XzDecompressionSink : CompressionSink -{ - Sink & nextSink; - uint8_t outbuf[BUFSIZ]; - lzma_stream strm = LZMA_STREAM_INIT; - bool finished = false; +struct XzDecompressionSink : CompressionSink { + Sink& nextSink; + uint8_t outbuf[BUFSIZ]; + lzma_stream strm = LZMA_STREAM_INIT; + bool finished = false; - XzDecompressionSink(Sink & nextSink) : nextSink(nextSink) - { - lzma_ret ret = lzma_stream_decoder( - &strm, UINT64_MAX, LZMA_CONCATENATED); - if (ret != LZMA_OK) - throw CompressionError("unable to initialise lzma decoder"); + XzDecompressionSink(Sink& nextSink) : nextSink(nextSink) { + lzma_ret ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); + if (ret != LZMA_OK) + throw CompressionError("unable to initialise lzma decoder"); - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - } + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + } - ~XzDecompressionSink() - { - lzma_end(&strm); - } + ~XzDecompressionSink() { lzma_end(&strm); } - void finish() override - { - CompressionSink::flush(); - write(nullptr, 0); - } + void finish() override { + CompressionSink::flush(); + write(nullptr, 0); + } - void write(const unsigned char * data, size_t len) override - { - strm.next_in = data; - strm.avail_in = len; + void write(const unsigned char* data, size_t len) override { + strm.next_in = data; + strm.avail_in = len; - while (!finished && (!data || strm.avail_in)) { - checkInterrupt(); + while (!finished && (!data || strm.avail_in)) { + checkInterrupt(); - lzma_ret ret = lzma_code(&strm, data ? LZMA_RUN : LZMA_FINISH); - if (ret != LZMA_OK && ret != LZMA_STREAM_END) - throw CompressionError("error %d while decompressing xz file", ret); + lzma_ret ret = lzma_code(&strm, data ? LZMA_RUN : LZMA_FINISH); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + throw CompressionError("error %d while decompressing xz file", ret); - finished = ret == LZMA_STREAM_END; + finished = ret == LZMA_STREAM_END; - if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - } - } + if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + } } + } }; -struct BzipDecompressionSink : ChunkedCompressionSink -{ - Sink & nextSink; - bz_stream strm; - bool finished = false; +struct BzipDecompressionSink : ChunkedCompressionSink { + Sink& nextSink; + bz_stream strm; + bool finished = false; - BzipDecompressionSink(Sink & nextSink) : nextSink(nextSink) - { - memset(&strm, 0, sizeof(strm)); - int ret = BZ2_bzDecompressInit(&strm, 0, 0); - if (ret != BZ_OK) - throw CompressionError("unable to initialise bzip2 decoder"); + BzipDecompressionSink(Sink& nextSink) : nextSink(nextSink) { + memset(&strm, 0, sizeof(strm)); + int ret = BZ2_bzDecompressInit(&strm, 0, 0); + if (ret != BZ_OK) + throw CompressionError("unable to initialise bzip2 decoder"); - strm.next_out = (char *) outbuf; - strm.avail_out = sizeof(outbuf); - } + strm.next_out = (char*)outbuf; + strm.avail_out = sizeof(outbuf); + } - ~BzipDecompressionSink() - { - BZ2_bzDecompressEnd(&strm); - } + ~BzipDecompressionSink() { BZ2_bzDecompressEnd(&strm); } - void finish() override - { - flush(); - write(nullptr, 0); - } + void finish() override { + flush(); + write(nullptr, 0); + } - void writeInternal(const unsigned char * data, size_t len) override - { - assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); + void writeInternal(const unsigned char* data, size_t len) override { + assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); - strm.next_in = (char *) data; - strm.avail_in = len; + strm.next_in = (char*)data; + strm.avail_in = len; - while (strm.avail_in) { - checkInterrupt(); + while (strm.avail_in) { + checkInterrupt(); - int ret = BZ2_bzDecompress(&strm); - if (ret != BZ_OK && ret != BZ_STREAM_END) - throw CompressionError("error while decompressing bzip2 file"); + int ret = BZ2_bzDecompress(&strm); + if (ret != BZ_OK && ret != BZ_STREAM_END) + throw CompressionError("error while decompressing bzip2 file"); - finished = ret == BZ_STREAM_END; + finished = ret == BZ_STREAM_END; - if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); - strm.next_out = (char *) outbuf; - strm.avail_out = sizeof(outbuf); - } - } + if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + strm.next_out = (char*)outbuf; + strm.avail_out = sizeof(outbuf); + } } + } }; -struct BrotliDecompressionSink : ChunkedCompressionSink -{ - Sink & nextSink; - BrotliDecoderState * state; - bool finished = false; - - BrotliDecompressionSink(Sink & nextSink) : nextSink(nextSink) - { - state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); - if (!state) - throw CompressionError("unable to initialize brotli decoder"); - } +struct BrotliDecompressionSink : ChunkedCompressionSink { + Sink& nextSink; + BrotliDecoderState* state; + bool finished = false; - ~BrotliDecompressionSink() - { - BrotliDecoderDestroyInstance(state); - } + BrotliDecompressionSink(Sink& nextSink) : nextSink(nextSink) { + state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + if (!state) throw CompressionError("unable to initialize brotli decoder"); + } - void finish() override - { - flush(); - writeInternal(nullptr, 0); - } + ~BrotliDecompressionSink() { BrotliDecoderDestroyInstance(state); } + + void finish() override { + flush(); + writeInternal(nullptr, 0); + } + + void writeInternal(const unsigned char* data, size_t len) override { + const uint8_t* next_in = data; + size_t avail_in = len; + uint8_t* next_out = outbuf; + size_t avail_out = sizeof(outbuf); + + while (!finished && (!data || avail_in)) { + checkInterrupt(); + + if (!BrotliDecoderDecompressStream(state, &avail_in, &next_in, &avail_out, + &next_out, nullptr)) + throw CompressionError("error while decompressing brotli file"); + + if (avail_out < sizeof(outbuf) || avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - avail_out); + next_out = outbuf; + avail_out = sizeof(outbuf); + } - void writeInternal(const unsigned char * data, size_t len) override - { - const uint8_t * next_in = data; - size_t avail_in = len; - uint8_t * next_out = outbuf; - size_t avail_out = sizeof(outbuf); - - while (!finished && (!data || avail_in)) { - checkInterrupt(); - - if (!BrotliDecoderDecompressStream(state, - &avail_in, &next_in, - &avail_out, &next_out, - nullptr)) - throw CompressionError("error while decompressing brotli file"); - - if (avail_out < sizeof(outbuf) || avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - avail_out); - next_out = outbuf; - avail_out = sizeof(outbuf); - } - - finished = BrotliDecoderIsFinished(state); - } + finished = BrotliDecoderIsFinished(state); } + } }; -ref<std::string> decompress(const std::string & method, const std::string & in) -{ - StringSink ssink; - auto sink = makeDecompressionSink(method, ssink); - (*sink)(in); - sink->finish(); - return ssink.s; +ref<std::string> decompress(const std::string& method, const std::string& in) { + StringSink ssink; + auto sink = makeDecompressionSink(method, ssink); + (*sink)(in); + sink->finish(); + return ssink.s; } -ref<CompressionSink> makeDecompressionSink(const std::string & method, Sink & nextSink) -{ - if (method == "none" || method == "") - return make_ref<NoneSink>(nextSink); - else if (method == "xz") - return make_ref<XzDecompressionSink>(nextSink); - else if (method == "bzip2") - return make_ref<BzipDecompressionSink>(nextSink); - else if (method == "br") - return make_ref<BrotliDecompressionSink>(nextSink); - else - throw UnknownCompressionMethod("unknown compression method '%s'", method); +ref<CompressionSink> makeDecompressionSink(const std::string& method, + Sink& nextSink) { + if (method == "none" || method == "") + return make_ref<NoneSink>(nextSink); + else if (method == "xz") + return make_ref<XzDecompressionSink>(nextSink); + else if (method == "bzip2") + return make_ref<BzipDecompressionSink>(nextSink); + else if (method == "br") + return make_ref<BrotliDecompressionSink>(nextSink); + else + throw UnknownCompressionMethod("unknown compression method '%s'", method); } -struct XzCompressionSink : CompressionSink -{ - Sink & nextSink; - uint8_t outbuf[BUFSIZ]; - lzma_stream strm = LZMA_STREAM_INIT; - bool finished = false; +struct XzCompressionSink : CompressionSink { + Sink& nextSink; + uint8_t outbuf[BUFSIZ]; + lzma_stream strm = LZMA_STREAM_INIT; + bool finished = false; - XzCompressionSink(Sink & nextSink, bool parallel) : nextSink(nextSink) - { - lzma_ret ret; - bool done = false; + XzCompressionSink(Sink& nextSink, bool parallel) : nextSink(nextSink) { + lzma_ret ret; + bool done = false; - if (parallel) { + if (parallel) { #ifdef HAVE_LZMA_MT - lzma_mt mt_options = {}; - mt_options.flags = 0; - mt_options.timeout = 300; // Using the same setting as the xz cmd line - mt_options.preset = LZMA_PRESET_DEFAULT; - mt_options.filters = NULL; - mt_options.check = LZMA_CHECK_CRC64; - mt_options.threads = lzma_cputhreads(); - mt_options.block_size = 0; - if (mt_options.threads == 0) - mt_options.threads = 1; - // FIXME: maybe use lzma_stream_encoder_mt_memusage() to control the - // number of threads. - ret = lzma_stream_encoder_mt(&strm, &mt_options); - done = true; + lzma_mt mt_options = {}; + mt_options.flags = 0; + mt_options.timeout = 300; // Using the same setting as the xz cmd line + mt_options.preset = LZMA_PRESET_DEFAULT; + mt_options.filters = NULL; + mt_options.check = LZMA_CHECK_CRC64; + mt_options.threads = lzma_cputhreads(); + mt_options.block_size = 0; + if (mt_options.threads == 0) mt_options.threads = 1; + // FIXME: maybe use lzma_stream_encoder_mt_memusage() to control the + // number of threads. + ret = lzma_stream_encoder_mt(&strm, &mt_options); + done = true; #else - printMsg(lvlError, "warning: parallel XZ compression requested but not supported, falling back to single-threaded compression"); + printMsg(lvlError, + "warning: parallel XZ compression requested but not supported, " + "falling back to single-threaded compression"); #endif - } + } - if (!done) - ret = lzma_easy_encoder(&strm, 6, LZMA_CHECK_CRC64); + if (!done) ret = lzma_easy_encoder(&strm, 6, LZMA_CHECK_CRC64); - if (ret != LZMA_OK) - throw CompressionError("unable to initialise lzma encoder"); + if (ret != LZMA_OK) + throw CompressionError("unable to initialise lzma encoder"); - // FIXME: apply the x86 BCJ filter? + // FIXME: apply the x86 BCJ filter? - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - } + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + } - ~XzCompressionSink() - { - lzma_end(&strm); - } + ~XzCompressionSink() { lzma_end(&strm); } - void finish() override - { - CompressionSink::flush(); - write(nullptr, 0); - } + void finish() override { + CompressionSink::flush(); + write(nullptr, 0); + } - void write(const unsigned char * data, size_t len) override - { - strm.next_in = data; - strm.avail_in = len; + void write(const unsigned char* data, size_t len) override { + strm.next_in = data; + strm.avail_in = len; - while (!finished && (!data || strm.avail_in)) { - checkInterrupt(); + while (!finished && (!data || strm.avail_in)) { + checkInterrupt(); - lzma_ret ret = lzma_code(&strm, data ? LZMA_RUN : LZMA_FINISH); - if (ret != LZMA_OK && ret != LZMA_STREAM_END) - throw CompressionError("error %d while compressing xz file", ret); + lzma_ret ret = lzma_code(&strm, data ? LZMA_RUN : LZMA_FINISH); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + throw CompressionError("error %d while compressing xz file", ret); - finished = ret == LZMA_STREAM_END; + finished = ret == LZMA_STREAM_END; - if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - } - } + if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + } } + } }; -struct BzipCompressionSink : ChunkedCompressionSink -{ - Sink & nextSink; - bz_stream strm; - bool finished = false; +struct BzipCompressionSink : ChunkedCompressionSink { + Sink& nextSink; + bz_stream strm; + bool finished = false; - BzipCompressionSink(Sink & nextSink) : nextSink(nextSink) - { - memset(&strm, 0, sizeof(strm)); - int ret = BZ2_bzCompressInit(&strm, 9, 0, 30); - if (ret != BZ_OK) - throw CompressionError("unable to initialise bzip2 encoder"); + BzipCompressionSink(Sink& nextSink) : nextSink(nextSink) { + memset(&strm, 0, sizeof(strm)); + int ret = BZ2_bzCompressInit(&strm, 9, 0, 30); + if (ret != BZ_OK) + throw CompressionError("unable to initialise bzip2 encoder"); - strm.next_out = (char *) outbuf; - strm.avail_out = sizeof(outbuf); - } + strm.next_out = (char*)outbuf; + strm.avail_out = sizeof(outbuf); + } - ~BzipCompressionSink() - { - BZ2_bzCompressEnd(&strm); - } + ~BzipCompressionSink() { BZ2_bzCompressEnd(&strm); } - void finish() override - { - flush(); - writeInternal(nullptr, 0); - } + void finish() override { + flush(); + writeInternal(nullptr, 0); + } - void writeInternal(const unsigned char * data, size_t len) override - { - assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); + void writeInternal(const unsigned char* data, size_t len) override { + assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); - strm.next_in = (char *) data; - strm.avail_in = len; + strm.next_in = (char*)data; + strm.avail_in = len; - while (!finished && (!data || strm.avail_in)) { - checkInterrupt(); + while (!finished && (!data || strm.avail_in)) { + checkInterrupt(); - int ret = BZ2_bzCompress(&strm, data ? BZ_RUN : BZ_FINISH); - if (ret != BZ_RUN_OK && ret != BZ_FINISH_OK && ret != BZ_STREAM_END) - throw CompressionError("error %d while compressing bzip2 file", ret); + int ret = BZ2_bzCompress(&strm, data ? BZ_RUN : BZ_FINISH); + if (ret != BZ_RUN_OK && ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + throw CompressionError("error %d while compressing bzip2 file", ret); - finished = ret == BZ_STREAM_END; + finished = ret == BZ_STREAM_END; - if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); - strm.next_out = (char *) outbuf; - strm.avail_out = sizeof(outbuf); - } - } + if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + strm.next_out = (char*)outbuf; + strm.avail_out = sizeof(outbuf); + } } + } }; -struct BrotliCompressionSink : ChunkedCompressionSink -{ - Sink & nextSink; - uint8_t outbuf[BUFSIZ]; - BrotliEncoderState *state; - bool finished = false; - - BrotliCompressionSink(Sink & nextSink) : nextSink(nextSink) - { - state = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); - if (!state) - throw CompressionError("unable to initialise brotli encoder"); - } - - ~BrotliCompressionSink() - { - BrotliEncoderDestroyInstance(state); - } - - void finish() override - { - flush(); - writeInternal(nullptr, 0); - } - - void writeInternal(const unsigned char * data, size_t len) override - { - const uint8_t * next_in = data; - size_t avail_in = len; - uint8_t * next_out = outbuf; - size_t avail_out = sizeof(outbuf); - - while (!finished && (!data || avail_in)) { - checkInterrupt(); - - if (!BrotliEncoderCompressStream(state, - data ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, - &avail_in, &next_in, - &avail_out, &next_out, - nullptr)) - throw CompressionError("error while compressing brotli compression"); - - if (avail_out < sizeof(outbuf) || avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - avail_out); - next_out = outbuf; - avail_out = sizeof(outbuf); - } - - finished = BrotliEncoderIsFinished(state); - } +struct BrotliCompressionSink : ChunkedCompressionSink { + Sink& nextSink; + uint8_t outbuf[BUFSIZ]; + BrotliEncoderState* state; + bool finished = false; + + BrotliCompressionSink(Sink& nextSink) : nextSink(nextSink) { + state = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + if (!state) throw CompressionError("unable to initialise brotli encoder"); + } + + ~BrotliCompressionSink() { BrotliEncoderDestroyInstance(state); } + + void finish() override { + flush(); + writeInternal(nullptr, 0); + } + + void writeInternal(const unsigned char* data, size_t len) override { + const uint8_t* next_in = data; + size_t avail_in = len; + uint8_t* next_out = outbuf; + size_t avail_out = sizeof(outbuf); + + while (!finished && (!data || avail_in)) { + checkInterrupt(); + + if (!BrotliEncoderCompressStream( + state, data ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, + &avail_in, &next_in, &avail_out, &next_out, nullptr)) + throw CompressionError("error while compressing brotli compression"); + + if (avail_out < sizeof(outbuf) || avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - avail_out); + next_out = outbuf; + avail_out = sizeof(outbuf); + } + + finished = BrotliEncoderIsFinished(state); } + } }; -ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel) -{ - if (method == "none") - return make_ref<NoneSink>(nextSink); - else if (method == "xz") - return make_ref<XzCompressionSink>(nextSink, parallel); - else if (method == "bzip2") - return make_ref<BzipCompressionSink>(nextSink); - else if (method == "br") - return make_ref<BrotliCompressionSink>(nextSink); - else - throw UnknownCompressionMethod(format("unknown compression method '%s'") % method); +ref<CompressionSink> makeCompressionSink(const std::string& method, + Sink& nextSink, const bool parallel) { + if (method == "none") + return make_ref<NoneSink>(nextSink); + else if (method == "xz") + return make_ref<XzCompressionSink>(nextSink, parallel); + else if (method == "bzip2") + return make_ref<BzipCompressionSink>(nextSink); + else if (method == "br") + return make_ref<BrotliCompressionSink>(nextSink); + else + throw UnknownCompressionMethod(format("unknown compression method '%s'") % + method); } -ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel) -{ - StringSink ssink; - auto sink = makeCompressionSink(method, ssink, parallel); - (*sink)(in); - sink->finish(); - return ssink.s; +ref<std::string> compress(const std::string& method, const std::string& in, + const bool parallel) { + StringSink ssink; + auto sink = makeCompressionSink(method, ssink, parallel); + (*sink)(in); + sink->finish(); + return ssink.s; } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/compression.hh b/third_party/nix/src/libutil/compression.hh index dd666a4e19fd..405863f3be02 100644 --- a/third_party/nix/src/libutil/compression.hh +++ b/third_party/nix/src/libutil/compression.hh @@ -1,28 +1,30 @@ #pragma once +#include <string> #include "ref.hh" -#include "types.hh" #include "serialise.hh" - -#include <string> +#include "types.hh" namespace nix { -struct CompressionSink : BufferedSink -{ - virtual void finish() = 0; +struct CompressionSink : BufferedSink { + virtual void finish() = 0; }; -ref<std::string> decompress(const std::string & method, const std::string & in); +ref<std::string> decompress(const std::string& method, const std::string& in); -ref<CompressionSink> makeDecompressionSink(const std::string & method, Sink & nextSink); +ref<CompressionSink> makeDecompressionSink(const std::string& method, + Sink& nextSink); -ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel = false); +ref<std::string> compress(const std::string& method, const std::string& in, + const bool parallel = false); -ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false); +ref<CompressionSink> makeCompressionSink(const std::string& method, + Sink& nextSink, + const bool parallel = false); MakeError(UnknownCompressionMethod, Error); MakeError(CompressionError, Error); -} +} // namespace nix diff --git a/third_party/nix/src/libutil/config.cc b/third_party/nix/src/libutil/config.cc index 9023cb1bb6de..d737945924e8 100644 --- a/third_party/nix/src/libutil/config.cc +++ b/third_party/nix/src/libutil/config.cc @@ -4,268 +4,251 @@ namespace nix { -bool Config::set(const std::string & name, const std::string & value) -{ - auto i = _settings.find(name); - if (i == _settings.end()) return false; - i->second.setting->set(value); - i->second.setting->overriden = true; - return true; -} - -void Config::addSetting(AbstractSetting * setting) -{ - _settings.emplace(setting->name, Config::SettingData(false, setting)); - for (auto & alias : setting->aliases) - _settings.emplace(alias, Config::SettingData(true, setting)); - - bool set = false; - - auto i = unknownSettings.find(setting->name); +bool Config::set(const std::string& name, const std::string& value) { + auto i = _settings.find(name); + if (i == _settings.end()) return false; + i->second.setting->set(value); + i->second.setting->overriden = true; + return true; +} + +void Config::addSetting(AbstractSetting* setting) { + _settings.emplace(setting->name, Config::SettingData(false, setting)); + for (auto& alias : setting->aliases) + _settings.emplace(alias, Config::SettingData(true, setting)); + + bool set = false; + + auto i = unknownSettings.find(setting->name); + if (i != unknownSettings.end()) { + setting->set(i->second); + setting->overriden = true; + unknownSettings.erase(i); + set = true; + } + + for (auto& alias : setting->aliases) { + auto i = unknownSettings.find(alias); if (i != unknownSettings.end()) { + if (set) + warn("setting '%s' is set, but it's an alias of '%s' which is also set", + alias, setting->name); + else { setting->set(i->second); setting->overriden = true; unknownSettings.erase(i); set = true; + } } - - for (auto & alias : setting->aliases) { - auto i = unknownSettings.find(alias); - if (i != unknownSettings.end()) { - if (set) - warn("setting '%s' is set, but it's an alias of '%s' which is also set", - alias, setting->name); - else { - setting->set(i->second); - setting->overriden = true; - unknownSettings.erase(i); - set = true; - } - } - } + } } -void AbstractConfig::warnUnknownSettings() -{ - for (auto & s : unknownSettings) - warn("unknown setting '%s'", s.first); +void AbstractConfig::warnUnknownSettings() { + for (auto& s : unknownSettings) warn("unknown setting '%s'", s.first); } -void AbstractConfig::reapplyUnknownSettings() -{ - auto unknownSettings2 = std::move(unknownSettings); - for (auto & s : unknownSettings2) - set(s.first, s.second); +void AbstractConfig::reapplyUnknownSettings() { + auto unknownSettings2 = std::move(unknownSettings); + for (auto& s : unknownSettings2) set(s.first, s.second); } -void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly) -{ - for (auto & opt : _settings) - if (!opt.second.isAlias && (!overridenOnly || opt.second.setting->overriden)) - res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description}); +void Config::getSettings(std::map<std::string, SettingInfo>& res, + bool overridenOnly) { + for (auto& opt : _settings) + if (!opt.second.isAlias && + (!overridenOnly || opt.second.setting->overriden)) + res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), + opt.second.setting->description}); } -void AbstractConfig::applyConfigFile(const Path & path) -{ - try { - string contents = readFile(path); - - unsigned int pos = 0; - - while (pos < contents.size()) { - string line; - while (pos < contents.size() && contents[pos] != '\n') - line += contents[pos++]; - pos++; - - string::size_type hash = line.find('#'); - if (hash != string::npos) - line = string(line, 0, hash); - - vector<string> tokens = tokenizeString<vector<string> >(line); - if (tokens.empty()) continue; - - if (tokens.size() < 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); - - auto include = false; - auto ignoreMissing = false; - if (tokens[0] == "include") - include = true; - else if (tokens[0] == "!include") { - include = true; - ignoreMissing = true; - } - - if (include) { - if (tokens.size() != 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); - auto p = absPath(tokens[1], dirOf(path)); - if (pathExists(p)) { - applyConfigFile(p); - } else if (!ignoreMissing) { - throw Error("file '%1%' included from '%2%' not found", p, path); - } - continue; - } - - if (tokens[1] != "=") - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); - - string name = tokens[0]; - - vector<string>::iterator i = tokens.begin(); - advance(i, 2); - - set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow - }; - } catch (SysError &) { } -} +void AbstractConfig::applyConfigFile(const Path& path) { + try { + string contents = readFile(path); -void Config::resetOverriden() -{ - for (auto & s : _settings) - s.second.setting->overriden = false; -} + unsigned int pos = 0; + + while (pos < contents.size()) { + string line; + while (pos < contents.size() && contents[pos] != '\n') + line += contents[pos++]; + pos++; + + string::size_type hash = line.find('#'); + if (hash != string::npos) line = string(line, 0, hash); + + vector<string> tokens = tokenizeString<vector<string> >(line); + if (tokens.empty()) continue; + + if (tokens.size() < 2) + throw UsageError("illegal configuration line '%1%' in '%2%'", line, + path); -void Config::toJSON(JSONObject & out) -{ - for (auto & s : _settings) - if (!s.second.isAlias) { - JSONObject out2(out.object(s.first)); - out2.attr("description", s.second.setting->description); - JSONPlaceholder out3(out2.placeholder("value")); - s.second.setting->toJSON(out3); + auto include = false; + auto ignoreMissing = false; + if (tokens[0] == "include") + include = true; + else if (tokens[0] == "!include") { + include = true; + ignoreMissing = true; + } + + if (include) { + if (tokens.size() != 2) + throw UsageError("illegal configuration line '%1%' in '%2%'", line, + path); + auto p = absPath(tokens[1], dirOf(path)); + if (pathExists(p)) { + applyConfigFile(p); + } else if (!ignoreMissing) { + throw Error("file '%1%' included from '%2%' not found", p, path); } -} + continue; + } + + if (tokens[1] != "=") + throw UsageError("illegal configuration line '%1%' in '%2%'", line, + path); -void Config::convertToArgs(Args & args, const std::string & category) -{ - for (auto & s : _settings) - if (!s.second.isAlias) - s.second.setting->convertToArg(args, category); + string name = tokens[0]; + + vector<string>::iterator i = tokens.begin(); + advance(i, 2); + + set(name, + concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow + }; + } catch (SysError&) { + } } -AbstractSetting::AbstractSetting( - const std::string & name, - const std::string & description, - const std::set<std::string> & aliases) - : name(name), description(description), aliases(aliases) -{ +void Config::resetOverriden() { + for (auto& s : _settings) s.second.setting->overriden = false; } -void AbstractSetting::toJSON(JSONPlaceholder & out) -{ - out.write(to_string()); +void Config::toJSON(JSONObject& out) { + for (auto& s : _settings) + if (!s.second.isAlias) { + JSONObject out2(out.object(s.first)); + out2.attr("description", s.second.setting->description); + JSONPlaceholder out3(out2.placeholder("value")); + s.second.setting->toJSON(out3); + } } -void AbstractSetting::convertToArg(Args & args, const std::string & category) -{ +void Config::convertToArgs(Args& args, const std::string& category) { + for (auto& s : _settings) + if (!s.second.isAlias) s.second.setting->convertToArg(args, category); } -template<typename T> -void BaseSetting<T>::toJSON(JSONPlaceholder & out) -{ - out.write(value); +AbstractSetting::AbstractSetting(const std::string& name, + const std::string& description, + const std::set<std::string>& aliases) + : name(name), description(description), aliases(aliases) {} + +void AbstractSetting::toJSON(JSONPlaceholder& out) { out.write(to_string()); } + +void AbstractSetting::convertToArg(Args& args, const std::string& category) {} + +template <typename T> +void BaseSetting<T>::toJSON(JSONPlaceholder& out) { + out.write(value); } -template<typename T> -void BaseSetting<T>::convertToArg(Args & args, const std::string & category) -{ - args.mkFlag() - .longName(name) - .description(description) - .arity(1) - .handler([=](std::vector<std::string> ss) { overriden = true; set(ss[0]); }) - .category(category); +template <typename T> +void BaseSetting<T>::convertToArg(Args& args, const std::string& category) { + args.mkFlag() + .longName(name) + .description(description) + .arity(1) + .handler([=](std::vector<std::string> ss) { + overriden = true; + set(ss[0]); + }) + .category(category); } -template<> void BaseSetting<std::string>::set(const std::string & str) -{ - value = str; +template <> +void BaseSetting<std::string>::set(const std::string& str) { + value = str; } -template<> std::string BaseSetting<std::string>::to_string() -{ - return value; +template <> +std::string BaseSetting<std::string>::to_string() { + return value; } -template<typename T> -void BaseSetting<T>::set(const std::string & str) -{ - static_assert(std::is_integral<T>::value, "Integer required."); - if (!string2Int(str, value)) - throw UsageError("setting '%s' has invalid value '%s'", name, str); +template <typename T> +void BaseSetting<T>::set(const std::string& str) { + static_assert(std::is_integral<T>::value, "Integer required."); + if (!string2Int(str, value)) + throw UsageError("setting '%s' has invalid value '%s'", name, str); } -template<typename T> -std::string BaseSetting<T>::to_string() -{ - static_assert(std::is_integral<T>::value, "Integer required."); - return std::to_string(value); +template <typename T> +std::string BaseSetting<T>::to_string() { + static_assert(std::is_integral<T>::value, "Integer required."); + return std::to_string(value); } -template<> void BaseSetting<bool>::set(const std::string & str) -{ - if (str == "true" || str == "yes" || str == "1") - value = true; - else if (str == "false" || str == "no" || str == "0") - value = false; - else - throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); +template <> +void BaseSetting<bool>::set(const std::string& str) { + if (str == "true" || str == "yes" || str == "1") + value = true; + else if (str == "false" || str == "no" || str == "0") + value = false; + else + throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); } -template<> std::string BaseSetting<bool>::to_string() -{ - return value ? "true" : "false"; +template <> +std::string BaseSetting<bool>::to_string() { + return value ? "true" : "false"; } -template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & category) -{ - args.mkFlag() - .longName(name) - .description(description) - .handler([=](std::vector<std::string> ss) { override(true); }) - .category(category); - args.mkFlag() - .longName("no-" + name) - .description(description) - .handler([=](std::vector<std::string> ss) { override(false); }) - .category(category); +template <> +void BaseSetting<bool>::convertToArg(Args& args, const std::string& category) { + args.mkFlag() + .longName(name) + .description(description) + .handler([=](std::vector<std::string> ss) { override(true); }) + .category(category); + args.mkFlag() + .longName("no-" + name) + .description(description) + .handler([=](std::vector<std::string> ss) { override(false); }) + .category(category); } -template<> void BaseSetting<Strings>::set(const std::string & str) -{ - value = tokenizeString<Strings>(str); +template <> +void BaseSetting<Strings>::set(const std::string& str) { + value = tokenizeString<Strings>(str); } -template<> std::string BaseSetting<Strings>::to_string() -{ - return concatStringsSep(" ", value); +template <> +std::string BaseSetting<Strings>::to_string() { + return concatStringsSep(" ", value); } -template<> void BaseSetting<Strings>::toJSON(JSONPlaceholder & out) -{ - JSONList list(out.list()); - for (auto & s : value) - list.elem(s); +template <> +void BaseSetting<Strings>::toJSON(JSONPlaceholder& out) { + JSONList list(out.list()); + for (auto& s : value) list.elem(s); } -template<> void BaseSetting<StringSet>::set(const std::string & str) -{ - value = tokenizeString<StringSet>(str); +template <> +void BaseSetting<StringSet>::set(const std::string& str) { + value = tokenizeString<StringSet>(str); } -template<> std::string BaseSetting<StringSet>::to_string() -{ - return concatStringsSep(" ", value); +template <> +std::string BaseSetting<StringSet>::to_string() { + return concatStringsSep(" ", value); } -template<> void BaseSetting<StringSet>::toJSON(JSONPlaceholder & out) -{ - JSONList list(out.list()); - for (auto & s : value) - list.elem(s); +template <> +void BaseSetting<StringSet>::toJSON(JSONPlaceholder& out) { + JSONList list(out.list()); + for (auto& s : value) list.elem(s); } template class BaseSetting<int>; @@ -279,60 +262,51 @@ template class BaseSetting<std::string>; template class BaseSetting<Strings>; template class BaseSetting<StringSet>; -void PathSetting::set(const std::string & str) -{ - if (str == "") { - if (allowEmpty) - value = ""; - else - throw UsageError("setting '%s' cannot be empty", name); - } else - value = canonPath(str); +void PathSetting::set(const std::string& str) { + if (str == "") { + if (allowEmpty) + value = ""; + else + throw UsageError("setting '%s' cannot be empty", name); + } else + value = canonPath(str); } -bool GlobalConfig::set(const std::string & name, const std::string & value) -{ - for (auto & config : *configRegistrations) - if (config->set(name, value)) return true; +bool GlobalConfig::set(const std::string& name, const std::string& value) { + for (auto& config : *configRegistrations) + if (config->set(name, value)) return true; - unknownSettings.emplace(name, value); + unknownSettings.emplace(name, value); - return false; + return false; } -void GlobalConfig::getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly) -{ - for (auto & config : *configRegistrations) - config->getSettings(res, overridenOnly); +void GlobalConfig::getSettings(std::map<std::string, SettingInfo>& res, + bool overridenOnly) { + for (auto& config : *configRegistrations) + config->getSettings(res, overridenOnly); } -void GlobalConfig::resetOverriden() -{ - for (auto & config : *configRegistrations) - config->resetOverriden(); +void GlobalConfig::resetOverriden() { + for (auto& config : *configRegistrations) config->resetOverriden(); } -void GlobalConfig::toJSON(JSONObject & out) -{ - for (auto & config : *configRegistrations) - config->toJSON(out); +void GlobalConfig::toJSON(JSONObject& out) { + for (auto& config : *configRegistrations) config->toJSON(out); } -void GlobalConfig::convertToArgs(Args & args, const std::string & category) -{ - for (auto & config : *configRegistrations) - config->convertToArgs(args, category); +void GlobalConfig::convertToArgs(Args& args, const std::string& category) { + for (auto& config : *configRegistrations) + config->convertToArgs(args, category); } GlobalConfig globalConfig; -GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations; +GlobalConfig::ConfigRegistrations* GlobalConfig::configRegistrations; -GlobalConfig::Register::Register(Config * config) -{ - if (!configRegistrations) - configRegistrations = new ConfigRegistrations; - configRegistrations->emplace_back(config); +GlobalConfig::Register::Register(Config* config) { + if (!configRegistrations) configRegistrations = new ConfigRegistrations; + configRegistrations->emplace_back(config); } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/config.hh b/third_party/nix/src/libutil/config.hh index d86c65ff033a..375fefa52b7c 100644 --- a/third_party/nix/src/libutil/config.hh +++ b/third_party/nix/src/libutil/config.hh @@ -1,6 +1,5 @@ #include <map> #include <set> - #include "types.hh" #pragma once @@ -12,38 +11,34 @@ class AbstractSetting; class JSONPlaceholder; class JSONObject; -class AbstractConfig -{ -protected: - StringMap unknownSettings; - - AbstractConfig(const StringMap & initials = {}) - : unknownSettings(initials) - { } +class AbstractConfig { + protected: + StringMap unknownSettings; -public: + AbstractConfig(const StringMap& initials = {}) : unknownSettings(initials) {} - virtual bool set(const std::string & name, const std::string & value) = 0; + public: + virtual bool set(const std::string& name, const std::string& value) = 0; - struct SettingInfo - { - std::string value; - std::string description; - }; + struct SettingInfo { + std::string value; + std::string description; + }; - virtual void getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly = false) = 0; + virtual void getSettings(std::map<std::string, SettingInfo>& res, + bool overridenOnly = false) = 0; - void applyConfigFile(const Path & path); + void applyConfigFile(const Path& path); - virtual void resetOverriden() = 0; + virtual void resetOverriden() = 0; - virtual void toJSON(JSONObject & out) = 0; + virtual void toJSON(JSONObject& out) = 0; - virtual void convertToArgs(Args & args, const std::string & category) = 0; + virtual void convertToArgs(Args& args, const std::string& category) = 0; - void warnUnknownSettings(); + void warnUnknownSettings(); - void reapplyUnknownSettings(); + void reapplyUnknownSettings(); }; /* A class to simplify providing configuration settings. The typical @@ -61,201 +56,171 @@ public: }; */ -class Config : public AbstractConfig -{ - friend class AbstractSetting; - -public: - - struct SettingData - { - bool isAlias; - AbstractSetting * setting; - SettingData(bool isAlias, AbstractSetting * setting) - : isAlias(isAlias), setting(setting) - { } - }; +class Config : public AbstractConfig { + friend class AbstractSetting; - typedef std::map<std::string, SettingData> Settings; + public: + struct SettingData { + bool isAlias; + AbstractSetting* setting; + SettingData(bool isAlias, AbstractSetting* setting) + : isAlias(isAlias), setting(setting) {} + }; -private: + typedef std::map<std::string, SettingData> Settings; - Settings _settings; + private: + Settings _settings; -public: + public: + Config(const StringMap& initials = {}) : AbstractConfig(initials) {} - Config(const StringMap & initials = {}) - : AbstractConfig(initials) - { } + bool set(const std::string& name, const std::string& value) override; - bool set(const std::string & name, const std::string & value) override; + void addSetting(AbstractSetting* setting); - void addSetting(AbstractSetting * setting); + void getSettings(std::map<std::string, SettingInfo>& res, + bool overridenOnly = false) override; - void getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly = false) override; + void resetOverriden() override; - void resetOverriden() override; + void toJSON(JSONObject& out) override; - void toJSON(JSONObject & out) override; - - void convertToArgs(Args & args, const std::string & category) override; + void convertToArgs(Args& args, const std::string& category) override; }; -class AbstractSetting -{ - friend class Config; - -public: - - const std::string name; - const std::string description; - const std::set<std::string> aliases; +class AbstractSetting { + friend class Config; - int created = 123; + public: + const std::string name; + const std::string description; + const std::set<std::string> aliases; - bool overriden = false; + int created = 123; -protected: + bool overriden = false; - AbstractSetting( - const std::string & name, - const std::string & description, - const std::set<std::string> & aliases); + protected: + AbstractSetting(const std::string& name, const std::string& description, + const std::set<std::string>& aliases); - virtual ~AbstractSetting() - { - // Check against a gcc miscompilation causing our constructor - // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431). - assert(created == 123); - } + virtual ~AbstractSetting() { + // Check against a gcc miscompilation causing our constructor + // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431). + assert(created == 123); + } - virtual void set(const std::string & value) = 0; + virtual void set(const std::string& value) = 0; - virtual std::string to_string() = 0; + virtual std::string to_string() = 0; - virtual void toJSON(JSONPlaceholder & out); + virtual void toJSON(JSONPlaceholder& out); - virtual void convertToArg(Args & args, const std::string & category); + virtual void convertToArg(Args& args, const std::string& category); - bool isOverriden() { return overriden; } + bool isOverriden() { return overriden; } }; /* A setting of type T. */ -template<typename T> -class BaseSetting : public AbstractSetting -{ -protected: +template <typename T> +class BaseSetting : public AbstractSetting { + protected: + T value; - T value; + public: + BaseSetting(const T& def, const std::string& name, + const std::string& description, + const std::set<std::string>& aliases = {}) + : AbstractSetting(name, description, aliases), value(def) {} -public: + operator const T&() const { return value; } + operator T&() { return value; } + const T& get() const { return value; } + bool operator==(const T& v2) const { return value == v2; } + bool operator!=(const T& v2) const { return value != v2; } + void operator=(const T& v) { assign(v); } + virtual void assign(const T& v) { value = v; } - BaseSetting(const T & def, - const std::string & name, - const std::string & description, - const std::set<std::string> & aliases = {}) - : AbstractSetting(name, description, aliases) - , value(def) - { } + void set(const std::string& str) override; - operator const T &() const { return value; } - operator T &() { return value; } - const T & get() const { return value; } - bool operator ==(const T & v2) const { return value == v2; } - bool operator !=(const T & v2) const { return value != v2; } - void operator =(const T & v) { assign(v); } - virtual void assign(const T & v) { value = v; } + virtual void override(const T& v) { + overriden = true; + value = v; + } - void set(const std::string & str) override; + std::string to_string() override; - virtual void override(const T & v) - { - overriden = true; - value = v; - } + void convertToArg(Args& args, const std::string& category) override; - std::string to_string() override; - - void convertToArg(Args & args, const std::string & category) override; - - void toJSON(JSONPlaceholder & out) override; + void toJSON(JSONPlaceholder& out) override; }; -template<typename T> -std::ostream & operator <<(std::ostream & str, const BaseSetting<T> & opt) -{ - str << (const T &) opt; - return str; +template <typename T> +std::ostream& operator<<(std::ostream& str, const BaseSetting<T>& opt) { + str << (const T&)opt; + return str; +} + +template <typename T> +bool operator==(const T& v1, const BaseSetting<T>& v2) { + return v1 == (const T&)v2; } -template<typename T> -bool operator ==(const T & v1, const BaseSetting<T> & v2) { return v1 == (const T &) v2; } - -template<typename T> -class Setting : public BaseSetting<T> -{ -public: - Setting(Config * options, - const T & def, - const std::string & name, - const std::string & description, - const std::set<std::string> & aliases = {}) - : BaseSetting<T>(def, name, description, aliases) - { - options->addSetting(this); - } - - void operator =(const T & v) { this->assign(v); } +template <typename T> +class Setting : public BaseSetting<T> { + public: + Setting(Config* options, const T& def, const std::string& name, + const std::string& description, + const std::set<std::string>& aliases = {}) + : BaseSetting<T>(def, name, description, aliases) { + options->addSetting(this); + } + + void operator=(const T& v) { this->assign(v); } }; /* A special setting for Paths. These are automatically canonicalised (e.g. "/foo//bar/" becomes "/foo/bar"). */ -class PathSetting : public BaseSetting<Path> -{ - bool allowEmpty; - -public: +class PathSetting : public BaseSetting<Path> { + bool allowEmpty; - PathSetting(Config * options, - bool allowEmpty, - const Path & def, - const std::string & name, - const std::string & description, - const std::set<std::string> & aliases = {}) - : BaseSetting<Path>(def, name, description, aliases) - , allowEmpty(allowEmpty) - { - options->addSetting(this); - } + public: + PathSetting(Config* options, bool allowEmpty, const Path& def, + const std::string& name, const std::string& description, + const std::set<std::string>& aliases = {}) + : BaseSetting<Path>(def, name, description, aliases), + allowEmpty(allowEmpty) { + options->addSetting(this); + } - void set(const std::string & str) override; + void set(const std::string& str) override; - Path operator +(const char * p) const { return value + p; } + Path operator+(const char* p) const { return value + p; } - void operator =(const Path & v) { this->assign(v); } + void operator=(const Path& v) { this->assign(v); } }; -struct GlobalConfig : public AbstractConfig -{ - typedef std::vector<Config*> ConfigRegistrations; - static ConfigRegistrations * configRegistrations; +struct GlobalConfig : public AbstractConfig { + typedef std::vector<Config*> ConfigRegistrations; + static ConfigRegistrations* configRegistrations; - bool set(const std::string & name, const std::string & value) override; + bool set(const std::string& name, const std::string& value) override; - void getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly = false) override; + void getSettings(std::map<std::string, SettingInfo>& res, + bool overridenOnly = false) override; - void resetOverriden() override; + void resetOverriden() override; - void toJSON(JSONObject & out) override; + void toJSON(JSONObject& out) override; - void convertToArgs(Args & args, const std::string & category) override; + void convertToArgs(Args& args, const std::string& category) override; - struct Register - { - Register(Config * config); - }; + struct Register { + Register(Config* config); + }; }; extern GlobalConfig globalConfig; -} +} // namespace nix diff --git a/third_party/nix/src/libutil/finally.hh b/third_party/nix/src/libutil/finally.hh index 7760cfe9a410..8d3083b6a3ac 100644 --- a/third_party/nix/src/libutil/finally.hh +++ b/third_party/nix/src/libutil/finally.hh @@ -3,12 +3,11 @@ #include <functional> /* A trivial class to run a function at the end of a scope. */ -class Finally -{ -private: - std::function<void()> fun; +class Finally { + private: + std::function<void()> fun; -public: - Finally(std::function<void()> fun) : fun(fun) { } - ~Finally() { fun(); } + public: + Finally(std::function<void()> fun) : fun(fun) {} + ~Finally() { fun(); } }; diff --git a/third_party/nix/src/libutil/hash.cc b/third_party/nix/src/libutil/hash.cc index 1c14ebb187cc..647822213278 100644 --- a/third_party/nix/src/libutil/hash.cc +++ b/third_party/nix/src/libutil/hash.cc @@ -1,355 +1,323 @@ -#include <iostream> -#include <cstring> - +#include "hash.hh" +#include <fcntl.h> #include <openssl/md5.h> #include <openssl/sha.h> - -#include "hash.hh" +#include <sys/stat.h> +#include <sys/types.h> +#include <cstring> +#include <iostream> #include "archive.hh" -#include "util.hh" #include "istringstream_nocopy.hh" - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> +#include "util.hh" namespace nix { - -void Hash::init() -{ - if (type == htMD5) hashSize = md5HashSize; - else if (type == htSHA1) hashSize = sha1HashSize; - else if (type == htSHA256) hashSize = sha256HashSize; - else if (type == htSHA512) hashSize = sha512HashSize; - else abort(); - assert(hashSize <= maxHashSize); - memset(hash, 0, maxHashSize); -} - - -bool Hash::operator == (const Hash & h2) const -{ - if (hashSize != h2.hashSize) return false; - for (unsigned int i = 0; i < hashSize; i++) - if (hash[i] != h2.hash[i]) return false; - return true; +void Hash::init() { + if (type == htMD5) + hashSize = md5HashSize; + else if (type == htSHA1) + hashSize = sha1HashSize; + else if (type == htSHA256) + hashSize = sha256HashSize; + else if (type == htSHA512) + hashSize = sha512HashSize; + else + abort(); + assert(hashSize <= maxHashSize); + memset(hash, 0, maxHashSize); } - -bool Hash::operator != (const Hash & h2) const -{ - return !(*this == h2); +bool Hash::operator==(const Hash& h2) const { + if (hashSize != h2.hashSize) return false; + for (unsigned int i = 0; i < hashSize; i++) + if (hash[i] != h2.hash[i]) return false; + return true; } +bool Hash::operator!=(const Hash& h2) const { return !(*this == h2); } -bool Hash::operator < (const Hash & h) const -{ - if (hashSize < h.hashSize) return true; - if (hashSize > h.hashSize) return false; - for (unsigned int i = 0; i < hashSize; i++) { - if (hash[i] < h.hash[i]) return true; - if (hash[i] > h.hash[i]) return false; - } - return false; +bool Hash::operator<(const Hash& h) const { + if (hashSize < h.hashSize) return true; + if (hashSize > h.hashSize) return false; + for (unsigned int i = 0; i < hashSize; i++) { + if (hash[i] < h.hash[i]) return true; + if (hash[i] > h.hash[i]) return false; + } + return false; } - const string base16Chars = "0123456789abcdef"; - -static string printHash16(const Hash & hash) -{ - char buf[hash.hashSize * 2]; - for (unsigned int i = 0; i < hash.hashSize; i++) { - buf[i * 2] = base16Chars[hash.hash[i] >> 4]; - buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f]; - } - return string(buf, hash.hashSize * 2); +static string printHash16(const Hash& hash) { + char buf[hash.hashSize * 2]; + for (unsigned int i = 0; i < hash.hashSize; i++) { + buf[i * 2] = base16Chars[hash.hash[i] >> 4]; + buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f]; + } + return string(buf, hash.hashSize * 2); } - // omitted: E O U T const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; - -static string printHash32(const Hash & hash) -{ - assert(hash.hashSize); - size_t len = hash.base32Len(); - assert(len); - - string s; - s.reserve(len); - - for (int n = (int) len - 1; n >= 0; n--) { - unsigned int b = n * 5; - unsigned int i = b / 8; - unsigned int j = b % 8; - unsigned char c = - (hash.hash[i] >> j) - | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j)); - s.push_back(base32Chars[c & 0x1f]); - } - - return s; +static string printHash32(const Hash& hash) { + assert(hash.hashSize); + size_t len = hash.base32Len(); + assert(len); + + string s; + s.reserve(len); + + for (int n = (int)len - 1; n >= 0; n--) { + unsigned int b = n * 5; + unsigned int i = b / 8; + unsigned int j = b % 8; + unsigned char c = + (hash.hash[i] >> j) | + (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j)); + s.push_back(base32Chars[c & 0x1f]); + } + + return s; } - -string printHash16or32(const Hash & hash) -{ - return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false); +string printHash16or32(const Hash& hash) { + return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false); } - -std::string Hash::to_string(Base base, bool includeType) const -{ - std::string s; - if (base == SRI || includeType) { - s += printHashType(type); - s += base == SRI ? '-' : ':'; - } - switch (base) { +std::string Hash::to_string(Base base, bool includeType) const { + std::string s; + if (base == SRI || includeType) { + s += printHashType(type); + s += base == SRI ? '-' : ':'; + } + switch (base) { case Base16: - s += printHash16(*this); - break; + s += printHash16(*this); + break; case Base32: - s += printHash32(*this); - break; + s += printHash32(*this); + break; case Base64: case SRI: - s += base64Encode(std::string((const char *) hash, hashSize)); - break; - } - return s; + s += base64Encode(std::string((const char*)hash, hashSize)); + break; + } + return s; } +Hash::Hash(const std::string& s, HashType type) : type(type) { + size_t pos = 0; + bool isSRI = false; -Hash::Hash(const std::string & s, HashType type) - : type(type) -{ - size_t pos = 0; - bool isSRI = false; - - auto sep = s.find(':'); - if (sep == string::npos) { - sep = s.find('-'); - if (sep != string::npos) { - isSRI = true; - } else if (type == htUnknown) - throw BadHash("hash '%s' does not include a type", s); - } - + auto sep = s.find(':'); + if (sep == string::npos) { + sep = s.find('-'); if (sep != string::npos) { - string hts = string(s, 0, sep); - this->type = parseHashType(hts); - if (this->type == htUnknown) - throw BadHash("unknown hash type '%s'", hts); - if (type != htUnknown && type != this->type) - throw BadHash("hash '%s' should have type '%s'", s, printHashType(type)); - pos = sep + 1; - } - - init(); - - size_t size = s.size() - pos; - - if (!isSRI && size == base16Len()) { + isSRI = true; + } else if (type == htUnknown) + throw BadHash("hash '%s' does not include a type", s); + } + + if (sep != string::npos) { + string hts = string(s, 0, sep); + this->type = parseHashType(hts); + if (this->type == htUnknown) throw BadHash("unknown hash type '%s'", hts); + if (type != htUnknown && type != this->type) + throw BadHash("hash '%s' should have type '%s'", s, printHashType(type)); + pos = sep + 1; + } + + init(); + + size_t size = s.size() - pos; + + if (!isSRI && size == base16Len()) { + auto parseHexDigit = [&](char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + throw BadHash("invalid base-16 hash '%s'", s); + }; - auto parseHexDigit = [&](char c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - throw BadHash("invalid base-16 hash '%s'", s); - }; - - for (unsigned int i = 0; i < hashSize; i++) { - hash[i] = - parseHexDigit(s[pos + i * 2]) << 4 - | parseHexDigit(s[pos + i * 2 + 1]); - } - } - - else if (!isSRI && size == base32Len()) { - - for (unsigned int n = 0; n < size; ++n) { - char c = s[pos + size - n - 1]; - unsigned char digit; - for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ - if (base32Chars[digit] == c) break; - if (digit >= 32) - throw BadHash("invalid base-32 hash '%s'", s); - unsigned int b = n * 5; - unsigned int i = b / 8; - unsigned int j = b % 8; - hash[i] |= digit << j; - - if (i < hashSize - 1) { - hash[i + 1] |= digit >> (8 - j); - } else { - if (digit >> (8 - j)) - throw BadHash("invalid base-32 hash '%s'", s); - } - } + for (unsigned int i = 0; i < hashSize; i++) { + hash[i] = parseHexDigit(s[pos + i * 2]) << 4 | + parseHexDigit(s[pos + i * 2 + 1]); } - - else if (isSRI || size == base64Len()) { - auto d = base64Decode(std::string(s, pos)); - if (d.size() != hashSize) - throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", s); - assert(hashSize); - memcpy(hash, d.data(), hashSize); + } + + else if (!isSRI && size == base32Len()) { + for (unsigned int n = 0; n < size; ++n) { + char c = s[pos + size - n - 1]; + unsigned char digit; + for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ + if (base32Chars[digit] == c) break; + if (digit >= 32) throw BadHash("invalid base-32 hash '%s'", s); + unsigned int b = n * 5; + unsigned int i = b / 8; + unsigned int j = b % 8; + hash[i] |= digit << j; + + if (i < hashSize - 1) { + hash[i + 1] |= digit >> (8 - j); + } else { + if (digit >> (8 - j)) throw BadHash("invalid base-32 hash '%s'", s); + } } - - else - throw BadHash("hash '%s' has wrong length for hash type '%s'", s, printHashType(type)); + } + + else if (isSRI || size == base64Len()) { + auto d = base64Decode(std::string(s, pos)); + if (d.size() != hashSize) + throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", s); + assert(hashSize); + memcpy(hash, d.data(), hashSize); + } + + else + throw BadHash("hash '%s' has wrong length for hash type '%s'", s, + printHashType(type)); } - -union Ctx -{ - MD5_CTX md5; - SHA_CTX sha1; - SHA256_CTX sha256; - SHA512_CTX sha512; +union Ctx { + MD5_CTX md5; + SHA_CTX sha1; + SHA256_CTX sha256; + SHA512_CTX sha512; }; - -static void start(HashType ht, Ctx & ctx) -{ - if (ht == htMD5) MD5_Init(&ctx.md5); - else if (ht == htSHA1) SHA1_Init(&ctx.sha1); - else if (ht == htSHA256) SHA256_Init(&ctx.sha256); - else if (ht == htSHA512) SHA512_Init(&ctx.sha512); +static void start(HashType ht, Ctx& ctx) { + if (ht == htMD5) + MD5_Init(&ctx.md5); + else if (ht == htSHA1) + SHA1_Init(&ctx.sha1); + else if (ht == htSHA256) + SHA256_Init(&ctx.sha256); + else if (ht == htSHA512) + SHA512_Init(&ctx.sha512); } - -static void update(HashType ht, Ctx & ctx, - const unsigned char * bytes, size_t len) -{ - if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len); - else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len); - else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len); - else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len); +static void update(HashType ht, Ctx& ctx, const unsigned char* bytes, + size_t len) { + if (ht == htMD5) + MD5_Update(&ctx.md5, bytes, len); + else if (ht == htSHA1) + SHA1_Update(&ctx.sha1, bytes, len); + else if (ht == htSHA256) + SHA256_Update(&ctx.sha256, bytes, len); + else if (ht == htSHA512) + SHA512_Update(&ctx.sha512, bytes, len); } - -static void finish(HashType ht, Ctx & ctx, unsigned char * hash) -{ - if (ht == htMD5) MD5_Final(hash, &ctx.md5); - else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1); - else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256); - else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512); +static void finish(HashType ht, Ctx& ctx, unsigned char* hash) { + if (ht == htMD5) + MD5_Final(hash, &ctx.md5); + else if (ht == htSHA1) + SHA1_Final(hash, &ctx.sha1); + else if (ht == htSHA256) + SHA256_Final(hash, &ctx.sha256); + else if (ht == htSHA512) + SHA512_Final(hash, &ctx.sha512); } - -Hash hashString(HashType ht, const string & s) -{ - Ctx ctx; - Hash hash(ht); - start(ht, ctx); - update(ht, ctx, (const unsigned char *) s.data(), s.length()); - finish(ht, ctx, hash.hash); - return hash; +Hash hashString(HashType ht, const string& s) { + Ctx ctx; + Hash hash(ht); + start(ht, ctx); + update(ht, ctx, (const unsigned char*)s.data(), s.length()); + finish(ht, ctx, hash.hash); + return hash; } +Hash hashFile(HashType ht, const Path& path) { + Ctx ctx; + Hash hash(ht); + start(ht, ctx); -Hash hashFile(HashType ht, const Path & path) -{ - Ctx ctx; - Hash hash(ht); - start(ht, ctx); - - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) throw SysError(format("opening file '%1%'") % path); + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + if (!fd) throw SysError(format("opening file '%1%'") % path); - std::vector<unsigned char> buf(8192); - ssize_t n; - while ((n = read(fd.get(), buf.data(), buf.size()))) { - checkInterrupt(); - if (n == -1) throw SysError(format("reading file '%1%'") % path); - update(ht, ctx, buf.data(), n); - } + std::vector<unsigned char> buf(8192); + ssize_t n; + while ((n = read(fd.get(), buf.data(), buf.size()))) { + checkInterrupt(); + if (n == -1) throw SysError(format("reading file '%1%'") % path); + update(ht, ctx, buf.data(), n); + } - finish(ht, ctx, hash.hash); - return hash; + finish(ht, ctx, hash.hash); + return hash; } - -HashSink::HashSink(HashType ht) : ht(ht) -{ - ctx = new Ctx; - bytes = 0; - start(ht, *ctx); +HashSink::HashSink(HashType ht) : ht(ht) { + ctx = new Ctx; + bytes = 0; + start(ht, *ctx); } -HashSink::~HashSink() -{ - bufPos = 0; - delete ctx; +HashSink::~HashSink() { + bufPos = 0; + delete ctx; } -void HashSink::write(const unsigned char * data, size_t len) -{ - bytes += len; - update(ht, *ctx, data, len); +void HashSink::write(const unsigned char* data, size_t len) { + bytes += len; + update(ht, *ctx, data, len); } -HashResult HashSink::finish() -{ - flush(); - Hash hash(ht); - nix::finish(ht, *ctx, hash.hash); - return HashResult(hash, bytes); +HashResult HashSink::finish() { + flush(); + Hash hash(ht); + nix::finish(ht, *ctx, hash.hash); + return HashResult(hash, bytes); } -HashResult HashSink::currentHash() -{ - flush(); - Ctx ctx2 = *ctx; - Hash hash(ht); - nix::finish(ht, ctx2, hash.hash); - return HashResult(hash, bytes); +HashResult HashSink::currentHash() { + flush(); + Ctx ctx2 = *ctx; + Hash hash(ht); + nix::finish(ht, ctx2, hash.hash); + return HashResult(hash, bytes); } - -HashResult hashPath( - HashType ht, const Path & path, PathFilter & filter) -{ - HashSink sink(ht); - dumpPath(path, sink, filter); - return sink.finish(); +HashResult hashPath(HashType ht, const Path& path, PathFilter& filter) { + HashSink sink(ht); + dumpPath(path, sink, filter); + return sink.finish(); } - -Hash compressHash(const Hash & hash, unsigned int newSize) -{ - Hash h; - h.hashSize = newSize; - for (unsigned int i = 0; i < hash.hashSize; ++i) - h.hash[i % newSize] ^= hash.hash[i]; - return h; +Hash compressHash(const Hash& hash, unsigned int newSize) { + Hash h; + h.hashSize = newSize; + for (unsigned int i = 0; i < hash.hashSize; ++i) + h.hash[i % newSize] ^= hash.hash[i]; + return h; } - -HashType parseHashType(const string & s) -{ - if (s == "md5") return htMD5; - else if (s == "sha1") return htSHA1; - else if (s == "sha256") return htSHA256; - else if (s == "sha512") return htSHA512; - else return htUnknown; +HashType parseHashType(const string& s) { + if (s == "md5") + return htMD5; + else if (s == "sha1") + return htSHA1; + else if (s == "sha256") + return htSHA256; + else if (s == "sha512") + return htSHA512; + else + return htUnknown; } - -string printHashType(HashType ht) -{ - if (ht == htMD5) return "md5"; - else if (ht == htSHA1) return "sha1"; - else if (ht == htSHA256) return "sha256"; - else if (ht == htSHA512) return "sha512"; - else abort(); +string printHashType(HashType ht) { + if (ht == htMD5) + return "md5"; + else if (ht == htSHA1) + return "sha1"; + else if (ht == htSHA256) + return "sha256"; + else if (ht == htSHA512) + return "sha512"; + else + abort(); } - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/hash.hh b/third_party/nix/src/libutil/hash.hh index 2dbc3b630814..a9002023fa31 100644 --- a/third_party/nix/src/libutil/hash.hh +++ b/third_party/nix/src/libutil/hash.hh @@ -1,18 +1,14 @@ #pragma once -#include "types.hh" #include "serialise.hh" - +#include "types.hh" namespace nix { - MakeError(BadHash, Error); - enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 }; - const int md5HashSize = 16; const int sha1HashSize = 20; const int sha256HashSize = 32; @@ -22,110 +18,95 @@ extern const string base32Chars; enum Base : int { Base64, Base32, Base16, SRI }; +struct Hash { + static const unsigned int maxHashSize = 64; + unsigned int hashSize = 0; + unsigned char hash[maxHashSize] = {}; -struct Hash -{ - static const unsigned int maxHashSize = 64; - unsigned int hashSize = 0; - unsigned char hash[maxHashSize] = {}; - - HashType type = htUnknown; + HashType type = htUnknown; - /* Create an unset hash object. */ - Hash() { }; + /* Create an unset hash object. */ + Hash(){}; - /* Create a zero-filled hash object. */ - Hash(HashType type) : type(type) { init(); }; + /* Create a zero-filled hash object. */ + Hash(HashType type) : type(type) { init(); }; - /* Initialize the hash from a string representation, in the format - "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a - Subresource Integrity hash expression). If the 'type' argument - is htUnknown, then the hash type must be specified in the - string. */ - Hash(const std::string & s, HashType type = htUnknown); + /* Initialize the hash from a string representation, in the format + "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a + Subresource Integrity hash expression). If the 'type' argument + is htUnknown, then the hash type must be specified in the + string. */ + Hash(const std::string& s, HashType type = htUnknown); - void init(); + void init(); - /* Check whether a hash is set. */ - operator bool () const { return type != htUnknown; } + /* Check whether a hash is set. */ + operator bool() const { return type != htUnknown; } - /* Check whether two hash are equal. */ - bool operator == (const Hash & h2) const; + /* Check whether two hash are equal. */ + bool operator==(const Hash& h2) const; - /* Check whether two hash are not equal. */ - bool operator != (const Hash & h2) const; + /* Check whether two hash are not equal. */ + bool operator!=(const Hash& h2) const; - /* For sorting. */ - bool operator < (const Hash & h) const; + /* For sorting. */ + bool operator<(const Hash& h) const; - /* Returns the length of a base-16 representation of this hash. */ - size_t base16Len() const - { - return hashSize * 2; - } + /* Returns the length of a base-16 representation of this hash. */ + size_t base16Len() const { return hashSize * 2; } - /* Returns the length of a base-32 representation of this hash. */ - size_t base32Len() const - { - return (hashSize * 8 - 1) / 5 + 1; - } + /* Returns the length of a base-32 representation of this hash. */ + size_t base32Len() const { return (hashSize * 8 - 1) / 5 + 1; } - /* Returns the length of a base-64 representation of this hash. */ - size_t base64Len() const - { - return ((4 * hashSize / 3) + 3) & ~3; - } + /* Returns the length of a base-64 representation of this hash. */ + size_t base64Len() const { return ((4 * hashSize / 3) + 3) & ~3; } - /* Return a string representation of the hash, in base-16, base-32 - or base-64. By default, this is prefixed by the hash type - (e.g. "sha256:"). */ - std::string to_string(Base base = Base32, bool includeType = true) const; + /* Return a string representation of the hash, in base-16, base-32 + or base-64. By default, this is prefixed by the hash type + (e.g. "sha256:"). */ + std::string to_string(Base base = Base32, bool includeType = true) const; }; - /* Print a hash in base-16 if it's MD5, or base-32 otherwise. */ -string printHash16or32(const Hash & hash); +string printHash16or32(const Hash& hash); /* Compute the hash of the given string. */ -Hash hashString(HashType ht, const string & s); +Hash hashString(HashType ht, const string& s); /* Compute the hash of the given file. */ -Hash hashFile(HashType ht, const Path & path); +Hash hashFile(HashType ht, const Path& path); /* Compute the hash of the given path. The hash is defined as (essentially) hashString(ht, dumpPath(path)). */ typedef std::pair<Hash, unsigned long long> HashResult; -HashResult hashPath(HashType ht, const Path & path, - PathFilter & filter = defaultPathFilter); +HashResult hashPath(HashType ht, const Path& path, + PathFilter& filter = defaultPathFilter); /* Compress a hash to the specified number of bytes by cyclically XORing bytes together. */ -Hash compressHash(const Hash & hash, unsigned int newSize); +Hash compressHash(const Hash& hash, unsigned int newSize); /* Parse a string representing a hash type. */ -HashType parseHashType(const string & s); +HashType parseHashType(const string& s); /* And the reverse. */ string printHashType(HashType ht); - union Ctx; -class HashSink : public BufferedSink -{ -private: - HashType ht; - Ctx * ctx; - unsigned long long bytes; - -public: - HashSink(HashType ht); - HashSink(const HashSink & h); - ~HashSink(); - void write(const unsigned char * data, size_t len); - HashResult finish(); - HashResult currentHash(); +class HashSink : public BufferedSink { + private: + HashType ht; + Ctx* ctx; + unsigned long long bytes; + + public: + HashSink(HashType ht); + HashSink(const HashSink& h); + ~HashSink(); + void write(const unsigned char* data, size_t len); + HashResult finish(); + HashResult currentHash(); }; - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/istringstream_nocopy.hh b/third_party/nix/src/libutil/istringstream_nocopy.hh index f7beac578e39..997965630b6c 100644 --- a/third_party/nix/src/libutil/istringstream_nocopy.hh +++ b/third_party/nix/src/libutil/istringstream_nocopy.hh @@ -5,88 +5,78 @@ #pragma once -#include <string> #include <iostream> +#include <string> -template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT>> -class basic_istringbuf_nocopy : public std::basic_streambuf<CharT, Traits> -{ -public: - typedef std::basic_string<CharT, Traits, Allocator> string_type; +template <class CharT, class Traits = std::char_traits<CharT>, + class Allocator = std::allocator<CharT>> +class basic_istringbuf_nocopy : public std::basic_streambuf<CharT, Traits> { + public: + typedef std::basic_string<CharT, Traits, Allocator> string_type; - typedef typename std::basic_streambuf<CharT, Traits>::off_type off_type; + typedef typename std::basic_streambuf<CharT, Traits>::off_type off_type; - typedef typename std::basic_streambuf<CharT, Traits>::pos_type pos_type; + typedef typename std::basic_streambuf<CharT, Traits>::pos_type pos_type; - typedef typename std::basic_streambuf<CharT, Traits>::int_type int_type; + typedef typename std::basic_streambuf<CharT, Traits>::int_type int_type; - typedef typename std::basic_streambuf<CharT, Traits>::traits_type traits_type; + typedef typename std::basic_streambuf<CharT, Traits>::traits_type traits_type; -private: - const string_type & s; + private: + const string_type& s; - off_type off; + off_type off; -public: - basic_istringbuf_nocopy(const string_type & s) : s{s}, off{0} - { - } - -private: - pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) - { - if (which & std::ios_base::in) { - this->off = dir == std::ios_base::beg - ? off - : (dir == std::ios_base::end - ? s.size() + off - : this->off + off); - } - return pos_type(this->off); - } + public: + basic_istringbuf_nocopy(const string_type& s) : s{s}, off{0} {} - pos_type seekpos(pos_type pos, std::ios_base::openmode which) - { - return seekoff(pos, std::ios_base::beg, which); + private: + pos_type seekoff(off_type off, std::ios_base::seekdir dir, + std::ios_base::openmode which) { + if (which & std::ios_base::in) { + this->off = + dir == std::ios_base::beg + ? off + : (dir == std::ios_base::end ? s.size() + off : this->off + off); } + return pos_type(this->off); + } - std::streamsize showmanyc() - { - return s.size() - off; - } + pos_type seekpos(pos_type pos, std::ios_base::openmode which) { + return seekoff(pos, std::ios_base::beg, which); + } - int_type underflow() - { - if (typename string_type::size_type(off) == s.size()) - return traits_type::eof(); - return traits_type::to_int_type(s[off]); - } + std::streamsize showmanyc() { return s.size() - off; } - int_type uflow() - { - if (typename string_type::size_type(off) == s.size()) - return traits_type::eof(); - return traits_type::to_int_type(s[off++]); - } + int_type underflow() { + if (typename string_type::size_type(off) == s.size()) + return traits_type::eof(); + return traits_type::to_int_type(s[off]); + } - int_type pbackfail(int_type ch) - { - if (off == 0 || (ch != traits_type::eof() && ch != s[off - 1])) - return traits_type::eof(); + int_type uflow() { + if (typename string_type::size_type(off) == s.size()) + return traits_type::eof(); + return traits_type::to_int_type(s[off++]); + } - return traits_type::to_int_type(s[--off]); - } + int_type pbackfail(int_type ch) { + if (off == 0 || (ch != traits_type::eof() && ch != s[off - 1])) + return traits_type::eof(); + return traits_type::to_int_type(s[--off]); + } }; -template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT>> -class basic_istringstream_nocopy : public std::basic_iostream<CharT, Traits> -{ - typedef basic_istringbuf_nocopy<CharT, Traits, Allocator> buf_type; - buf_type buf; -public: - basic_istringstream_nocopy(const typename buf_type::string_type & s) : - std::basic_iostream<CharT, Traits>(&buf), buf(s) {}; +template <class CharT, class Traits = std::char_traits<CharT>, + class Allocator = std::allocator<CharT>> +class basic_istringstream_nocopy : public std::basic_iostream<CharT, Traits> { + typedef basic_istringbuf_nocopy<CharT, Traits, Allocator> buf_type; + buf_type buf; + + public: + basic_istringstream_nocopy(const typename buf_type::string_type& s) + : std::basic_iostream<CharT, Traits>(&buf), buf(s){}; }; typedef basic_istringstream_nocopy<char> istringstream_nocopy; diff --git a/third_party/nix/src/libutil/json.cc b/third_party/nix/src/libutil/json.cc index 0a6fb65f0605..37cae1680155 100644 --- a/third_party/nix/src/libutil/json.cc +++ b/third_party/nix/src/libutil/json.cc @@ -1,174 +1,184 @@ #include "json.hh" - -#include <iomanip> #include <cstring> +#include <iomanip> namespace nix { -void toJSON(std::ostream & str, const char * start, const char * end) -{ - str << '"'; - for (auto i = start; i != end; i++) - if (*i == '\"' || *i == '\\') str << '\\' << *i; - else if (*i == '\n') str << "\\n"; - else if (*i == '\r') str << "\\r"; - else if (*i == '\t') str << "\\t"; - else if (*i >= 0 && *i < 32) - str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec; - else str << *i; - str << '"'; +void toJSON(std::ostream& str, const char* start, const char* end) { + str << '"'; + for (auto i = start; i != end; i++) + if (*i == '\"' || *i == '\\') + str << '\\' << *i; + else if (*i == '\n') + str << "\\n"; + else if (*i == '\r') + str << "\\r"; + else if (*i == '\t') + str << "\\t"; + else if (*i >= 0 && *i < 32) + str << "\\u" << std::setfill('0') << std::setw(4) << std::hex + << (uint16_t)*i << std::dec; + else + str << *i; + str << '"'; +} + +void toJSON(std::ostream& str, const char* s) { + if (!s) + str << "null"; + else + toJSON(str, s, s + strlen(s)); } -void toJSON(std::ostream & str, const char * s) -{ - if (!s) str << "null"; else toJSON(str, s, s + strlen(s)); +template <> +void toJSON<int>(std::ostream& str, const int& n) { + str << n; } - -template<> void toJSON<int>(std::ostream & str, const int & n) { str << n; } -template<> void toJSON<unsigned int>(std::ostream & str, const unsigned int & n) { str << n; } -template<> void toJSON<long>(std::ostream & str, const long & n) { str << n; } -template<> void toJSON<unsigned long>(std::ostream & str, const unsigned long & n) { str << n; } -template<> void toJSON<long long>(std::ostream & str, const long long & n) { str << n; } -template<> void toJSON<unsigned long long>(std::ostream & str, const unsigned long long & n) { str << n; } -template<> void toJSON<float>(std::ostream & str, const float & n) { str << n; } -template<> void toJSON<double>(std::ostream & str, const double & n) { str << n; } - -template<> void toJSON<std::string>(std::ostream & str, const std::string & s) -{ - toJSON(str, s.c_str(), s.c_str() + s.size()); +template <> +void toJSON<unsigned int>(std::ostream& str, const unsigned int& n) { + str << n; } - -template<> void toJSON<bool>(std::ostream & str, const bool & b) -{ - str << (b ? "true" : "false"); +template <> +void toJSON<long>(std::ostream& str, const long& n) { + str << n; +} +template <> +void toJSON<unsigned long>(std::ostream& str, const unsigned long& n) { + str << n; +} +template <> +void toJSON<long long>(std::ostream& str, const long long& n) { + str << n; +} +template <> +void toJSON<unsigned long long>(std::ostream& str, + const unsigned long long& n) { + str << n; +} +template <> +void toJSON<float>(std::ostream& str, const float& n) { + str << n; +} +template <> +void toJSON<double>(std::ostream& str, const double& n) { + str << n; } -template<> void toJSON<std::nullptr_t>(std::ostream & str, const std::nullptr_t & b) -{ - str << "null"; +template <> +void toJSON<std::string>(std::ostream& str, const std::string& s) { + toJSON(str, s.c_str(), s.c_str() + s.size()); } -JSONWriter::JSONWriter(std::ostream & str, bool indent) - : state(new JSONState(str, indent)) -{ - state->stack++; +template <> +void toJSON<bool>(std::ostream& str, const bool& b) { + str << (b ? "true" : "false"); } -JSONWriter::JSONWriter(JSONState * state) - : state(state) -{ - state->stack++; +template <> +void toJSON<std::nullptr_t>(std::ostream& str, const std::nullptr_t& b) { + str << "null"; } -JSONWriter::~JSONWriter() -{ - if (state) { - assertActive(); - state->stack--; - if (state->stack == 0) delete state; - } +JSONWriter::JSONWriter(std::ostream& str, bool indent) + : state(new JSONState(str, indent)) { + state->stack++; } -void JSONWriter::comma() -{ +JSONWriter::JSONWriter(JSONState* state) : state(state) { state->stack++; } + +JSONWriter::~JSONWriter() { + if (state) { assertActive(); - if (first) { - first = false; - } else { - state->str << ','; - } - if (state->indent) indent(); + state->stack--; + if (state->stack == 0) delete state; + } } -void JSONWriter::indent() -{ - state->str << '\n' << std::string(state->depth * 2, ' '); +void JSONWriter::comma() { + assertActive(); + if (first) { + first = false; + } else { + state->str << ','; + } + if (state->indent) indent(); } -void JSONList::open() -{ - state->depth++; - state->str << '['; +void JSONWriter::indent() { + state->str << '\n' << std::string(state->depth * 2, ' '); } -JSONList::~JSONList() -{ - state->depth--; - if (state->indent && !first) indent(); - state->str << "]"; +void JSONList::open() { + state->depth++; + state->str << '['; } -JSONList JSONList::list() -{ - comma(); - return JSONList(state); +JSONList::~JSONList() { + state->depth--; + if (state->indent && !first) indent(); + state->str << "]"; } -JSONObject JSONList::object() -{ - comma(); - return JSONObject(state); +JSONList JSONList::list() { + comma(); + return JSONList(state); } -JSONPlaceholder JSONList::placeholder() -{ - comma(); - return JSONPlaceholder(state); +JSONObject JSONList::object() { + comma(); + return JSONObject(state); } -void JSONObject::open() -{ - state->depth++; - state->str << '{'; +JSONPlaceholder JSONList::placeholder() { + comma(); + return JSONPlaceholder(state); } -JSONObject::~JSONObject() -{ - if (state) { - state->depth--; - if (state->indent && !first) indent(); - state->str << "}"; - } +void JSONObject::open() { + state->depth++; + state->str << '{'; } -void JSONObject::attr(const std::string & s) -{ - comma(); - toJSON(state->str, s); - state->str << ':'; - if (state->indent) state->str << ' '; +JSONObject::~JSONObject() { + if (state) { + state->depth--; + if (state->indent && !first) indent(); + state->str << "}"; + } } -JSONList JSONObject::list(const std::string & name) -{ - attr(name); - return JSONList(state); +void JSONObject::attr(const std::string& s) { + comma(); + toJSON(state->str, s); + state->str << ':'; + if (state->indent) state->str << ' '; } -JSONObject JSONObject::object(const std::string & name) -{ - attr(name); - return JSONObject(state); +JSONList JSONObject::list(const std::string& name) { + attr(name); + return JSONList(state); } -JSONPlaceholder JSONObject::placeholder(const std::string & name) -{ - attr(name); - return JSONPlaceholder(state); +JSONObject JSONObject::object(const std::string& name) { + attr(name); + return JSONObject(state); } -JSONList JSONPlaceholder::list() -{ - assertValid(); - first = false; - return JSONList(state); +JSONPlaceholder JSONObject::placeholder(const std::string& name) { + attr(name); + return JSONPlaceholder(state); } -JSONObject JSONPlaceholder::object() -{ - assertValid(); - first = false; - return JSONObject(state); +JSONList JSONPlaceholder::list() { + assertValid(); + first = false; + return JSONList(state); } +JSONObject JSONPlaceholder::object() { + assertValid(); + first = false; + return JSONObject(state); } + +} // namespace nix diff --git a/third_party/nix/src/libutil/json.hh b/third_party/nix/src/libutil/json.hh index 02a39917fb5c..a3843a8a8a7b 100644 --- a/third_party/nix/src/libutil/json.hh +++ b/third_party/nix/src/libutil/json.hh @@ -1,189 +1,142 @@ #pragma once +#include <cassert> #include <iostream> #include <vector> -#include <cassert> namespace nix { -void toJSON(std::ostream & str, const char * start, const char * end); -void toJSON(std::ostream & str, const char * s); - -template<typename T> -void toJSON(std::ostream & str, const T & n); +void toJSON(std::ostream& str, const char* start, const char* end); +void toJSON(std::ostream& str, const char* s); -class JSONWriter -{ -protected: +template <typename T> +void toJSON(std::ostream& str, const T& n); - struct JSONState - { - std::ostream & str; - bool indent; - size_t depth = 0; - size_t stack = 0; - JSONState(std::ostream & str, bool indent) : str(str), indent(indent) { } - ~JSONState() - { - assert(stack == 0); - } - }; +class JSONWriter { + protected: + struct JSONState { + std::ostream& str; + bool indent; + size_t depth = 0; + size_t stack = 0; + JSONState(std::ostream& str, bool indent) : str(str), indent(indent) {} + ~JSONState() { assert(stack == 0); } + }; - JSONState * state; + JSONState* state; - bool first = true; + bool first = true; - JSONWriter(std::ostream & str, bool indent); + JSONWriter(std::ostream& str, bool indent); - JSONWriter(JSONState * state); + JSONWriter(JSONState* state); - ~JSONWriter(); + ~JSONWriter(); - void assertActive() - { - assert(state->stack != 0); - } + void assertActive() { assert(state->stack != 0); } - void comma(); + void comma(); - void indent(); + void indent(); }; class JSONObject; class JSONPlaceholder; -class JSONList : JSONWriter -{ -private: - - friend class JSONObject; - friend class JSONPlaceholder; - - void open(); +class JSONList : JSONWriter { + private: + friend class JSONObject; + friend class JSONPlaceholder; - JSONList(JSONState * state) - : JSONWriter(state) - { - open(); - } + void open(); -public: + JSONList(JSONState* state) : JSONWriter(state) { open(); } - JSONList(std::ostream & str, bool indent = false) - : JSONWriter(str, indent) - { - open(); - } + public: + JSONList(std::ostream& str, bool indent = false) : JSONWriter(str, indent) { + open(); + } - ~JSONList(); + ~JSONList(); - template<typename T> - JSONList & elem(const T & v) - { - comma(); - toJSON(state->str, v); - return *this; - } + template <typename T> + JSONList& elem(const T& v) { + comma(); + toJSON(state->str, v); + return *this; + } - JSONList list(); + JSONList list(); - JSONObject object(); + JSONObject object(); - JSONPlaceholder placeholder(); + JSONPlaceholder placeholder(); }; -class JSONObject : JSONWriter -{ -private: +class JSONObject : JSONWriter { + private: + friend class JSONList; + friend class JSONPlaceholder; - friend class JSONList; - friend class JSONPlaceholder; + void open(); - void open(); + JSONObject(JSONState* state) : JSONWriter(state) { open(); } - JSONObject(JSONState * state) - : JSONWriter(state) - { - open(); - } + void attr(const std::string& s); - void attr(const std::string & s); + public: + JSONObject(std::ostream& str, bool indent = false) : JSONWriter(str, indent) { + open(); + } -public: + JSONObject(const JSONObject& obj) = delete; - JSONObject(std::ostream & str, bool indent = false) - : JSONWriter(str, indent) - { - open(); - } + JSONObject(JSONObject&& obj) : JSONWriter(obj.state) { obj.state = 0; } - JSONObject(const JSONObject & obj) = delete; + ~JSONObject(); - JSONObject(JSONObject && obj) - : JSONWriter(obj.state) - { - obj.state = 0; - } + template <typename T> + JSONObject& attr(const std::string& name, const T& v) { + attr(name); + toJSON(state->str, v); + return *this; + } - ~JSONObject(); + JSONList list(const std::string& name); - template<typename T> - JSONObject & attr(const std::string & name, const T & v) - { - attr(name); - toJSON(state->str, v); - return *this; - } + JSONObject object(const std::string& name); - JSONList list(const std::string & name); - - JSONObject object(const std::string & name); - - JSONPlaceholder placeholder(const std::string & name); + JSONPlaceholder placeholder(const std::string& name); }; -class JSONPlaceholder : JSONWriter -{ - -private: - - friend class JSONList; - friend class JSONObject; - - JSONPlaceholder(JSONState * state) - : JSONWriter(state) - { - } +class JSONPlaceholder : JSONWriter { + private: + friend class JSONList; + friend class JSONObject; - void assertValid() - { - assertActive(); - assert(first); - } + JSONPlaceholder(JSONState* state) : JSONWriter(state) {} -public: + void assertValid() { + assertActive(); + assert(first); + } - JSONPlaceholder(std::ostream & str, bool indent = false) - : JSONWriter(str, indent) - { - } + public: + JSONPlaceholder(std::ostream& str, bool indent = false) + : JSONWriter(str, indent) {} - ~JSONPlaceholder() - { - assert(!first || std::uncaught_exception()); - } + ~JSONPlaceholder() { assert(!first || std::uncaught_exception()); } - template<typename T> - void write(const T & v) - { - assertValid(); - first = false; - toJSON(state->str, v); - } + template <typename T> + void write(const T& v) { + assertValid(); + first = false; + toJSON(state->str, v); + } - JSONList list(); + JSONList list(); - JSONObject object(); + JSONObject object(); }; -} +} // namespace nix diff --git a/third_party/nix/src/libutil/lazy.hh b/third_party/nix/src/libutil/lazy.hh index d073e486c2eb..9807b7540620 100644 --- a/third_party/nix/src/libutil/lazy.hh +++ b/third_party/nix/src/libutil/lazy.hh @@ -12,37 +12,32 @@ namespace nix { thread-safe way) on first use, that is, when var() is first called. If the initialiser code throws an exception, then all subsequent calls to var() will rethrow that exception. */ -template<typename T> -class Lazy -{ +template <typename T> +class Lazy { + typedef std::function<T()> Init; - typedef std::function<T()> Init; + Init init; - Init init; + std::once_flag done; - std::once_flag done; + T value; - T value; + std::exception_ptr ex; - std::exception_ptr ex; + public: + Lazy(Init init) : init(init) {} -public: - - Lazy(Init init) : init(init) - { } - - const T & operator () () - { - std::call_once(done, [&]() { - try { - value = init(); - } catch (...) { - ex = std::current_exception(); - } - }); - if (ex) std::rethrow_exception(ex); - return value; - } + const T& operator()() { + std::call_once(done, [&]() { + try { + value = init(); + } catch (...) { + ex = std::current_exception(); + } + }); + if (ex) std::rethrow_exception(ex); + return value; + } }; -} +} // namespace nix diff --git a/third_party/nix/src/libutil/logging.cc b/third_party/nix/src/libutil/logging.cc index b379306f6ec0..4f88044757cd 100644 --- a/third_party/nix/src/libutil/logging.cc +++ b/third_party/nix/src/libutil/logging.cc @@ -1,242 +1,227 @@ #include "logging.hh" -#include "util.hh" - #include <atomic> #include <nlohmann/json.hpp> +#include "util.hh" namespace nix { static thread_local ActivityId curActivity = 0; -ActivityId getCurActivity() -{ - return curActivity; -} -void setCurActivity(const ActivityId activityId) -{ - curActivity = activityId; -} +ActivityId getCurActivity() { return curActivity; } +void setCurActivity(const ActivityId activityId) { curActivity = activityId; } -Logger * logger = makeDefaultLogger(); +Logger* logger = makeDefaultLogger(); -void Logger::warn(const std::string & msg) -{ - log(lvlWarn, ANSI_RED "warning:" ANSI_NORMAL " " + msg); +void Logger::warn(const std::string& msg) { + log(lvlWarn, ANSI_RED "warning:" ANSI_NORMAL " " + msg); } -class SimpleLogger : public Logger -{ -public: - - bool systemd, tty; - - SimpleLogger() - { - systemd = getEnv("IN_SYSTEMD") == "1"; - tty = isatty(STDERR_FILENO); +class SimpleLogger : public Logger { + public: + bool systemd, tty; + + SimpleLogger() { + systemd = getEnv("IN_SYSTEMD") == "1"; + tty = isatty(STDERR_FILENO); + } + + void log(Verbosity lvl, const FormatOrString& fs) override { + if (lvl > verbosity) return; + + std::string prefix; + + if (systemd) { + char c; + switch (lvl) { + case lvlError: + c = '3'; + break; + case lvlWarn: + c = '4'; + break; + case lvlInfo: + c = '5'; + break; + case lvlTalkative: + case lvlChatty: + c = '6'; + break; + default: + c = '7'; + } + prefix = std::string("<") + c + ">"; } - void log(Verbosity lvl, const FormatOrString & fs) override - { - if (lvl > verbosity) return; - - std::string prefix; - - if (systemd) { - char c; - switch (lvl) { - case lvlError: c = '3'; break; - case lvlWarn: c = '4'; break; - case lvlInfo: c = '5'; break; - case lvlTalkative: case lvlChatty: c = '6'; break; - default: c = '7'; - } - prefix = std::string("<") + c + ">"; - } - - writeToStderr(prefix + filterANSIEscapes(fs.s, !tty) + "\n"); - } + writeToStderr(prefix + filterANSIEscapes(fs.s, !tty) + "\n"); + } - void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) - override - { - if (lvl <= verbosity && !s.empty()) - log(lvl, s + "..."); - } + void startActivity(ActivityId act, Verbosity lvl, ActivityType type, + const std::string& s, const Fields& fields, + ActivityId parent) override { + if (lvl <= verbosity && !s.empty()) log(lvl, s + "..."); + } }; Verbosity verbosity = lvlInfo; -void warnOnce(bool & haveWarned, const FormatOrString & fs) -{ - if (!haveWarned) { - warn(fs.s); - haveWarned = true; - } +void warnOnce(bool& haveWarned, const FormatOrString& fs) { + if (!haveWarned) { + warn(fs.s); + haveWarned = true; + } } -void writeToStderr(const string & s) -{ - try { - writeFull(STDERR_FILENO, s, false); - } catch (SysError & e) { - /* 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. */ - } +void writeToStderr(const string& s) { + try { + writeFull(STDERR_FILENO, s, false); + } catch (SysError& e) { + /* 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. */ + } } -Logger * makeDefaultLogger() -{ - return new SimpleLogger(); +Logger* makeDefaultLogger() { return new SimpleLogger(); } + +std::atomic<uint64_t> nextId{(uint64_t)getpid() << 32}; + +Activity::Activity(Logger& logger, Verbosity lvl, ActivityType type, + const std::string& s, const Logger::Fields& fields, + ActivityId parent) + : logger(logger), id(nextId++) { + logger.startActivity(id, lvl, type, s, fields, parent); } -std::atomic<uint64_t> nextId{(uint64_t) getpid() << 32}; +struct JSONLogger : Logger { + Logger& prevLogger; + + JSONLogger(Logger& prevLogger) : prevLogger(prevLogger) {} + + void addFields(nlohmann::json& json, const Fields& fields) { + if (fields.empty()) return; + auto& arr = json["fields"] = nlohmann::json::array(); + for (auto& f : fields) + if (f.type == Logger::Field::tInt) + arr.push_back(f.i); + else if (f.type == Logger::Field::tString) + arr.push_back(f.s); + else + abort(); + } + + void write(const nlohmann::json& json) { + prevLogger.log(lvlError, "@nix " + json.dump()); + } + + void log(Verbosity lvl, const FormatOrString& fs) override { + nlohmann::json json; + json["action"] = "msg"; + json["level"] = lvl; + json["msg"] = fs.s; + write(json); + } + + void startActivity(ActivityId act, Verbosity lvl, ActivityType type, + const std::string& s, const Fields& fields, + ActivityId parent) override { + nlohmann::json json; + json["action"] = "start"; + json["id"] = act; + json["level"] = lvl; + json["type"] = type; + json["text"] = s; + addFields(json, fields); + // FIXME: handle parent + write(json); + } + + void stopActivity(ActivityId act) override { + nlohmann::json json; + json["action"] = "stop"; + json["id"] = act; + write(json); + } + + void result(ActivityId act, ResultType type, const Fields& fields) override { + nlohmann::json json; + json["action"] = "result"; + json["id"] = act; + json["type"] = type; + addFields(json, fields); + write(json); + } +}; -Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type, - const std::string & s, const Logger::Fields & fields, ActivityId parent) - : logger(logger), id(nextId++) -{ - logger.startActivity(id, lvl, type, s, fields, parent); +Logger* makeJSONLogger(Logger& prevLogger) { + return new JSONLogger(prevLogger); } -struct JSONLogger : Logger -{ - Logger & prevLogger; - - JSONLogger(Logger & prevLogger) : prevLogger(prevLogger) { } - - void addFields(nlohmann::json & json, const Fields & fields) - { - if (fields.empty()) return; - auto & arr = json["fields"] = nlohmann::json::array(); - for (auto & f : fields) - if (f.type == Logger::Field::tInt) - arr.push_back(f.i); - else if (f.type == Logger::Field::tString) - arr.push_back(f.s); - else - abort(); - } +static Logger::Fields getFields(nlohmann::json& json) { + Logger::Fields fields; + for (auto& f : json) { + if (f.type() == nlohmann::json::value_t::number_unsigned) + fields.emplace_back(Logger::Field(f.get<uint64_t>())); + else if (f.type() == nlohmann::json::value_t::string) + fields.emplace_back(Logger::Field(f.get<std::string>())); + else + throw Error("unsupported JSON type %d", (int)f.type()); + } + return fields; +} - void write(const nlohmann::json & json) - { - prevLogger.log(lvlError, "@nix " + json.dump()); - } +bool handleJSONLogMessage(const std::string& msg, const Activity& act, + std::map<ActivityId, Activity>& activities, + bool trusted) { + if (!hasPrefix(msg, "@nix ")) return false; - void log(Verbosity lvl, const FormatOrString & fs) override - { - nlohmann::json json; - json["action"] = "msg"; - json["level"] = lvl; - json["msg"] = fs.s; - write(json); - } + try { + auto json = nlohmann::json::parse(std::string(msg, 5)); - void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) override - { - nlohmann::json json; - json["action"] = "start"; - json["id"] = act; - json["level"] = lvl; - json["type"] = type; - json["text"] = s; - addFields(json, fields); - // FIXME: handle parent - write(json); - } + std::string action = json["action"]; - void stopActivity(ActivityId act) override - { - nlohmann::json json; - json["action"] = "stop"; - json["id"] = act; - write(json); + if (action == "start") { + auto type = (ActivityType)json["type"]; + if (trusted || type == actDownload) + activities.emplace( + std::piecewise_construct, std::forward_as_tuple(json["id"]), + std::forward_as_tuple(*logger, (Verbosity)json["level"], type, + json["text"], getFields(json["fields"]), + act.id)); } - void result(ActivityId act, ResultType type, const Fields & fields) override - { - nlohmann::json json; - json["action"] = "result"; - json["id"] = act; - json["type"] = type; - addFields(json, fields); - write(json); - } -}; + else if (action == "stop") + activities.erase((ActivityId)json["id"]); -Logger * makeJSONLogger(Logger & prevLogger) -{ - return new JSONLogger(prevLogger); -} + else if (action == "result") { + auto i = activities.find((ActivityId)json["id"]); + if (i != activities.end()) + i->second.result((ResultType)json["type"], getFields(json["fields"])); + } -static Logger::Fields getFields(nlohmann::json & json) -{ - Logger::Fields fields; - for (auto & f : json) { - if (f.type() == nlohmann::json::value_t::number_unsigned) - fields.emplace_back(Logger::Field(f.get<uint64_t>())); - else if (f.type() == nlohmann::json::value_t::string) - fields.emplace_back(Logger::Field(f.get<std::string>())); - else throw Error("unsupported JSON type %d", (int) f.type()); + else if (action == "setPhase") { + std::string phase = json["phase"]; + act.result(resSetPhase, phase); } - return fields; -} -bool handleJSONLogMessage(const std::string & msg, - const Activity & act, std::map<ActivityId, Activity> & activities, bool trusted) -{ - if (!hasPrefix(msg, "@nix ")) return false; - - try { - auto json = nlohmann::json::parse(std::string(msg, 5)); - - std::string action = json["action"]; - - if (action == "start") { - auto type = (ActivityType) json["type"]; - if (trusted || type == actDownload) - activities.emplace(std::piecewise_construct, - std::forward_as_tuple(json["id"]), - std::forward_as_tuple(*logger, (Verbosity) json["level"], type, - json["text"], getFields(json["fields"]), act.id)); - } - - else if (action == "stop") - activities.erase((ActivityId) json["id"]); - - else if (action == "result") { - auto i = activities.find((ActivityId) json["id"]); - if (i != activities.end()) - i->second.result((ResultType) json["type"], getFields(json["fields"])); - } - - else if (action == "setPhase") { - std::string phase = json["phase"]; - act.result(resSetPhase, phase); - } - - else if (action == "msg") { - std::string msg = json["msg"]; - logger->log((Verbosity) json["level"], msg); - } - - } catch (std::exception & e) { - printError("bad log message from builder: %s", e.what()); + else if (action == "msg") { + std::string msg = json["msg"]; + logger->log((Verbosity)json["level"], msg); } - return true; + } catch (std::exception& e) { + printError("bad log message from builder: %s", e.what()); + } + + return true; } Activity::~Activity() { - try { - logger.stopActivity(id); - } catch (...) { - ignoreException(); - } + try { + logger.stopActivity(id); + } catch (...) { + ignoreException(); + } } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/logging.hh b/third_party/nix/src/libutil/logging.hh index 5df03da74e00..8b04dfd17ee3 100644 --- a/third_party/nix/src/libutil/logging.hh +++ b/third_party/nix/src/libutil/logging.hh @@ -5,151 +5,148 @@ namespace nix { typedef enum { - lvlError = 0, - lvlWarn, - lvlInfo, - lvlTalkative, - lvlChatty, - lvlDebug, - lvlVomit + lvlError = 0, + lvlWarn, + lvlInfo, + lvlTalkative, + lvlChatty, + lvlDebug, + lvlVomit } Verbosity; typedef enum { - actUnknown = 0, - actCopyPath = 100, - actDownload = 101, - actRealise = 102, - actCopyPaths = 103, - actBuilds = 104, - actBuild = 105, - actOptimiseStore = 106, - actVerifyPaths = 107, - actSubstitute = 108, - actQueryPathInfo = 109, - actPostBuildHook = 110, + actUnknown = 0, + actCopyPath = 100, + actDownload = 101, + actRealise = 102, + actCopyPaths = 103, + actBuilds = 104, + actBuild = 105, + actOptimiseStore = 106, + actVerifyPaths = 107, + actSubstitute = 108, + actQueryPathInfo = 109, + actPostBuildHook = 110, } ActivityType; typedef enum { - resFileLinked = 100, - resBuildLogLine = 101, - resUntrustedPath = 102, - resCorruptedPath = 103, - resSetPhase = 104, - resProgress = 105, - resSetExpected = 106, - resPostBuildLogLine = 107, + resFileLinked = 100, + resBuildLogLine = 101, + resUntrustedPath = 102, + resCorruptedPath = 103, + resSetPhase = 104, + resProgress = 105, + resSetExpected = 106, + resPostBuildLogLine = 107, } ResultType; typedef uint64_t ActivityId; -class Logger -{ - friend struct Activity; +class Logger { + friend struct Activity; -public: + public: + struct Field { + // FIXME: use std::variant. + enum { tInt = 0, tString = 1 } type; + uint64_t i = 0; + std::string s; + Field(const std::string& s) : type(tString), s(s) {} + Field(const char* s) : type(tString), s(s) {} + Field(const uint64_t& i) : type(tInt), i(i) {} + }; - struct Field - { - // FIXME: use std::variant. - enum { tInt = 0, tString = 1 } type; - uint64_t i = 0; - std::string s; - Field(const std::string & s) : type(tString), s(s) { } - Field(const char * s) : type(tString), s(s) { } - Field(const uint64_t & i) : type(tInt), i(i) { } - }; + typedef std::vector<Field> Fields; - typedef std::vector<Field> Fields; + virtual ~Logger() {} - virtual ~Logger() { } + virtual void log(Verbosity lvl, const FormatOrString& fs) = 0; - virtual void log(Verbosity lvl, const FormatOrString & fs) = 0; + void log(const FormatOrString& fs) { log(lvlInfo, fs); } - void log(const FormatOrString & fs) - { - log(lvlInfo, fs); - } + virtual void warn(const std::string& msg); - virtual void warn(const std::string & msg); + virtual void startActivity(ActivityId act, Verbosity lvl, ActivityType type, + const std::string& s, const Fields& fields, + ActivityId parent){}; - virtual void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) { }; + virtual void stopActivity(ActivityId act){}; - virtual void stopActivity(ActivityId act) { }; - - virtual void result(ActivityId act, ResultType type, const Fields & fields) { }; + virtual void result(ActivityId act, ResultType type, const Fields& fields){}; }; ActivityId getCurActivity(); void setCurActivity(const ActivityId activityId); -struct Activity -{ - Logger & logger; +struct Activity { + Logger& logger; - const ActivityId id; + const ActivityId id; - Activity(Logger & logger, Verbosity lvl, ActivityType type, const std::string & s = "", - const Logger::Fields & fields = {}, ActivityId parent = getCurActivity()); + Activity(Logger& logger, Verbosity lvl, ActivityType type, + const std::string& s = "", const Logger::Fields& fields = {}, + ActivityId parent = getCurActivity()); - Activity(Logger & logger, ActivityType type, - const Logger::Fields & fields = {}, ActivityId parent = getCurActivity()) - : Activity(logger, lvlError, type, "", fields, parent) { }; + Activity(Logger& logger, ActivityType type, const Logger::Fields& fields = {}, + ActivityId parent = getCurActivity()) + : Activity(logger, lvlError, type, "", fields, parent){}; - Activity(const Activity & act) = delete; + Activity(const Activity& act) = delete; - ~Activity(); + ~Activity(); - void progress(uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, uint64_t failed = 0) const - { result(resProgress, done, expected, running, failed); } + void progress(uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, + uint64_t failed = 0) const { + result(resProgress, done, expected, running, failed); + } - void setExpected(ActivityType type2, uint64_t expected) const - { result(resSetExpected, type2, expected); } + void setExpected(ActivityType type2, uint64_t expected) const { + result(resSetExpected, type2, expected); + } - template<typename... Args> - void result(ResultType type, const Args & ... args) const - { - Logger::Fields fields; - nop{(fields.emplace_back(Logger::Field(args)), 1)...}; - result(type, fields); - } + template <typename... Args> + void result(ResultType type, const Args&... args) const { + Logger::Fields fields; + nop{(fields.emplace_back(Logger::Field(args)), 1)...}; + result(type, fields); + } - void result(ResultType type, const Logger::Fields & fields) const - { - logger.result(id, type, fields); - } + void result(ResultType type, const Logger::Fields& fields) const { + logger.result(id, type, fields); + } - friend class Logger; + friend class Logger; }; -struct PushActivity -{ - const ActivityId prevAct; - PushActivity(ActivityId act) : prevAct(getCurActivity()) { setCurActivity(act); } - ~PushActivity() { setCurActivity(prevAct); } +struct PushActivity { + const ActivityId prevAct; + PushActivity(ActivityId act) : prevAct(getCurActivity()) { + setCurActivity(act); + } + ~PushActivity() { setCurActivity(prevAct); } }; -extern Logger * logger; +extern Logger* logger; -Logger * makeDefaultLogger(); +Logger* makeDefaultLogger(); -Logger * makeJSONLogger(Logger & prevLogger); +Logger* makeJSONLogger(Logger& prevLogger); -bool handleJSONLogMessage(const std::string & msg, - const Activity & act, std::map<ActivityId, Activity> & activities, - bool trusted); +bool handleJSONLogMessage(const std::string& msg, const Activity& act, + std::map<ActivityId, Activity>& activities, + bool trusted); extern Verbosity verbosity; /* suppress msgs > this */ /* 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, fmt(args)); \ - } \ - } while (0) +#define printMsg(level, args...) \ + do { \ + if (level <= nix::verbosity) { \ + logger->log(level, fmt(args)); \ + } \ + } while (0) #define printError(args...) printMsg(lvlError, args) #define printInfo(args...) printMsg(lvlInfo, args) @@ -157,16 +154,15 @@ extern Verbosity verbosity; /* suppress msgs > this */ #define debug(args...) printMsg(lvlDebug, args) #define vomit(args...) printMsg(lvlVomit, args) -template<typename... Args> -inline void warn(const std::string & fs, Args... args) -{ - boost::format f(fs); - nop{boost::io::detail::feed(f, args)...}; - logger->warn(f.str()); +template <typename... Args> +inline void warn(const std::string& fs, Args... args) { + boost::format f(fs); + nop{boost::io::detail::feed(f, args)...}; + logger->warn(f.str()); } -void warnOnce(bool & haveWarned, const FormatOrString & fs); +void warnOnce(bool& haveWarned, const FormatOrString& fs); -void writeToStderr(const string & s); +void writeToStderr(const string& s); -} +} // namespace nix diff --git a/third_party/nix/src/libutil/lru-cache.hh b/third_party/nix/src/libutil/lru-cache.hh index 8b83f842c324..bf7263243c31 100644 --- a/third_party/nix/src/libutil/lru-cache.hh +++ b/third_party/nix/src/libutil/lru-cache.hh @@ -1,92 +1,84 @@ #pragma once -#include <map> #include <list> +#include <map> #include <optional> namespace nix { /* A simple least-recently used cache. Not thread-safe. */ -template<typename Key, typename Value> -class LRUCache -{ -private: - - size_t capacity; +template <typename Key, typename Value> +class LRUCache { + private: + size_t capacity; - // Stupid wrapper to get around circular dependency between Data - // and LRU. - struct LRUIterator; + // Stupid wrapper to get around circular dependency between Data + // and LRU. + struct LRUIterator; - using Data = std::map<Key, std::pair<LRUIterator, Value>>; - using LRU = std::list<typename Data::iterator>; + using Data = std::map<Key, std::pair<LRUIterator, Value>>; + using LRU = std::list<typename Data::iterator>; - struct LRUIterator { typename LRU::iterator it; }; + struct LRUIterator { + typename LRU::iterator it; + }; - Data data; - LRU lru; + Data data; + LRU lru; -public: + public: + LRUCache(size_t capacity) : capacity(capacity) {} - LRUCache(size_t capacity) : capacity(capacity) { } + /* Insert or upsert an item in the cache. */ + void upsert(const Key& key, const Value& value) { + if (capacity == 0) return; - /* Insert or upsert an item in the cache. */ - void upsert(const Key & key, const Value & value) - { - if (capacity == 0) return; + erase(key); - erase(key); - - if (data.size() >= capacity) { - /* Retire the oldest item. */ - auto oldest = lru.begin(); - data.erase(*oldest); - lru.erase(oldest); - } + if (data.size() >= capacity) { + /* Retire the oldest item. */ + auto oldest = lru.begin(); + data.erase(*oldest); + lru.erase(oldest); + } - auto res = data.emplace(key, std::make_pair(LRUIterator(), value)); - assert(res.second); - auto & i(res.first); + auto res = data.emplace(key, std::make_pair(LRUIterator(), value)); + assert(res.second); + auto& i(res.first); - auto j = lru.insert(lru.end(), i); + auto j = lru.insert(lru.end(), i); - i->second.first.it = j; - } + i->second.first.it = j; + } - bool erase(const Key & key) - { - auto i = data.find(key); - if (i == data.end()) return false; - lru.erase(i->second.first.it); - data.erase(i); - return true; - } + bool erase(const Key& key) { + auto i = data.find(key); + if (i == data.end()) return false; + lru.erase(i->second.first.it); + data.erase(i); + return true; + } - /* Look up an item in the cache. If it exists, it becomes the most - recently used item. */ - std::optional<Value> get(const Key & key) - { - auto i = data.find(key); - if (i == data.end()) return {}; + /* Look up an item in the cache. If it exists, it becomes the most + recently used item. */ + std::optional<Value> get(const Key& key) { + auto i = data.find(key); + if (i == data.end()) return {}; - /* Move this item to the back of the LRU list. */ - lru.erase(i->second.first.it); - auto j = lru.insert(lru.end(), i); - i->second.first.it = j; + /* Move this item to the back of the LRU list. */ + lru.erase(i->second.first.it); + auto j = lru.insert(lru.end(), i); + i->second.first.it = j; - return i->second.second; - } + return i->second.second; + } - size_t size() - { - return data.size(); - } + size_t size() { return data.size(); } - void clear() - { - data.clear(); - lru.clear(); - } + void clear() { + data.clear(); + lru.clear(); + } }; -} +} // namespace nix diff --git a/third_party/nix/src/libutil/monitor-fd.hh b/third_party/nix/src/libutil/monitor-fd.hh index 5ee0b88ef50f..faf79e6381d2 100644 --- a/third_party/nix/src/libutil/monitor-fd.hh +++ b/third_party/nix/src/libutil/monitor-fd.hh @@ -1,58 +1,52 @@ #pragma once -#include <thread> -#include <atomic> - -#include <cstdlib> #include <poll.h> +#include <signal.h> #include <sys/types.h> #include <unistd.h> -#include <signal.h> +#include <atomic> +#include <cstdlib> +#include <thread> namespace nix { - -class MonitorFdHup -{ -private: - std::thread thread; - -public: - MonitorFdHup(int fd) - { - thread = std::thread([fd]() { - while (true) { - /* Wait indefinitely until a POLLHUP occurs. */ - struct pollfd fds[1]; - fds[0].fd = fd; - /* This shouldn't be necessary, but macOS doesn't seem to - like a zeroed out events field. - See rdar://37537852. - */ - fds[0].events = POLLHUP; - auto count = poll(fds, 1, -1); - if (count == -1) abort(); // can't happen - /* This shouldn't happen, but can on macOS due to a bug. - See rdar://37550628. - - This may eventually need a delay or further - coordination with the main thread if spinning proves - too harmful. - */ - if (count == 0) continue; - assert(fds[0].revents & POLLHUP); - triggerInterrupt(); - break; - } - }); - }; - - ~MonitorFdHup() - { - pthread_cancel(thread.native_handle()); - thread.join(); - } +class MonitorFdHup { + private: + std::thread thread; + + public: + MonitorFdHup(int fd) { + thread = std::thread([fd]() { + while (true) { + /* Wait indefinitely until a POLLHUP occurs. */ + struct pollfd fds[1]; + fds[0].fd = fd; + /* This shouldn't be necessary, but macOS doesn't seem to + like a zeroed out events field. + See rdar://37537852. + */ + fds[0].events = POLLHUP; + auto count = poll(fds, 1, -1); + if (count == -1) abort(); // can't happen + /* This shouldn't happen, but can on macOS due to a bug. + See rdar://37550628. + + This may eventually need a delay or further + coordination with the main thread if spinning proves + too harmful. + */ + if (count == 0) continue; + assert(fds[0].revents & POLLHUP); + triggerInterrupt(); + break; + } + }); + }; + + ~MonitorFdHup() { + pthread_cancel(thread.native_handle()); + thread.join(); + } }; - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/pool.hh b/third_party/nix/src/libutil/pool.hh index d49067bb95dc..592bd50897cc 100644 --- a/third_party/nix/src/libutil/pool.hh +++ b/third_party/nix/src/libutil/pool.hh @@ -1,13 +1,12 @@ #pragma once +#include <cassert> #include <functional> #include <limits> #include <list> #include <memory> -#include <cassert> - -#include "sync.hh" #include "ref.hh" +#include "sync.hh" namespace nix { @@ -29,159 +28,140 @@ namespace nix { */ template <class R> -class Pool -{ -public: - - /* A function that produces new instances of R on demand. */ - typedef std::function<ref<R>()> Factory; - - /* A function that checks whether an instance of R is still - usable. Unusable instances are removed from the pool. */ - typedef std::function<bool(const ref<R> &)> Validator; - -private: - - Factory factory; - Validator validator; - - struct State - { - size_t inUse = 0; - size_t max; - std::vector<ref<R>> idle; - }; - - Sync<State> state; - - std::condition_variable wakeup; - -public: - - Pool(size_t max = std::numeric_limits<size_t>::max(), - const Factory & factory = []() { return make_ref<R>(); }, - const Validator & validator = [](ref<R> r) { return true; }) - : factory(factory) - , validator(validator) - { - auto state_(state.lock()); - state_->max = max; - } - - void incCapacity() - { - auto state_(state.lock()); - state_->max++; - /* we could wakeup here, but this is only used when we're - * about to nest Pool usages, and we want to save the slot for - * the nested use if we can - */ +class Pool { + public: + /* A function that produces new instances of R on demand. */ + typedef std::function<ref<R>()> Factory; + + /* A function that checks whether an instance of R is still + usable. Unusable instances are removed from the pool. */ + typedef std::function<bool(const ref<R>&)> Validator; + + private: + Factory factory; + Validator validator; + + struct State { + size_t inUse = 0; + size_t max; + std::vector<ref<R>> idle; + }; + + Sync<State> state; + + std::condition_variable wakeup; + + public: + Pool( + size_t max = std::numeric_limits<size_t>::max(), + const Factory& factory = []() { return make_ref<R>(); }, + const Validator& validator = [](ref<R> r) { return true; }) + : factory(factory), validator(validator) { + auto state_(state.lock()); + state_->max = max; + } + + void incCapacity() { + auto state_(state.lock()); + state_->max++; + /* we could wakeup here, but this is only used when we're + * about to nest Pool usages, and we want to save the slot for + * the nested use if we can + */ + } + + void decCapacity() { + auto state_(state.lock()); + state_->max--; + } + + ~Pool() { + auto state_(state.lock()); + assert(!state_->inUse); + state_->max = 0; + state_->idle.clear(); + } + + class Handle { + private: + Pool& pool; + std::shared_ptr<R> r; + bool bad = false; + + friend Pool; + + Handle(Pool& pool, std::shared_ptr<R> r) : pool(pool), r(r) {} + + public: + Handle(Handle&& h) : pool(h.pool), r(h.r) { h.r.reset(); } + + Handle(const Handle& l) = delete; + + ~Handle() { + if (!r) return; + { + auto state_(pool.state.lock()); + if (!bad) state_->idle.push_back(ref<R>(r)); + assert(state_->inUse); + state_->inUse--; + } + pool.wakeup.notify_one(); } - void decCapacity() - { - auto state_(state.lock()); - state_->max--; - } - - ~Pool() - { - auto state_(state.lock()); - assert(!state_->inUse); - state_->max = 0; - state_->idle.clear(); - } - - class Handle - { - private: - Pool & pool; - std::shared_ptr<R> r; - bool bad = false; - - friend Pool; - - Handle(Pool & pool, std::shared_ptr<R> r) : pool(pool), r(r) { } - - public: - Handle(Handle && h) : pool(h.pool), r(h.r) { h.r.reset(); } - - Handle(const Handle & l) = delete; - - ~Handle() - { - if (!r) return; - { - auto state_(pool.state.lock()); - if (!bad) - state_->idle.push_back(ref<R>(r)); - assert(state_->inUse); - state_->inUse--; - } - pool.wakeup.notify_one(); - } - - R * operator -> () { return &*r; } - R & operator * () { return *r; } + R* operator->() { return &*r; } + R& operator*() { return *r; } - void markBad() { bad = true; } - }; + void markBad() { bad = true; } + }; - Handle get() + Handle get() { { - { - auto state_(state.lock()); - - /* If we're over the maximum number of instance, we need - to wait until a slot becomes available. */ - while (state_->idle.empty() && state_->inUse >= state_->max) - state_.wait(wakeup); - - while (!state_->idle.empty()) { - auto p = state_->idle.back(); - state_->idle.pop_back(); - if (validator(p)) { - state_->inUse++; - return Handle(*this, p); - } - } - - state_->inUse++; + auto state_(state.lock()); + + /* If we're over the maximum number of instance, we need + to wait until a slot becomes available. */ + while (state_->idle.empty() && state_->inUse >= state_->max) + state_.wait(wakeup); + + while (!state_->idle.empty()) { + auto p = state_->idle.back(); + state_->idle.pop_back(); + if (validator(p)) { + state_->inUse++; + return Handle(*this, p); } + } - /* We need to create a new instance. Because that might take a - while, we don't hold the lock in the meantime. */ - try { - Handle h(*this, factory()); - return h; - } catch (...) { - auto state_(state.lock()); - state_->inUse--; - wakeup.notify_one(); - throw; - } - } - - size_t count() - { - auto state_(state.lock()); - return state_->idle.size() + state_->inUse; + state_->inUse++; } - size_t capacity() - { - return state.lock()->max; - } - - void flushBad() - { - auto state_(state.lock()); - std::vector<ref<R>> left; - for (auto & p : state_->idle) - if (validator(p)) - left.push_back(p); - std::swap(state_->idle, left); + /* We need to create a new instance. Because that might take a + while, we don't hold the lock in the meantime. */ + try { + Handle h(*this, factory()); + return h; + } catch (...) { + auto state_(state.lock()); + state_->inUse--; + wakeup.notify_one(); + throw; } + } + + size_t count() { + auto state_(state.lock()); + return state_->idle.size() + state_->inUse; + } + + size_t capacity() { return state.lock()->max; } + + void flushBad() { + auto state_(state.lock()); + std::vector<ref<R>> left; + for (auto& p : state_->idle) + if (validator(p)) left.push_back(p); + std::swap(state_->idle, left); + } }; -} +} // namespace nix diff --git a/third_party/nix/src/libutil/ref.hh b/third_party/nix/src/libutil/ref.hh index 0be2a7e74ae0..5f0d7db4b6a1 100644 --- a/third_party/nix/src/libutil/ref.hh +++ b/third_party/nix/src/libutil/ref.hh @@ -1,92 +1,61 @@ #pragma once -#include <memory> #include <exception> +#include <memory> #include <stdexcept> namespace nix { /* A simple non-nullable reference-counted pointer. Actually a wrapper around std::shared_ptr that prevents non-null constructions. */ -template<typename T> -class ref -{ -private: - - std::shared_ptr<T> p; - -public: - - ref<T>(const ref<T> & r) - : p(r.p) - { } - - explicit ref<T>(const std::shared_ptr<T> & p) - : p(p) - { - if (!p) - throw std::invalid_argument("null pointer cast to ref"); - } - - explicit ref<T>(T * p) - : p(p) - { - if (!p) - throw std::invalid_argument("null pointer cast to ref"); - } - - T* operator ->() const - { - return &*p; - } - - T& operator *() const - { - return *p; - } - - operator std::shared_ptr<T> () const - { - return p; - } - - std::shared_ptr<T> get_ptr() const - { - return p; - } - - template<typename T2> - ref<T2> cast() const - { - return ref<T2>(std::dynamic_pointer_cast<T2>(p)); - } - - template<typename T2> - std::shared_ptr<T2> dynamic_pointer_cast() const - { - return std::dynamic_pointer_cast<T2>(p); - } - - template<typename T2> - operator ref<T2> () const - { - return ref<T2>((std::shared_ptr<T2>) p); - } - -private: - - template<typename T2, typename... Args> - friend ref<T2> - make_ref(Args&&... args); +template <typename T> +class ref { + private: + std::shared_ptr<T> p; + + public: + ref<T>(const ref<T>& r) : p(r.p) {} + + explicit ref<T>(const std::shared_ptr<T>& p) : p(p) { + if (!p) throw std::invalid_argument("null pointer cast to ref"); + } + + explicit ref<T>(T* p) : p(p) { + if (!p) throw std::invalid_argument("null pointer cast to ref"); + } + + T* operator->() const { return &*p; } + + T& operator*() const { return *p; } + operator std::shared_ptr<T>() const { return p; } + + std::shared_ptr<T> get_ptr() const { return p; } + + template <typename T2> + ref<T2> cast() const { + return ref<T2>(std::dynamic_pointer_cast<T2>(p)); + } + + template <typename T2> + std::shared_ptr<T2> dynamic_pointer_cast() const { + return std::dynamic_pointer_cast<T2>(p); + } + + template <typename T2> + operator ref<T2>() const { + return ref<T2>((std::shared_ptr<T2>)p); + } + + private: + template <typename T2, typename... Args> + friend ref<T2> make_ref(Args&&... args); }; -template<typename T, typename... Args> -inline ref<T> -make_ref(Args&&... args) -{ - auto p = std::make_shared<T>(std::forward<Args>(args)...); - return ref<T>(p); +template <typename T, typename... Args> +inline ref<T> make_ref(Args&&... args) { + auto p = std::make_shared<T>(std::forward<Args>(args)...); + return ref<T>(p); } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/serialise.cc b/third_party/nix/src/libutil/serialise.cc index 8201549fd7d0..6d7cb1d8d156 100644 --- a/third_party/nix/src/libutil/serialise.cc +++ b/third_party/nix/src/libutil/serialise.cc @@ -1,323 +1,277 @@ #include "serialise.hh" -#include "util.hh" - -#include <cstring> +#include <boost/coroutine2/coroutine.hpp> #include <cerrno> +#include <cstring> #include <memory> - -#include <boost/coroutine2/coroutine.hpp> - +#include "util.hh" namespace nix { +void BufferedSink::operator()(const unsigned char* data, size_t len) { + if (!buffer) buffer = decltype(buffer)(new unsigned char[bufSize]); -void BufferedSink::operator () (const unsigned char * data, size_t len) -{ - if (!buffer) buffer = decltype(buffer)(new unsigned char[bufSize]); - - while (len) { - /* Optimisation: bypass the buffer if the data exceeds the - buffer size. */ - if (bufPos + len >= bufSize) { - flush(); - write(data, len); - break; - } - /* Otherwise, copy the bytes to the buffer. Flush the buffer - when it's full. */ - size_t n = bufPos + len > bufSize ? bufSize - bufPos : len; - memcpy(buffer.get() + bufPos, data, n); - data += n; bufPos += n; len -= n; - if (bufPos == bufSize) flush(); + while (len) { + /* Optimisation: bypass the buffer if the data exceeds the + buffer size. */ + if (bufPos + len >= bufSize) { + flush(); + write(data, len); + break; } + /* Otherwise, copy the bytes to the buffer. Flush the buffer + when it's full. */ + size_t n = bufPos + len > bufSize ? bufSize - bufPos : len; + memcpy(buffer.get() + bufPos, data, n); + data += n; + bufPos += n; + len -= n; + if (bufPos == bufSize) flush(); + } } - -void BufferedSink::flush() -{ - if (bufPos == 0) return; - size_t n = bufPos; - bufPos = 0; // don't trigger the assert() in ~BufferedSink() - write(buffer.get(), n); +void BufferedSink::flush() { + if (bufPos == 0) return; + size_t n = bufPos; + bufPos = 0; // don't trigger the assert() in ~BufferedSink() + write(buffer.get(), n); } - -FdSink::~FdSink() -{ - try { flush(); } catch (...) { ignoreException(); } +FdSink::~FdSink() { + try { + flush(); + } catch (...) { + ignoreException(); + } } - size_t threshold = 256 * 1024 * 1024; -static void warnLargeDump() -{ - printError("warning: dumping very large path (> 256 MiB); this may run out of memory"); +static void warnLargeDump() { + printError( + "warning: dumping very large path (> 256 MiB); this may run out of " + "memory"); } - -void FdSink::write(const unsigned char * data, size_t len) -{ - written += len; - static bool warned = false; - if (warn && !warned) { - if (written > threshold) { - warnLargeDump(); - warned = true; - } - } - try { - writeFull(fd, data, len); - } catch (SysError & e) { - _good = false; - throw; +void FdSink::write(const unsigned char* data, size_t len) { + written += len; + static bool warned = false; + if (warn && !warned) { + if (written > threshold) { + warnLargeDump(); + warned = true; } + } + try { + writeFull(fd, data, len); + } catch (SysError& e) { + _good = false; + throw; + } } +bool FdSink::good() { return _good; } -bool FdSink::good() -{ - return _good; +void Source::operator()(unsigned char* data, size_t len) { + while (len) { + size_t n = read(data, len); + data += n; + len -= n; + } } - -void Source::operator () (unsigned char * data, size_t len) -{ - while (len) { - size_t n = read(data, len); - data += n; len -= n; - } -} - - -std::string Source::drain() -{ - std::string s; - std::vector<unsigned char> buf(8192); - while (true) { - size_t n; - try { - n = read(buf.data(), buf.size()); - s.append((char *) buf.data(), n); - } catch (EndOfFile &) { - break; - } +std::string Source::drain() { + std::string s; + std::vector<unsigned char> buf(8192); + while (true) { + size_t n; + try { + n = read(buf.data(), buf.size()); + s.append((char*)buf.data(), n); + } catch (EndOfFile&) { + break; } - return s; -} - - -size_t BufferedSource::read(unsigned char * data, size_t len) -{ - if (!buffer) buffer = decltype(buffer)(new unsigned char[bufSize]); - - if (!bufPosIn) bufPosIn = readUnbuffered(buffer.get(), bufSize); - - /* Copy out the data in the buffer. */ - size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len; - memcpy(data, buffer.get() + bufPosOut, n); - bufPosOut += n; - if (bufPosIn == bufPosOut) bufPosIn = bufPosOut = 0; - return n; + } + return s; } +size_t BufferedSource::read(unsigned char* data, size_t len) { + if (!buffer) buffer = decltype(buffer)(new unsigned char[bufSize]); -bool BufferedSource::hasData() -{ - return bufPosOut < bufPosIn; -} - + if (!bufPosIn) bufPosIn = readUnbuffered(buffer.get(), bufSize); -size_t FdSource::readUnbuffered(unsigned char * data, size_t len) -{ - ssize_t n; - do { - checkInterrupt(); - n = ::read(fd, (char *) data, len); - } while (n == -1 && errno == EINTR); - if (n == -1) { _good = false; throw SysError("reading from file"); } - if (n == 0) { _good = false; throw EndOfFile("unexpected end-of-file"); } - read += n; - return n; + /* Copy out the data in the buffer. */ + size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len; + memcpy(data, buffer.get() + bufPosOut, n); + bufPosOut += n; + if (bufPosIn == bufPosOut) bufPosIn = bufPosOut = 0; + return n; } - -bool FdSource::good() -{ - return _good; +bool BufferedSource::hasData() { return bufPosOut < bufPosIn; } + +size_t FdSource::readUnbuffered(unsigned char* data, size_t len) { + ssize_t n; + do { + checkInterrupt(); + n = ::read(fd, (char*)data, len); + } while (n == -1 && errno == EINTR); + if (n == -1) { + _good = false; + throw SysError("reading from file"); + } + if (n == 0) { + _good = false; + throw EndOfFile("unexpected end-of-file"); + } + read += n; + return n; } +bool FdSource::good() { return _good; } -size_t StringSource::read(unsigned char * data, size_t len) -{ - if (pos == s.size()) throw EndOfFile("end of string reached"); - size_t n = s.copy((char *) data, len, pos); - pos += n; - return n; +size_t StringSource::read(unsigned char* data, size_t len) { + if (pos == s.size()) throw EndOfFile("end of string reached"); + size_t n = s.copy((char*)data, len, pos); + pos += n; + return n; } - #if BOOST_VERSION >= 106300 && BOOST_VERSION < 106600 #error Coroutines are broken in this version of Boost! #endif -std::unique_ptr<Source> sinkToSource( - std::function<void(Sink &)> fun, - std::function<void()> eof) -{ - struct SinkToSource : Source - { - typedef boost::coroutines2::coroutine<std::string> coro_t; - - std::function<void(Sink &)> fun; - std::function<void()> eof; - std::optional<coro_t::pull_type> coro; - bool started = false; - - SinkToSource(std::function<void(Sink &)> fun, std::function<void()> eof) - : fun(fun), eof(eof) - { - } - - std::string cur; - size_t pos = 0; - - size_t read(unsigned char * data, size_t len) override - { - if (!coro) - coro = coro_t::pull_type([&](coro_t::push_type & yield) { - LambdaSink sink([&](const unsigned char * data, size_t len) { - if (len) yield(std::string((const char *) data, len)); - }); - fun(sink); - }); - - if (!*coro) { eof(); abort(); } - - if (pos == cur.size()) { - if (!cur.empty()) (*coro)(); - cur = coro->get(); - pos = 0; - } - - auto n = std::min(cur.size() - pos, len); - memcpy(data, (unsigned char *) cur.data() + pos, n); - pos += n; - - return n; - } - }; - - return std::make_unique<SinkToSource>(fun, eof); -} - - -void writePadding(size_t len, Sink & sink) -{ - if (len % 8) { - unsigned char zero[8]; - memset(zero, 0, sizeof(zero)); - sink(zero, 8 - (len % 8)); +std::unique_ptr<Source> sinkToSource(std::function<void(Sink&)> fun, + std::function<void()> eof) { + struct SinkToSource : Source { + typedef boost::coroutines2::coroutine<std::string> coro_t; + + std::function<void(Sink&)> fun; + std::function<void()> eof; + std::optional<coro_t::pull_type> coro; + bool started = false; + + SinkToSource(std::function<void(Sink&)> fun, std::function<void()> eof) + : fun(fun), eof(eof) {} + + std::string cur; + size_t pos = 0; + + size_t read(unsigned char* data, size_t len) override { + if (!coro) + coro = coro_t::pull_type([&](coro_t::push_type& yield) { + LambdaSink sink([&](const unsigned char* data, size_t len) { + if (len) yield(std::string((const char*)data, len)); + }); + fun(sink); + }); + + if (!*coro) { + eof(); + abort(); + } + + if (pos == cur.size()) { + if (!cur.empty()) (*coro)(); + cur = coro->get(); + pos = 0; + } + + auto n = std::min(cur.size() - pos, len); + memcpy(data, (unsigned char*)cur.data() + pos, n); + pos += n; + + return n; } -} - + }; -void writeString(const unsigned char * buf, size_t len, Sink & sink) -{ - sink << len; - sink(buf, len); - writePadding(len, sink); + return std::make_unique<SinkToSource>(fun, eof); } - -Sink & operator << (Sink & sink, const string & s) -{ - writeString((const unsigned char *) s.data(), s.size(), sink); - return sink; +void writePadding(size_t len, Sink& sink) { + if (len % 8) { + unsigned char zero[8]; + memset(zero, 0, sizeof(zero)); + sink(zero, 8 - (len % 8)); + } } - -template<class T> void writeStrings(const T & ss, Sink & sink) -{ - sink << ss.size(); - for (auto & i : ss) - sink << i; +void writeString(const unsigned char* buf, size_t len, Sink& sink) { + sink << len; + sink(buf, len); + writePadding(len, sink); } -Sink & operator << (Sink & sink, const Strings & s) -{ - writeStrings(s, sink); - return sink; +Sink& operator<<(Sink& sink, const string& s) { + writeString((const unsigned char*)s.data(), s.size(), sink); + return sink; } -Sink & operator << (Sink & sink, const StringSet & s) -{ - writeStrings(s, sink); - return sink; +template <class T> +void writeStrings(const T& ss, Sink& sink) { + sink << ss.size(); + for (auto& i : ss) sink << i; } - -void readPadding(size_t len, Source & source) -{ - if (len % 8) { - unsigned char zero[8]; - size_t n = 8 - (len % 8); - source(zero, n); - for (unsigned int i = 0; i < n; i++) - if (zero[i]) throw SerialisationError("non-zero padding"); - } +Sink& operator<<(Sink& sink, const Strings& s) { + writeStrings(s, sink); + return sink; } - -size_t readString(unsigned char * buf, size_t max, Source & source) -{ - auto len = readNum<size_t>(source); - if (len > max) throw SerialisationError("string is too long"); - source(buf, len); - readPadding(len, source); - return len; +Sink& operator<<(Sink& sink, const StringSet& s) { + writeStrings(s, sink); + return sink; } - -string readString(Source & source, size_t max) -{ - auto len = readNum<size_t>(source); - if (len > max) throw SerialisationError("string is too long"); - std::string res(len, 0); - source((unsigned char*) res.data(), len); - readPadding(len, source); - return res; +void readPadding(size_t len, Source& source) { + if (len % 8) { + unsigned char zero[8]; + size_t n = 8 - (len % 8); + source(zero, n); + for (unsigned int i = 0; i < n; i++) + if (zero[i]) throw SerialisationError("non-zero padding"); + } } -Source & operator >> (Source & in, string & s) -{ - s = readString(in); - return in; +size_t readString(unsigned char* buf, size_t max, Source& source) { + auto len = readNum<size_t>(source); + if (len > max) throw SerialisationError("string is too long"); + source(buf, len); + readPadding(len, source); + return len; } - -template<class T> T readStrings(Source & source) -{ - auto count = readNum<size_t>(source); - T ss; - while (count--) - ss.insert(ss.end(), readString(source)); - return ss; +string readString(Source& source, size_t max) { + auto len = readNum<size_t>(source); + if (len > max) throw SerialisationError("string is too long"); + std::string res(len, 0); + source((unsigned char*)res.data(), len); + readPadding(len, source); + return res; } -template Paths readStrings(Source & source); -template PathSet readStrings(Source & source); - +Source& operator>>(Source& in, string& s) { + s = readString(in); + return in; +} -void StringSink::operator () (const unsigned char * data, size_t len) -{ - static bool warned = false; - if (!warned && s->size() > threshold) { - warnLargeDump(); - warned = true; - } - s->append((const char *) data, len); +template <class T> +T readStrings(Source& source) { + auto count = readNum<size_t>(source); + T ss; + while (count--) ss.insert(ss.end(), readString(source)); + return ss; } +template Paths readStrings(Source& source); +template PathSet readStrings(Source& source); +void StringSink::operator()(const unsigned char* data, size_t len) { + static bool warned = false; + if (!warned && s->size() > threshold) { + warnLargeDump(); + warned = true; + } + s->append((const char*)data, len); } + +} // namespace nix diff --git a/third_party/nix/src/libutil/serialise.hh b/third_party/nix/src/libutil/serialise.hh index a344a5ac7520..2ab4bdff6f46 100644 --- a/third_party/nix/src/libutil/serialise.hh +++ b/third_party/nix/src/libutil/serialise.hh @@ -1,337 +1,285 @@ #pragma once #include <memory> - #include "types.hh" #include "util.hh" - namespace nix { - /* Abstract destination of binary data. */ -struct Sink -{ - virtual ~Sink() { } - virtual void operator () (const unsigned char * data, size_t len) = 0; - virtual bool good() { return true; } - - void operator () (const std::string & s) - { - (*this)((const unsigned char *) s.data(), s.size()); - } +struct Sink { + virtual ~Sink() {} + virtual void operator()(const unsigned char* data, size_t len) = 0; + virtual bool good() { return true; } + + void operator()(const std::string& s) { + (*this)((const unsigned char*)s.data(), s.size()); + } }; - /* A buffered abstract sink. */ -struct BufferedSink : Sink -{ - size_t bufSize, bufPos; - std::unique_ptr<unsigned char[]> buffer; +struct BufferedSink : Sink { + size_t bufSize, bufPos; + std::unique_ptr<unsigned char[]> buffer; - BufferedSink(size_t bufSize = 32 * 1024) - : bufSize(bufSize), bufPos(0), buffer(nullptr) { } + BufferedSink(size_t bufSize = 32 * 1024) + : bufSize(bufSize), bufPos(0), buffer(nullptr) {} - void operator () (const unsigned char * data, size_t len) override; + void operator()(const unsigned char* data, size_t len) override; - void operator () (const std::string & s) - { - Sink::operator()(s); - } + void operator()(const std::string& s) { Sink::operator()(s); } - void flush(); + void flush(); - virtual void write(const unsigned char * data, size_t len) = 0; + virtual void write(const unsigned char* data, size_t len) = 0; }; - /* Abstract source of binary data. */ -struct Source -{ - virtual ~Source() { } +struct Source { + virtual ~Source() {} - /* Store exactly โlenโ bytes in the buffer pointed to by โdataโ. - It blocks until all the requested data is available, or throws - an error if it is not going to be available. */ - void operator () (unsigned char * data, size_t len); + /* Store exactly โlenโ bytes in the buffer pointed to by โdataโ. + It blocks until all the requested data is available, or throws + an error if it is not going to be available. */ + void operator()(unsigned char* data, size_t len); - /* Store up to โlenโ in the buffer pointed to by โdataโ, and - return the number of bytes stored. It blocks until at least - one byte is available. */ - virtual size_t read(unsigned char * data, size_t len) = 0; + /* Store up to โlenโ in the buffer pointed to by โdataโ, and + return the number of bytes stored. It blocks until at least + one byte is available. */ + virtual size_t read(unsigned char* data, size_t len) = 0; - virtual bool good() { return true; } + virtual bool good() { return true; } - std::string drain(); + std::string drain(); }; - /* A buffered abstract source. */ -struct BufferedSource : Source -{ - size_t bufSize, bufPosIn, bufPosOut; - std::unique_ptr<unsigned char[]> buffer; - - BufferedSource(size_t bufSize = 32 * 1024) - : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) { } +struct BufferedSource : Source { + size_t bufSize, bufPosIn, bufPosOut; + std::unique_ptr<unsigned char[]> buffer; - size_t read(unsigned char * data, size_t len) override; + BufferedSource(size_t bufSize = 32 * 1024) + : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) {} + size_t read(unsigned char* data, size_t len) override; - bool hasData(); + bool hasData(); -protected: - /* Underlying read call, to be overridden. */ - virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0; + protected: + /* Underlying read call, to be overridden. */ + virtual size_t readUnbuffered(unsigned char* data, size_t len) = 0; }; - /* A sink that writes data to a file descriptor. */ -struct FdSink : BufferedSink -{ - int fd; - bool warn = false; - size_t written = 0; - - FdSink() : fd(-1) { } - FdSink(int fd) : fd(fd) { } - FdSink(FdSink&&) = default; - - FdSink& operator=(FdSink && s) - { - flush(); - fd = s.fd; - s.fd = -1; - warn = s.warn; - written = s.written; - return *this; - } +struct FdSink : BufferedSink { + int fd; + bool warn = false; + size_t written = 0; - ~FdSink(); + FdSink() : fd(-1) {} + FdSink(int fd) : fd(fd) {} + FdSink(FdSink&&) = default; - void write(const unsigned char * data, size_t len) override; + FdSink& operator=(FdSink&& s) { + flush(); + fd = s.fd; + s.fd = -1; + warn = s.warn; + written = s.written; + return *this; + } - bool good() override; + ~FdSink(); -private: - bool _good = true; -}; + void write(const unsigned char* data, size_t len) override; + bool good() override; + + private: + bool _good = true; +}; /* A source that reads data from a file descriptor. */ -struct FdSource : BufferedSource -{ - int fd; - size_t read = 0; - - FdSource() : fd(-1) { } - FdSource(int fd) : fd(fd) { } - FdSource(FdSource&&) = default; - - FdSource& operator=(FdSource && s) - { - fd = s.fd; - s.fd = -1; - read = s.read; - return *this; - } +struct FdSource : BufferedSource { + int fd; + size_t read = 0; - bool good() override; -protected: - size_t readUnbuffered(unsigned char * data, size_t len) override; -private: - bool _good = true; -}; + FdSource() : fd(-1) {} + FdSource(int fd) : fd(fd) {} + FdSource(FdSource&&) = default; + FdSource& operator=(FdSource&& s) { + fd = s.fd; + s.fd = -1; + read = s.read; + return *this; + } -/* A sink that writes data to a string. */ -struct StringSink : Sink -{ - ref<std::string> s; - StringSink() : s(make_ref<std::string>()) { }; - StringSink(ref<std::string> s) : s(s) { }; - void operator () (const unsigned char * data, size_t len) override; + bool good() override; + + protected: + size_t readUnbuffered(unsigned char* data, size_t len) override; + + private: + bool _good = true; }; +/* A sink that writes data to a string. */ +struct StringSink : Sink { + ref<std::string> s; + StringSink() : s(make_ref<std::string>()){}; + StringSink(ref<std::string> s) : s(s){}; + void operator()(const unsigned char* data, size_t len) override; +}; /* A source that reads data from a string. */ -struct StringSource : Source -{ - const string & s; - size_t pos; - StringSource(const string & _s) : s(_s), pos(0) { } - size_t read(unsigned char * data, size_t len) override; +struct StringSource : Source { + const string& s; + size_t pos; + StringSource(const string& _s) : s(_s), pos(0) {} + size_t read(unsigned char* data, size_t len) override; }; - /* Adapter class of a Source that saves all data read to `s'. */ -struct TeeSource : Source -{ - Source & orig; - ref<std::string> data; - TeeSource(Source & orig) - : orig(orig), data(make_ref<std::string>()) { } - size_t read(unsigned char * data, size_t len) - { - size_t n = orig.read(data, len); - this->data->append((const char *) data, n); - return n; - } +struct TeeSource : Source { + Source& orig; + ref<std::string> data; + TeeSource(Source& orig) : orig(orig), data(make_ref<std::string>()) {} + size_t read(unsigned char* data, size_t len) { + size_t n = orig.read(data, len); + this->data->append((const char*)data, n); + return n; + } }; /* A reader that consumes the original Source until 'size'. */ -struct SizedSource : Source -{ - Source & orig; - size_t remain; - SizedSource(Source & orig, size_t size) - : orig(orig), remain(size) { } - size_t read(unsigned char * data, size_t len) - { - if (this->remain <= 0) { - throw EndOfFile("sized: unexpected end-of-file"); - } - len = std::min(len, this->remain); - size_t n = this->orig.read(data, len); - this->remain -= n; - return n; +struct SizedSource : Source { + Source& orig; + size_t remain; + SizedSource(Source& orig, size_t size) : orig(orig), remain(size) {} + size_t read(unsigned char* data, size_t len) { + if (this->remain <= 0) { + throw EndOfFile("sized: unexpected end-of-file"); } - - /* Consume the original source until no remain data is left to consume. */ - size_t drainAll() - { - std::vector<unsigned char> buf(8192); - size_t sum = 0; - while (this->remain > 0) { - size_t n = read(buf.data(), buf.size()); - sum += n; - } - return sum; + len = std::min(len, this->remain); + size_t n = this->orig.read(data, len); + this->remain -= n; + return n; + } + + /* Consume the original source until no remain data is left to consume. */ + size_t drainAll() { + std::vector<unsigned char> buf(8192); + size_t sum = 0; + while (this->remain > 0) { + size_t n = read(buf.data(), buf.size()); + sum += n; } + return sum; + } }; /* Convert a function into a sink. */ -struct LambdaSink : Sink -{ - typedef std::function<void(const unsigned char *, size_t)> lambda_t; +struct LambdaSink : Sink { + typedef std::function<void(const unsigned char*, size_t)> lambda_t; - lambda_t lambda; + lambda_t lambda; - LambdaSink(const lambda_t & lambda) : lambda(lambda) { } + LambdaSink(const lambda_t& lambda) : lambda(lambda) {} - virtual void operator () (const unsigned char * data, size_t len) - { - lambda(data, len); - } + virtual void operator()(const unsigned char* data, size_t len) { + lambda(data, len); + } }; - /* Convert a function into a source. */ -struct LambdaSource : Source -{ - typedef std::function<size_t(unsigned char *, size_t)> lambda_t; +struct LambdaSource : Source { + typedef std::function<size_t(unsigned char*, size_t)> lambda_t; - lambda_t lambda; + lambda_t lambda; - LambdaSource(const lambda_t & lambda) : lambda(lambda) { } + LambdaSource(const lambda_t& lambda) : lambda(lambda) {} - size_t read(unsigned char * data, size_t len) override - { - return lambda(data, len); - } + size_t read(unsigned char* data, size_t len) override { + return lambda(data, len); + } }; - /* Convert a function that feeds data into a Sink into a Source. The Source executes the function as a coroutine. */ std::unique_ptr<Source> sinkToSource( - std::function<void(Sink &)> fun, - std::function<void()> eof = []() { - throw EndOfFile("coroutine has finished"); + std::function<void(Sink&)> fun, std::function<void()> eof = []() { + throw EndOfFile("coroutine has finished"); }); - -void writePadding(size_t len, Sink & sink); -void writeString(const unsigned char * buf, size_t len, Sink & sink); - -inline Sink & operator << (Sink & sink, uint64_t n) -{ - unsigned char buf[8]; - buf[0] = n & 0xff; - buf[1] = (n >> 8) & 0xff; - buf[2] = (n >> 16) & 0xff; - buf[3] = (n >> 24) & 0xff; - buf[4] = (n >> 32) & 0xff; - buf[5] = (n >> 40) & 0xff; - buf[6] = (n >> 48) & 0xff; - buf[7] = (unsigned char) (n >> 56) & 0xff; - sink(buf, sizeof(buf)); - return sink; +void writePadding(size_t len, Sink& sink); +void writeString(const unsigned char* buf, size_t len, Sink& sink); + +inline Sink& operator<<(Sink& sink, uint64_t n) { + unsigned char buf[8]; + buf[0] = n & 0xff; + buf[1] = (n >> 8) & 0xff; + buf[2] = (n >> 16) & 0xff; + buf[3] = (n >> 24) & 0xff; + buf[4] = (n >> 32) & 0xff; + buf[5] = (n >> 40) & 0xff; + buf[6] = (n >> 48) & 0xff; + buf[7] = (unsigned char)(n >> 56) & 0xff; + sink(buf, sizeof(buf)); + return sink; } -Sink & operator << (Sink & sink, const string & s); -Sink & operator << (Sink & sink, const Strings & s); -Sink & operator << (Sink & sink, const StringSet & s); - +Sink& operator<<(Sink& sink, const string& s); +Sink& operator<<(Sink& sink, const Strings& s); +Sink& operator<<(Sink& sink, const StringSet& s); MakeError(SerialisationError, Error) + template <typename T> + T readNum(Source& source) { + unsigned char buf[8]; + source(buf, sizeof(buf)); -template<typename T> -T readNum(Source & source) -{ - unsigned char buf[8]; - source(buf, sizeof(buf)); + uint64_t n = + ((unsigned long long)buf[0]) | ((unsigned long long)buf[1] << 8) | + ((unsigned long long)buf[2] << 16) | ((unsigned long long)buf[3] << 24) | + ((unsigned long long)buf[4] << 32) | ((unsigned long long)buf[5] << 40) | + ((unsigned long long)buf[6] << 48) | ((unsigned long long)buf[7] << 56); - uint64_t n = - ((unsigned long long) buf[0]) | - ((unsigned long long) buf[1] << 8) | - ((unsigned long long) buf[2] << 16) | - ((unsigned long long) buf[3] << 24) | - ((unsigned long long) buf[4] << 32) | - ((unsigned long long) buf[5] << 40) | - ((unsigned long long) buf[6] << 48) | - ((unsigned long long) buf[7] << 56); + if (n > std::numeric_limits<T>::max()) + throw SerialisationError("serialised integer %d is too large for type '%s'", + n, typeid(T).name()); - if (n > std::numeric_limits<T>::max()) - throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name()); - - return (T) n; + return (T)n; } - -inline unsigned int readInt(Source & source) -{ - return readNum<unsigned int>(source); +inline unsigned int readInt(Source& source) { + return readNum<unsigned int>(source); } - -inline uint64_t readLongLong(Source & source) -{ - return readNum<uint64_t>(source); +inline uint64_t readLongLong(Source& source) { + return readNum<uint64_t>(source); } +void readPadding(size_t len, Source& source); +size_t readString(unsigned char* buf, size_t max, Source& source); +string readString(Source& source, + size_t max = std::numeric_limits<size_t>::max()); +template <class T> +T readStrings(Source& source); -void readPadding(size_t len, Source & source); -size_t readString(unsigned char * buf, size_t max, Source & source); -string readString(Source & source, size_t max = std::numeric_limits<size_t>::max()); -template<class T> T readStrings(Source & source); - -Source & operator >> (Source & in, string & s); +Source& operator>>(Source& in, string& s); -template<typename T> -Source & operator >> (Source & in, T & n) -{ - n = readNum<T>(in); - return in; +template <typename T> +Source& operator>>(Source& in, T& n) { + n = readNum<T>(in); + return in; } -template<typename T> -Source & operator >> (Source & in, bool & b) -{ - b = readNum<uint64_t>(in); - return in; +template <typename T> +Source& operator>>(Source& in, bool& b) { + b = readNum<uint64_t>(in); + return in; } - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/sync.hh b/third_party/nix/src/libutil/sync.hh index e1d591d77a84..b79d1176b9f3 100644 --- a/third_party/nix/src/libutil/sync.hh +++ b/third_party/nix/src/libutil/sync.hh @@ -1,9 +1,9 @@ #pragma once +#include <cassert> +#include <condition_variable> #include <cstdlib> #include <mutex> -#include <condition_variable> -#include <cassert> namespace nix { @@ -23,66 +23,62 @@ namespace nix { scope. */ -template<class T, class M = std::mutex> -class Sync -{ -private: - M mutex; - T data; - -public: - - Sync() { } - Sync(const T & data) : data(data) { } - Sync(T && data) noexcept : data(std::move(data)) { } - - class Lock - { - private: - Sync * s; - std::unique_lock<M> lk; - friend Sync; - Lock(Sync * s) : s(s), lk(s->mutex) { } - public: - Lock(Lock && l) : s(l.s) { abort(); } - Lock(const Lock & l) = delete; - ~Lock() { } - T * operator -> () { return &s->data; } - T & operator * () { return s->data; } - - void wait(std::condition_variable & cv) - { - assert(s); - cv.wait(lk); - } - - template<class Rep, class Period> - std::cv_status wait_for(std::condition_variable & cv, - const std::chrono::duration<Rep, Period> & duration) - { - assert(s); - return 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, - Predicate pred) - { - assert(s); - return cv.wait_for(lk, duration, pred); - } - - template<class Clock, class Duration> - std::cv_status wait_until(std::condition_variable & cv, - const std::chrono::time_point<Clock, Duration> & duration) - { - assert(s); - return cv.wait_until(lk, duration); - } - }; - - Lock lock() { return Lock(this); } +template <class T, class M = std::mutex> +class Sync { + private: + M mutex; + T data; + + public: + Sync() {} + Sync(const T& data) : data(data) {} + Sync(T&& data) noexcept : data(std::move(data)) {} + + class Lock { + private: + Sync* s; + std::unique_lock<M> lk; + friend Sync; + Lock(Sync* s) : s(s), lk(s->mutex) {} + + public: + Lock(Lock&& l) : s(l.s) { abort(); } + Lock(const Lock& l) = delete; + ~Lock() {} + T* operator->() { return &s->data; } + T& operator*() { return s->data; } + + void wait(std::condition_variable& cv) { + assert(s); + cv.wait(lk); + } + + template <class Rep, class Period> + std::cv_status wait_for( + std::condition_variable& cv, + const std::chrono::duration<Rep, Period>& duration) { + assert(s); + return 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, + Predicate pred) { + assert(s); + return cv.wait_for(lk, duration, pred); + } + + template <class Clock, class Duration> + std::cv_status wait_until( + std::condition_variable& cv, + const std::chrono::time_point<Clock, Duration>& duration) { + assert(s); + return cv.wait_until(lk, duration); + } + }; + + Lock lock() { return Lock(this); } }; -} +} // namespace nix diff --git a/third_party/nix/src/libutil/thread-pool.cc b/third_party/nix/src/libutil/thread-pool.cc index 857ee91f87d0..5b5be926539d 100644 --- a/third_party/nix/src/libutil/thread-pool.cc +++ b/third_party/nix/src/libutil/thread-pool.cc @@ -3,154 +3,141 @@ namespace nix { -ThreadPool::ThreadPool(size_t _maxThreads) - : maxThreads(_maxThreads) -{ - restoreAffinity(); // FIXME - - if (!maxThreads) { - maxThreads = std::thread::hardware_concurrency(); - if (!maxThreads) maxThreads = 1; - } +ThreadPool::ThreadPool(size_t _maxThreads) : maxThreads(_maxThreads) { + restoreAffinity(); // FIXME - debug("starting pool of %d threads", maxThreads - 1); -} + if (!maxThreads) { + maxThreads = std::thread::hardware_concurrency(); + if (!maxThreads) maxThreads = 1; + } -ThreadPool::~ThreadPool() -{ - shutdown(); + debug("starting pool of %d threads", maxThreads - 1); } -void ThreadPool::shutdown() -{ - std::vector<std::thread> workers; - { - auto state(state_.lock()); - quit = true; - std::swap(workers, state->workers); - } +ThreadPool::~ThreadPool() { shutdown(); } + +void ThreadPool::shutdown() { + std::vector<std::thread> workers; + { + auto state(state_.lock()); + quit = true; + std::swap(workers, state->workers); + } - if (workers.empty()) return; + if (workers.empty()) return; - debug("reaping %d worker threads", workers.size()); + debug("reaping %d worker threads", workers.size()); - work.notify_all(); + work.notify_all(); - for (auto & thr : workers) - thr.join(); + for (auto& thr : workers) thr.join(); } -void ThreadPool::enqueue(const work_t & t) -{ - auto state(state_.lock()); - if (quit) - throw ThreadPoolShutDown("cannot enqueue a work item while the thread pool is shutting down"); - state->pending.push(t); - /* Note: process() also executes items, so count it as a worker. */ - if (state->pending.size() > state->workers.size() + 1 && state->workers.size() + 1 < maxThreads) - state->workers.emplace_back(&ThreadPool::doWork, this, false); - work.notify_one(); +void ThreadPool::enqueue(const work_t& t) { + auto state(state_.lock()); + if (quit) + throw ThreadPoolShutDown( + "cannot enqueue a work item while the thread pool is shutting down"); + state->pending.push(t); + /* Note: process() also executes items, so count it as a worker. */ + if (state->pending.size() > state->workers.size() + 1 && + state->workers.size() + 1 < maxThreads) + state->workers.emplace_back(&ThreadPool::doWork, this, false); + work.notify_one(); } -void ThreadPool::process() -{ - state_.lock()->draining = true; +void ThreadPool::process() { + state_.lock()->draining = true; - /* Do work until no more work is pending or active. */ - try { - doWork(true); + /* Do work until no more work is pending or active. */ + try { + doWork(true); - auto state(state_.lock()); + auto state(state_.lock()); - assert(quit); + assert(quit); - if (state->exception) - std::rethrow_exception(state->exception); + if (state->exception) std::rethrow_exception(state->exception); - } catch (...) { - /* In the exceptional case, some workers may still be - active. They may be referencing the stack frame of the - caller. So wait for them to finish. (~ThreadPool also does - this, but it might be destroyed after objects referenced by - the work item lambdas.) */ - shutdown(); - throw; - } + } catch (...) { + /* In the exceptional case, some workers may still be + active. They may be referencing the stack frame of the + caller. So wait for them to finish. (~ThreadPool also does + this, but it might be destroyed after objects referenced by + the work item lambdas.) */ + shutdown(); + throw; + } } -void ThreadPool::doWork(bool mainThread) -{ - if (!mainThread) - interruptCheck = [&]() { return (bool) quit; }; - - bool didWork = false; - std::exception_ptr exc; - - while (true) { - work_t w; - { - auto state(state_.lock()); - - if (didWork) { - assert(state->active); - state->active--; - - if (exc) { - - if (!state->exception) { - state->exception = exc; - // Tell the other workers to quit. - quit = true; - work.notify_all(); - } else { - /* Print the exception, since we can't - propagate it. */ - try { - std::rethrow_exception(exc); - } catch (std::exception & e) { - if (!dynamic_cast<Interrupted*>(&e) && - !dynamic_cast<ThreadPoolShutDown*>(&e)) - ignoreException(); - } catch (...) { - } - } - } - } - - /* Wait until a work item is available or we're asked to - quit. */ - while (true) { - if (quit) return; +void ThreadPool::doWork(bool mainThread) { + if (!mainThread) interruptCheck = [&]() { return (bool)quit; }; - if (!state->pending.empty()) break; + bool didWork = false; + std::exception_ptr exc; - /* If there are no active or pending items, and the - main thread is running process(), then no new items - can be added. So exit. */ - if (!state->active && state->draining) { - quit = true; - work.notify_all(); - return; - } - - state.wait(work); + while (true) { + work_t w; + { + auto state(state_.lock()); + + if (didWork) { + assert(state->active); + state->active--; + + if (exc) { + if (!state->exception) { + state->exception = exc; + // Tell the other workers to quit. + quit = true; + work.notify_all(); + } else { + /* Print the exception, since we can't + propagate it. */ + try { + std::rethrow_exception(exc); + } catch (std::exception& e) { + if (!dynamic_cast<Interrupted*>(&e) && + !dynamic_cast<ThreadPoolShutDown*>(&e)) + ignoreException(); + } catch (...) { } - - w = std::move(state->pending.front()); - state->pending.pop(); - state->active++; + } } - - try { - w(); - } catch (...) { - exc = std::current_exception(); + } + + /* Wait until a work item is available or we're asked to + quit. */ + while (true) { + if (quit) return; + + if (!state->pending.empty()) break; + + /* If there are no active or pending items, and the + main thread is running process(), then no new items + can be added. So exit. */ + if (!state->active && state->draining) { + quit = true; + work.notify_all(); + return; } - didWork = true; + state.wait(work); + } + + w = std::move(state->pending.front()); + state->pending.pop(); + state->active++; } -} -} + try { + w(); + } catch (...) { + exc = std::current_exception(); + } + didWork = true; + } +} +} // namespace nix diff --git a/third_party/nix/src/libutil/thread-pool.hh b/third_party/nix/src/libutil/thread-pool.hh index bb16b639a591..18fd208b1bb4 100644 --- a/third_party/nix/src/libutil/thread-pool.hh +++ b/third_party/nix/src/libutil/thread-pool.hh @@ -1,143 +1,129 @@ #pragma once -#include "sync.hh" -#include "util.hh" - -#include <queue> +#include <atomic> #include <functional> -#include <thread> #include <map> -#include <atomic> +#include <queue> +#include <thread> +#include "sync.hh" +#include "util.hh" namespace nix { MakeError(ThreadPoolShutDown, Error) -/* A simple thread pool that executes a queue of work items - (lambdas). */ -class ThreadPool -{ -public: + /* A simple thread pool that executes a queue of work items + (lambdas). */ + class ThreadPool { + public: + ThreadPool(size_t maxThreads = 0); - ThreadPool(size_t maxThreads = 0); + ~ThreadPool(); - ~ThreadPool(); + // FIXME: use std::packaged_task? + typedef std::function<void()> work_t; - // FIXME: use std::packaged_task? - typedef std::function<void()> work_t; + /* Enqueue a function to be executed by the thread pool. */ + void enqueue(const work_t& t); - /* Enqueue a function to be executed by the thread pool. */ - void enqueue(const work_t & t); + /* Execute work items until the queue is empty. Note that work + items are allowed to add new items to the queue; this is + handled correctly. Queue processing stops prematurely if any + work item throws an exception. This exception is propagated to + the calling thread. If multiple work items throw an exception + concurrently, only one item is propagated; the others are + printed on stderr and otherwise ignored. */ + void process(); - /* Execute work items until the queue is empty. Note that work - items are allowed to add new items to the queue; this is - handled correctly. Queue processing stops prematurely if any - work item throws an exception. This exception is propagated to - the calling thread. If multiple work items throw an exception - concurrently, only one item is propagated; the others are - printed on stderr and otherwise ignored. */ - void process(); + private: + size_t maxThreads; -private: + struct State { + std::queue<work_t> pending; + size_t active = 0; + std::exception_ptr exception; + std::vector<std::thread> workers; + bool draining = false; + }; - size_t maxThreads; + std::atomic_bool quit{false}; - struct State - { - std::queue<work_t> pending; - size_t active = 0; - std::exception_ptr exception; - std::vector<std::thread> workers; - bool draining = false; - }; + Sync<State> state_; - std::atomic_bool quit{false}; + std::condition_variable work; - Sync<State> state_; + void doWork(bool mainThread); - std::condition_variable work; - - void doWork(bool mainThread); - - void shutdown(); + void shutdown(); }; /* Process in parallel a set of items of type T that have a partial ordering between them. Thus, any item is only processed after all its dependencies have been processed. */ -template<typename T> -void processGraph( - ThreadPool & pool, - const std::set<T> & nodes, - std::function<std::set<T>(const T &)> getEdges, - std::function<void(const T &)> processNode) -{ - struct Graph { - std::set<T> left; - std::map<T, std::set<T>> refs, rrefs; - }; - - Sync<Graph> graph_(Graph{nodes, {}, {}}); - - std::function<void(const T &)> worker; - - worker = [&](const T & node) { - - { - auto graph(graph_.lock()); - auto i = graph->refs.find(node); - if (i == graph->refs.end()) - goto getRefs; - goto doWork; - } +template <typename T> +void processGraph(ThreadPool& pool, const std::set<T>& nodes, + std::function<std::set<T>(const T&)> getEdges, + std::function<void(const T&)> processNode) { + struct Graph { + std::set<T> left; + std::map<T, std::set<T>> refs, rrefs; + }; - getRefs: - { - auto refs = getEdges(node); - refs.erase(node); - - { - auto graph(graph_.lock()); - for (auto & ref : refs) - if (graph->left.count(ref)) { - graph->refs[node].insert(ref); - graph->rrefs[ref].insert(node); - } - if (graph->refs[node].empty()) - goto doWork; - } - } + Sync<Graph> graph_(Graph{nodes, {}, {}}); + + std::function<void(const T&)> worker; - return; - - doWork: - processNode(node); - - /* Enqueue work for all nodes that were waiting on this one - and have no unprocessed dependencies. */ - { - auto graph(graph_.lock()); - for (auto & rref : graph->rrefs[node]) { - auto & refs(graph->refs[rref]); - auto i = refs.find(node); - assert(i != refs.end()); - refs.erase(i); - if (refs.empty()) - pool.enqueue(std::bind(worker, rref)); - } - graph->left.erase(node); - graph->refs.erase(node); - graph->rrefs.erase(node); + worker = [&](const T& node) { + { + auto graph(graph_.lock()); + auto i = graph->refs.find(node); + if (i == graph->refs.end()) goto getRefs; + goto doWork; + } + + getRefs : { + auto refs = getEdges(node); + refs.erase(node); + + { + auto graph(graph_.lock()); + for (auto& ref : refs) + if (graph->left.count(ref)) { + graph->refs[node].insert(ref); + graph->rrefs[ref].insert(node); } - }; + if (graph->refs[node].empty()) goto doWork; + } + } - for (auto & node : nodes) - pool.enqueue(std::bind(worker, std::ref(node))); + return; - pool.process(); + doWork: + processNode(node); - if (!graph_.lock()->left.empty()) - throw Error("graph processing incomplete (cyclic reference?)"); + /* Enqueue work for all nodes that were waiting on this one + and have no unprocessed dependencies. */ + { + auto graph(graph_.lock()); + for (auto& rref : graph->rrefs[node]) { + auto& refs(graph->refs[rref]); + auto i = refs.find(node); + assert(i != refs.end()); + refs.erase(i); + if (refs.empty()) pool.enqueue(std::bind(worker, rref)); + } + graph->left.erase(node); + graph->refs.erase(node); + graph->rrefs.erase(node); + } + }; + + for (auto& node : nodes) pool.enqueue(std::bind(worker, std::ref(node))); + + pool.process(); + + if (!graph_.lock()->left.empty()) + throw Error("graph processing incomplete (cyclic reference?)"); } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/types.hh b/third_party/nix/src/libutil/types.hh index 92bf469b5c6f..ab2fee392286 100644 --- a/third_party/nix/src/libutil/types.hh +++ b/third_party/nix/src/libutil/types.hh @@ -1,15 +1,12 @@ #pragma once - -#include "ref.hh" - -#include <string> +#include <boost/format.hpp> #include <list> -#include <set> -#include <memory> #include <map> - -#include <boost/format.hpp> +#include <memory> +#include <set> +#include <string> +#include "ref.hh" /* Before 4.7, gcc's std::exception uses empty throw() specifiers for * its (virtual) destructor and what() in c++11 mode, in violation of spec @@ -20,131 +17,103 @@ #endif #endif - namespace nix { - /* Inherit some names from other namespaces for convenience. */ -using std::string; +using boost::format; using std::list; using std::set; +using std::string; using std::vector; -using boost::format; - /* A variadic template that does nothing. Useful to call a function for all variadic arguments but ignoring the result. */ -struct nop { template<typename... T> nop(T...) {} }; - - -struct FormatOrString -{ - string s; - FormatOrString(const string & s) : s(s) { }; - FormatOrString(const format & f) : s(f.str()) { }; - FormatOrString(const char * s) : s(s) { }; +struct nop { + template <typename... T> + nop(T...) {} }; +struct FormatOrString { + string s; + FormatOrString(const string& s) : s(s){}; + FormatOrString(const format& f) : s(f.str()){}; + FormatOrString(const char* s) : s(s){}; +}; /* 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 std::string fmt(const std::string & s) -{ - return s; -} +inline std::string fmt(const std::string& s) { return s; } -inline std::string fmt(const char * s) -{ - return s; -} +inline std::string fmt(const char* s) { return s; } -inline std::string fmt(const FormatOrString & fs) -{ - return fs.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); - f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); - nop{boost::io::detail::feed(f, args)...}; - return f.str(); +template <typename... Args> +inline std::string fmt(const std::string& fs, Args... args) { + boost::format f(fs); + f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); + nop{boost::io::detail::feed(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 -{ -protected: - string prefix_; // used for location traces etc. - string err; -public: - 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...)) - { - } +class BaseError : public std::exception { + protected: + string prefix_; // used for location traces etc. + string err; + + public: + 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(); } + ~BaseError() throw(){}; + const char* what() const throw() { return err.c_str(); } #else - const char * what() const noexcept { return err.c_str(); } + 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); + const string& msg() const { return err; } + const string& prefix() const { return prefix_; } + BaseError& addPrefix(const FormatOrString& fs); }; #define MakeError(newClass, superClass) \ - class newClass : public superClass \ - { \ - public: \ - using superClass::superClass; \ - }; + class newClass : public superClass { \ + public: \ + using superClass::superClass; \ + }; MakeError(Error, BaseError) -class SysError : public Error -{ -public: - int errNo; - - template<typename... Args> - SysError(Args... args) - : Error(addErrno(fmt(args...))) - { } + class SysError : public Error { + public: + int errNo; -private: + template <typename... Args> + SysError(Args... args) : Error(addErrno(fmt(args...))) {} - std::string addErrno(const std::string & s); + private: + std::string addErrno(const std::string& s); }; - typedef list<string> Strings; typedef set<string> StringSet; typedef std::map<std::string, std::string> StringMap; - /* Paths are just strings. */ typedef string Path; typedef list<Path> Paths; typedef set<Path> PathSet; - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/util.cc b/third_party/nix/src/libutil/util.cc index 05527473210d..1f0bc74cc72d 100644 --- a/third_party/nix/src/libutil/util.cc +++ b/third_party/nix/src/libutil/util.cc @@ -1,20 +1,4 @@ -#include "lazy.hh" #include "util.hh" -#include "affinity.hh" -#include "sync.hh" -#include "finally.hh" -#include "serialise.hh" - -#include <cctype> -#include <cerrno> -#include <cstdio> -#include <cstdlib> -#include <cstring> -#include <iostream> -#include <sstream> -#include <thread> -#include <future> - #include <fcntl.h> #include <grp.h> #include <limits.h> @@ -23,6 +7,20 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> +#include <cctype> +#include <cerrno> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <future> +#include <iostream> +#include <sstream> +#include <thread> +#include "affinity.hh" +#include "finally.hh" +#include "lazy.hh" +#include "serialise.hh" +#include "sync.hh" #ifdef __APPLE__ #include <sys/syscall.h> @@ -32,1040 +30,864 @@ #include <sys/prctl.h> #endif - -extern char * * environ __attribute__((weak)); - +extern char** environ __attribute__((weak)); namespace nix { - const std::string nativeSystem = SYSTEM; - -BaseError & BaseError::addPrefix(const FormatOrString & fs) -{ - prefix_ = fs.s + prefix_; - return *this; +BaseError& BaseError::addPrefix(const FormatOrString& fs) { + prefix_ = fs.s + prefix_; + return *this; } - -std::string SysError::addErrno(const std::string & s) -{ - errNo = errno; - return s + ": " + strerror(errNo); +std::string SysError::addErrno(const std::string& s) { + errNo = errno; + return s + ": " + strerror(errNo); } - -string getEnv(const string & key, const string & def) -{ - char * value = getenv(key.c_str()); - return value ? string(value) : def; +string getEnv(const string& key, const string& def) { + char* value = getenv(key.c_str()); + return value ? string(value) : 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; +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; } - -void clearEnv() -{ - for (auto & name : getEnv()) - unsetenv(name.first.c_str()); +void clearEnv() { + for (auto& name : getEnv()) unsetenv(name.first.c_str()); } -void replaceEnv(std::map<std::string, std::string> newEnv) -{ - clearEnv(); - for (auto newEnvVar : newEnv) - { - setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); - } +void replaceEnv(std::map<std::string, std::string> newEnv) { + clearEnv(); + for (auto newEnvVar : newEnv) { + setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); + } } - -Path absPath(Path path, Path dir) -{ - if (path[0] != '/') { - if (dir == "") { +Path absPath(Path path, Path dir) { + if (path[0] != '/') { + if (dir == "") { #ifdef __GNU__ - /* GNU (aka. GNU/Hurd) doesn't have any limitation on path - lengths and doesn't define `PATH_MAX'. */ - char *buf = getcwd(NULL, 0); - if (buf == NULL) + /* GNU (aka. GNU/Hurd) doesn't have any limitation on path + lengths and doesn't define `PATH_MAX'. */ + char* buf = getcwd(NULL, 0); + if (buf == NULL) #else - char buf[PATH_MAX]; - if (!getcwd(buf, sizeof(buf))) + char buf[PATH_MAX]; + if (!getcwd(buf, sizeof(buf))) #endif - throw SysError("cannot get cwd"); - dir = buf; + throw SysError("cannot get cwd"); + dir = buf; #ifdef __GNU__ - free(buf); + free(buf); #endif - } - path = dir + "/" + path; } - return canonPath(path); + path = dir + "/" + path; + } + return canonPath(path); } +Path canonPath(const Path& path, bool resolveSymlinks) { + assert(path != ""); -Path canonPath(const Path & path, bool resolveSymlinks) -{ - assert(path != ""); - - string s; + string s; - if (path[0] != '/') - throw Error(format("not an absolute path: '%1%'") % path); + if (path[0] != '/') throw Error(format("not an absolute path: '%1%'") % path); - string::const_iterator i = path.begin(), end = path.end(); - string temp; + string::const_iterator i = path.begin(), end = path.end(); + string temp; - /* Count the number of times we follow a symlink and stop at some - arbitrary (but high) limit to prevent infinite loops. */ - unsigned int followCount = 0, maxFollow = 1024; + /* Count the number of times we follow a symlink and stop at some + arbitrary (but high) limit to prevent infinite loops. */ + unsigned int followCount = 0, maxFollow = 1024; - while (1) { + while (1) { + /* Skip slashes. */ + while (i != end && *i == '/') i++; + if (i == end) break; - /* Skip slashes. */ - while (i != end && *i == '/') i++; - if (i == end) break; + /* Ignore `.'. */ + if (*i == '.' && (i + 1 == end || i[1] == '/')) i++; - /* Ignore `.'. */ - if (*i == '.' && (i + 1 == end || i[1] == '/')) - i++; - - /* If `..', delete the last component. */ - else if (*i == '.' && i + 1 < end && i[1] == '.' && - (i + 2 == end || i[2] == '/')) - { - if (!s.empty()) s.erase(s.rfind('/')); - i += 2; - } + /* If `..', delete the last component. */ + else if (*i == '.' && i + 1 < end && i[1] == '.' && + (i + 2 == end || i[2] == '/')) { + if (!s.empty()) s.erase(s.rfind('/')); + i += 2; + } - /* Normal component; copy it. */ - else { - s += '/'; - while (i != end && *i != '/') s += *i++; - - /* If s points to a symlink, resolve it and restart (since - the symlink target might contain new symlinks). */ - if (resolveSymlinks && isLink(s)) { - if (++followCount >= maxFollow) - throw Error(format("infinite symlink recursion in path '%1%'") % path); - temp = absPath(readLink(s), dirOf(s)) - + string(i, end); - i = temp.begin(); /* restart */ - end = temp.end(); - s = ""; - } - } + /* Normal component; copy it. */ + else { + s += '/'; + while (i != end && *i != '/') s += *i++; + + /* If s points to a symlink, resolve it and restart (since + the symlink target might contain new symlinks). */ + if (resolveSymlinks && isLink(s)) { + if (++followCount >= maxFollow) + throw Error(format("infinite symlink recursion in path '%1%'") % + path); + temp = absPath(readLink(s), dirOf(s)) + string(i, end); + i = temp.begin(); /* restart */ + end = temp.end(); + s = ""; + } } + } - return s.empty() ? "/" : s; + return s.empty() ? "/" : s; } - -Path dirOf(const Path & path) -{ - Path::size_type pos = path.rfind('/'); - if (pos == string::npos) - return "."; - return pos == 0 ? "/" : Path(path, 0, pos); +Path dirOf(const Path& path) { + Path::size_type pos = path.rfind('/'); + if (pos == string::npos) return "."; + return pos == 0 ? "/" : Path(path, 0, pos); } +string baseNameOf(const Path& path) { + if (path.empty()) return ""; -string baseNameOf(const Path & path) -{ - if (path.empty()) - return ""; - - Path::size_type last = path.length() - 1; - if (path[last] == '/' && last > 0) - last -= 1; + Path::size_type last = path.length() - 1; + if (path[last] == '/' && last > 0) last -= 1; - Path::size_type pos = path.rfind('/', last); - if (pos == string::npos) - pos = 0; - else - pos += 1; + Path::size_type pos = path.rfind('/', last); + if (pos == string::npos) + pos = 0; + else + pos += 1; - return string(path, pos, last - pos + 1); + return string(path, pos, last - pos + 1); } - -bool isInDir(const Path & path, const Path & dir) -{ - return path[0] == '/' - && string(path, 0, dir.size()) == dir - && path.size() >= dir.size() + 2 - && path[dir.size()] == '/'; +bool isInDir(const Path& path, const Path& dir) { + return path[0] == '/' && string(path, 0, dir.size()) == dir && + path.size() >= dir.size() + 2 && path[dir.size()] == '/'; } - -bool isDirOrInDir(const Path & path, const Path & dir) -{ - return path == dir || isInDir(path, dir); +bool isDirOrInDir(const Path& path, const Path& dir) { + return path == dir || isInDir(path, dir); } - -struct stat lstat(const Path & path) -{ - struct stat st; - if (lstat(path.c_str(), &st)) - throw SysError(format("getting status of '%1%'") % path); - return st; +struct stat lstat(const Path& path) { + struct stat st; + if (lstat(path.c_str(), &st)) + throw SysError(format("getting status of '%1%'") % path); + return st; } - -bool pathExists(const Path & path) -{ - int res; - struct stat st; - res = lstat(path.c_str(), &st); - if (!res) return true; - if (errno != ENOENT && errno != ENOTDIR) - throw SysError(format("getting status of %1%") % path); - return false; +bool pathExists(const Path& path) { + int res; + struct stat st; + res = lstat(path.c_str(), &st); + if (!res) return true; + if (errno != ENOENT && errno != ENOTDIR) + throw SysError(format("getting status of %1%") % path); + return false; } - -Path readLink(const Path & path) -{ - checkInterrupt(); - std::vector<char> buf; - for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) { - buf.resize(bufSize); - ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize); - if (rlSize == -1) - if (errno == EINVAL) - throw Error("'%1%' is not a symlink", path); - else - throw SysError("reading symbolic link '%1%'", path); - else if (rlSize < bufSize) - return string(buf.data(), rlSize); - } +Path readLink(const Path& path) { + checkInterrupt(); + std::vector<char> buf; + for (ssize_t bufSize = PATH_MAX / 4; true; bufSize += bufSize / 2) { + buf.resize(bufSize); + ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize); + if (rlSize == -1) + if (errno == EINVAL) + throw Error("'%1%' is not a symlink", path); + else + throw SysError("reading symbolic link '%1%'", path); + else if (rlSize < bufSize) + return string(buf.data(), rlSize); + } } - -bool isLink(const Path & path) -{ - struct stat st = lstat(path); - return S_ISLNK(st.st_mode); +bool isLink(const Path& path) { + struct stat st = lstat(path); + return S_ISLNK(st.st_mode); } +DirEntries readDirectory(const Path& path) { + DirEntries entries; + entries.reserve(64); -DirEntries readDirectory(const Path & path) -{ - DirEntries entries; - entries.reserve(64); + AutoCloseDir dir(opendir(path.c_str())); + if (!dir) throw SysError(format("opening directory '%1%'") % path); - AutoCloseDir dir(opendir(path.c_str())); - if (!dir) throw SysError(format("opening directory '%1%'") % path); - - struct dirent * dirent; - while (errno = 0, dirent = readdir(dir.get())) { /* sic */ - checkInterrupt(); - string name = dirent->d_name; - if (name == "." || name == "..") continue; - entries.emplace_back(name, dirent->d_ino, + struct dirent* dirent; + while (errno = 0, dirent = readdir(dir.get())) { /* sic */ + checkInterrupt(); + string name = dirent->d_name; + if (name == "." || name == "..") continue; + entries.emplace_back(name, dirent->d_ino, #ifdef HAVE_STRUCT_DIRENT_D_TYPE - dirent->d_type + dirent->d_type #else - DT_UNKNOWN + DT_UNKNOWN #endif - ); - } - if (errno) throw SysError(format("reading directory '%1%'") % path); + ); + } + if (errno) throw SysError(format("reading directory '%1%'") % path); - return entries; + return entries; } - -unsigned char getFileType(const Path & path) -{ - struct stat st = lstat(path); - if (S_ISDIR(st.st_mode)) return DT_DIR; - if (S_ISLNK(st.st_mode)) return DT_LNK; - if (S_ISREG(st.st_mode)) return DT_REG; - return DT_UNKNOWN; +unsigned char getFileType(const Path& path) { + struct stat st = lstat(path); + if (S_ISDIR(st.st_mode)) return DT_DIR; + if (S_ISLNK(st.st_mode)) return DT_LNK; + if (S_ISREG(st.st_mode)) return DT_REG; + return DT_UNKNOWN; } +string readFile(int fd) { + struct stat st; + if (fstat(fd, &st) == -1) throw SysError("statting file"); -string readFile(int fd) -{ - struct stat st; - if (fstat(fd, &st) == -1) - throw SysError("statting file"); + std::vector<unsigned char> buf(st.st_size); + readFull(fd, buf.data(), st.st_size); - std::vector<unsigned char> buf(st.st_size); - readFull(fd, buf.data(), st.st_size); - - return string((char *) buf.data(), st.st_size); + return string((char*)buf.data(), st.st_size); } - -string readFile(const Path & path, bool drain) -{ - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) - throw SysError(format("opening file '%1%'") % path); - return drain ? drainFD(fd.get()) : readFile(fd.get()); +string readFile(const Path& path, bool drain) { + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + if (!fd) throw SysError(format("opening file '%1%'") % path); + return drain ? drainFD(fd.get()) : readFile(fd.get()); } - -void readFile(const Path & path, Sink & sink) -{ - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) throw SysError("opening file '%s'", path); - drainFD(fd.get(), sink); +void readFile(const Path& path, Sink& sink) { + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + if (!fd) throw SysError("opening file '%s'", path); + drainFD(fd.get(), sink); } - -void writeFile(const Path & path, const string & s, mode_t mode) -{ - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); - if (!fd) - throw SysError(format("opening file '%1%'") % path); - writeFull(fd.get(), s); +void writeFile(const Path& path, const string& s, mode_t mode) { + AutoCloseFD fd = + open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + if (!fd) throw SysError(format("opening file '%1%'") % path); + writeFull(fd.get(), s); } +void writeFile(const Path& path, Source& source, mode_t mode) { + AutoCloseFD fd = + open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + if (!fd) throw SysError(format("opening file '%1%'") % path); -void writeFile(const Path & path, Source & source, mode_t mode) -{ - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); - if (!fd) - throw SysError(format("opening file '%1%'") % path); + std::vector<unsigned char> buf(64 * 1024); - std::vector<unsigned char> buf(64 * 1024); - - while (true) { - try { - auto n = source.read(buf.data(), buf.size()); - writeFull(fd.get(), (unsigned char *) buf.data(), n); - } catch (EndOfFile &) { break; } + while (true) { + try { + auto n = source.read(buf.data(), buf.size()); + writeFull(fd.get(), (unsigned char*)buf.data(), n); + } catch (EndOfFile&) { + break; } + } } - -string readLine(int fd) -{ - string s; - while (1) { - checkInterrupt(); - char ch; - // FIXME: inefficient - ssize_t rd = read(fd, &ch, 1); - if (rd == -1) { - if (errno != EINTR) - throw SysError("reading a line"); - } else if (rd == 0) - throw EndOfFile("unexpected EOF reading a line"); - else { - if (ch == '\n') return s; - s += ch; - } +string readLine(int fd) { + string s; + while (1) { + checkInterrupt(); + char ch; + // FIXME: inefficient + ssize_t rd = read(fd, &ch, 1); + if (rd == -1) { + if (errno != EINTR) throw SysError("reading a line"); + } else if (rd == 0) + throw EndOfFile("unexpected EOF reading a line"); + else { + if (ch == '\n') return s; + s += ch; } + } } - -void writeLine(int fd, string s) -{ - s += '\n'; - writeFull(fd, s); +void writeLine(int fd, string s) { + s += '\n'; + writeFull(fd, s); } +static void _deletePath(const Path& path, unsigned long long& bytesFreed) { + checkInterrupt(); -static void _deletePath(const Path & path, unsigned long long & bytesFreed) -{ - checkInterrupt(); + struct stat st; + if (lstat(path.c_str(), &st) == -1) { + if (errno == ENOENT) return; + throw SysError(format("getting status of '%1%'") % path); + } - struct stat st; - if (lstat(path.c_str(), &st) == -1) { - if (errno == ENOENT) return; - throw SysError(format("getting status of '%1%'") % path); - } - - if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) - bytesFreed += st.st_size; - - if (S_ISDIR(st.st_mode)) { - /* 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); - } + if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) bytesFreed += st.st_size; - for (auto & i : readDirectory(path)) - _deletePath(path + "/" + i.name, bytesFreed); + if (S_ISDIR(st.st_mode)) { + /* 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); } - if (remove(path.c_str()) == -1) { - if (errno == ENOENT) return; - throw SysError(format("cannot unlink '%1%'") % path); - } -} + for (auto& i : readDirectory(path)) + _deletePath(path + "/" + i.name, bytesFreed); + } - -void deletePath(const Path & path) -{ - unsigned long long dummy; - deletePath(path, dummy); + if (remove(path.c_str()) == -1) { + if (errno == ENOENT) return; + throw SysError(format("cannot unlink '%1%'") % path); + } } - -void deletePath(const Path & path, unsigned long long & bytesFreed) -{ - //Activity act(*logger, lvlDebug, format("recursively deleting path '%1%'") % path); - bytesFreed = 0; - _deletePath(path, bytesFreed); +void deletePath(const Path& path) { + unsigned long long dummy; + deletePath(path, dummy); } - -static Path tempName(Path tmpRoot, const Path & prefix, bool includePid, - int & counter) -{ - tmpRoot = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true); - if (includePid) - return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++).str(); - else - return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str(); +void deletePath(const Path& path, unsigned long long& bytesFreed) { + // Activity act(*logger, lvlDebug, format("recursively deleting path '%1%'") % + // path); + bytesFreed = 0; + _deletePath(path, bytesFreed); } +static Path tempName(Path tmpRoot, const Path& prefix, bool includePid, + int& counter) { + tmpRoot = + canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true); + if (includePid) + return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++) + .str(); + else + return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str(); +} -Path createTempDir(const Path & tmpRoot, const Path & prefix, - bool includePid, bool useGlobalCounter, mode_t mode) -{ - static int globalCounter = 0; - int localCounter = 0; - int & counter(useGlobalCounter ? globalCounter : localCounter); +Path createTempDir(const Path& tmpRoot, const Path& prefix, bool includePid, + bool useGlobalCounter, mode_t mode) { + static int globalCounter = 0; + int localCounter = 0; + int& counter(useGlobalCounter ? globalCounter : localCounter); - while (1) { - checkInterrupt(); - Path tmpDir = tempName(tmpRoot, prefix, includePid, counter); - if (mkdir(tmpDir.c_str(), mode) == 0) { + while (1) { + checkInterrupt(); + Path tmpDir = tempName(tmpRoot, prefix, includePid, counter); + if (mkdir(tmpDir.c_str(), mode) == 0) { #if __FreeBSD__ - /* Explicitly set the group of the directory. This is to - work around around problems caused by BSD's group - ownership semantics (directories inherit the group of - the parent). For instance, the group of /tmp on - FreeBSD is "wheel", so all directories created in /tmp - will be owned by "wheel"; but if the user is not in - "wheel", then "tar" will fail to unpack archives that - have the setgid bit set on directories. */ - if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0) - throw SysError(format("setting group of directory '%1%'") % tmpDir); + /* Explicitly set the group of the directory. This is to + work around around problems caused by BSD's group + ownership semantics (directories inherit the group of + the parent). For instance, the group of /tmp on + FreeBSD is "wheel", so all directories created in /tmp + will be owned by "wheel"; but if the user is not in + "wheel", then "tar" will fail to unpack archives that + have the setgid bit set on directories. */ + if (chown(tmpDir.c_str(), (uid_t)-1, getegid()) != 0) + throw SysError(format("setting group of directory '%1%'") % tmpDir); #endif - return tmpDir; - } - if (errno != EEXIST) - throw SysError(format("creating directory '%1%'") % tmpDir); + return tmpDir; } + if (errno != EEXIST) + throw SysError(format("creating directory '%1%'") % tmpDir); + } } - -std::string getUserName() -{ - auto pw = getpwuid(geteuid()); - std::string name = pw ? pw->pw_name : getEnv("USER", ""); - if (name.empty()) - throw Error("cannot figure out user name"); - return name; +std::string getUserName() { + auto pw = getpwuid(geteuid()); + std::string name = pw ? pw->pw_name : getEnv("USER", ""); + if (name.empty()) throw Error("cannot figure out user name"); + return name; } - static Lazy<Path> getHome2([]() { - Path homeDir = getEnv("HOME"); - if (homeDir.empty()) { - std::vector<char> buf(16384); - struct passwd pwbuf; - struct passwd * pw; - if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0 - || !pw || !pw->pw_dir || !pw->pw_dir[0]) - throw Error("cannot determine user's home directory"); - homeDir = pw->pw_dir; - } - return homeDir; + Path homeDir = getEnv("HOME"); + if (homeDir.empty()) { + std::vector<char> buf(16384); + struct passwd pwbuf; + struct passwd* pw; + if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0 || + !pw || !pw->pw_dir || !pw->pw_dir[0]) + throw Error("cannot determine user's home directory"); + homeDir = pw->pw_dir; + } + return homeDir; }); Path getHome() { return getHome2(); } - -Path getCacheDir() -{ - Path cacheDir = getEnv("XDG_CACHE_HOME"); - if (cacheDir.empty()) - cacheDir = getHome() + "/.cache"; - return cacheDir; +Path getCacheDir() { + Path cacheDir = getEnv("XDG_CACHE_HOME"); + if (cacheDir.empty()) cacheDir = getHome() + "/.cache"; + return cacheDir; } - -Path getConfigDir() -{ - Path configDir = getEnv("XDG_CONFIG_HOME"); - if (configDir.empty()) - configDir = getHome() + "/.config"; - return configDir; +Path getConfigDir() { + Path configDir = getEnv("XDG_CONFIG_HOME"); + if (configDir.empty()) configDir = getHome() + "/.config"; + return configDir; } -std::vector<Path> getConfigDirs() -{ - Path configHome = getConfigDir(); - string configDirs = getEnv("XDG_CONFIG_DIRS"); - std::vector<Path> result = tokenizeString<std::vector<string>>(configDirs, ":"); - result.insert(result.begin(), configHome); - return result; +std::vector<Path> getConfigDirs() { + Path configHome = getConfigDir(); + string configDirs = getEnv("XDG_CONFIG_DIRS"); + std::vector<Path> result = + tokenizeString<std::vector<string>>(configDirs, ":"); + result.insert(result.begin(), configHome); + return result; } - -Path getDataDir() -{ - Path dataDir = getEnv("XDG_DATA_HOME"); - if (dataDir.empty()) - dataDir = getHome() + "/.local/share"; - return dataDir; +Path getDataDir() { + Path dataDir = getEnv("XDG_DATA_HOME"); + if (dataDir.empty()) dataDir = getHome() + "/.local/share"; + return dataDir; } +Paths createDirs(const Path& path) { + Paths created; + if (path == "/") return created; -Paths createDirs(const Path & path) -{ - Paths created; - if (path == "/") return created; - - struct stat st; - if (lstat(path.c_str(), &st) == -1) { - created = createDirs(dirOf(path)); - if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST) - throw SysError(format("creating directory '%1%'") % path); - st = lstat(path); - created.push_back(path); - } + struct stat st; + if (lstat(path.c_str(), &st) == -1) { + created = createDirs(dirOf(path)); + if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST) + throw SysError(format("creating directory '%1%'") % path); + st = lstat(path); + created.push_back(path); + } - if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1) - throw SysError(format("statting symlink '%1%'") % path); + if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1) + throw SysError(format("statting symlink '%1%'") % path); - if (!S_ISDIR(st.st_mode)) throw Error(format("'%1%' is not a directory") % path); + if (!S_ISDIR(st.st_mode)) + throw Error(format("'%1%' is not a directory") % path); - return created; + return created; } - -void createSymlink(const Path & target, const Path & link) -{ - if (symlink(target.c_str(), link.c_str())) - throw SysError(format("creating symlink from '%1%' to '%2%'") % link % target); +void createSymlink(const Path& target, const Path& link) { + if (symlink(target.c_str(), link.c_str())) + throw SysError(format("creating symlink from '%1%' to '%2%'") % link % + target); } +void replaceSymlink(const Path& target, const Path& link) { + for (unsigned int n = 0; true; n++) { + Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link))); -void replaceSymlink(const Path & target, const Path & link) -{ - for (unsigned int n = 0; true; n++) { - Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link))); - - try { - createSymlink(target, tmp); - } catch (SysError & e) { - if (e.errNo == EEXIST) continue; - throw; - } - - if (rename(tmp.c_str(), link.c_str()) != 0) - throw SysError(format("renaming '%1%' to '%2%'") % tmp % link); - - break; + try { + createSymlink(target, tmp); + } catch (SysError& e) { + if (e.errNo == EEXIST) continue; + throw; } -} + if (rename(tmp.c_str(), link.c_str()) != 0) + throw SysError(format("renaming '%1%' to '%2%'") % tmp % link); -void readFull(int fd, unsigned char * buf, size_t count) -{ - while (count) { - checkInterrupt(); - ssize_t res = read(fd, (char *) buf, count); - if (res == -1) { - if (errno == EINTR) continue; - throw SysError("reading from file"); - } - if (res == 0) throw EndOfFile("unexpected end-of-file"); - count -= res; - buf += res; - } + break; + } } - -void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts) -{ - while (count) { - if (allowInterrupts) checkInterrupt(); - ssize_t res = write(fd, (char *) buf, count); - if (res == -1 && errno != EINTR) - throw SysError("writing to file"); - if (res > 0) { - count -= res; - buf += res; - } +void readFull(int fd, unsigned char* buf, size_t count) { + while (count) { + checkInterrupt(); + ssize_t res = read(fd, (char*)buf, count); + if (res == -1) { + if (errno == EINTR) continue; + throw SysError("reading from file"); + } + if (res == 0) throw EndOfFile("unexpected end-of-file"); + count -= res; + buf += res; + } +} + +void writeFull(int fd, const unsigned char* buf, size_t count, + bool allowInterrupts) { + while (count) { + if (allowInterrupts) checkInterrupt(); + ssize_t res = write(fd, (char*)buf, count); + if (res == -1 && errno != EINTR) throw SysError("writing to file"); + if (res > 0) { + count -= res; + buf += res; } + } } - -void writeFull(int fd, const string & s, bool allowInterrupts) -{ - writeFull(fd, (const unsigned char *) s.data(), s.size(), allowInterrupts); +void writeFull(int fd, const string& s, bool allowInterrupts) { + writeFull(fd, (const unsigned char*)s.data(), s.size(), allowInterrupts); } - -string drainFD(int fd, bool block) -{ - StringSink sink; - drainFD(fd, sink, block); - return std::move(*sink.s); +string drainFD(int fd, bool block) { + StringSink sink; + drainFD(fd, sink, block); + return std::move(*sink.s); } +void drainFD(int fd, Sink& sink, bool block) { + int saved; -void drainFD(int fd, Sink & sink, bool block) -{ - int saved; - - Finally finally([&]() { - if (!block) { - if (fcntl(fd, F_SETFL, saved) == -1) - throw SysError("making file descriptor blocking"); - } - }); - + Finally finally([&]() { if (!block) { - saved = fcntl(fd, F_GETFL); - if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) - throw SysError("making file descriptor non-blocking"); - } - - std::vector<unsigned char> buf(64 * 1024); - while (1) { - checkInterrupt(); - ssize_t rd = read(fd, buf.data(), buf.size()); - if (rd == -1) { - if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) - break; - if (errno != EINTR) - throw SysError("reading from file"); - } - else if (rd == 0) break; - else sink(buf.data(), rd); + if (fcntl(fd, F_SETFL, saved) == -1) + throw SysError("making file descriptor blocking"); } -} + }); + if (!block) { + saved = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) + throw SysError("making file descriptor non-blocking"); + } + std::vector<unsigned char> buf(64 * 1024); + while (1) { + checkInterrupt(); + ssize_t rd = read(fd, buf.data(), buf.size()); + if (rd == -1) { + if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) break; + if (errno != EINTR) throw SysError("reading from file"); + } else if (rd == 0) + break; + else + sink(buf.data(), rd); + } +} ////////////////////////////////////////////////////////////////////// - AutoDelete::AutoDelete() : del{false} {} -AutoDelete::AutoDelete(const string & p, bool recursive) : path(p) -{ - del = true; - this->recursive = recursive; +AutoDelete::AutoDelete(const string& p, bool recursive) : path(p) { + del = true; + this->recursive = recursive; } -AutoDelete::~AutoDelete() -{ - try { - if (del) { - if (recursive) - deletePath(path); - else { - if (remove(path.c_str()) == -1) - throw SysError(format("cannot unlink '%1%'") % path); - } - } - } catch (...) { - ignoreException(); +AutoDelete::~AutoDelete() { + try { + if (del) { + if (recursive) + deletePath(path); + else { + if (remove(path.c_str()) == -1) + throw SysError(format("cannot unlink '%1%'") % path); + } } + } catch (...) { + ignoreException(); + } } -void AutoDelete::cancel() -{ - del = false; -} +void AutoDelete::cancel() { del = false; } -void AutoDelete::reset(const Path & p, bool recursive) { - path = p; - this->recursive = recursive; - del = true; +void AutoDelete::reset(const Path& p, bool recursive) { + path = p; + this->recursive = recursive; + del = true; } - - ////////////////////////////////////////////////////////////////////// - AutoCloseFD::AutoCloseFD() : fd{-1} {} - AutoCloseFD::AutoCloseFD(int fd) : fd{fd} {} +AutoCloseFD::AutoCloseFD(AutoCloseFD&& that) : fd{that.fd} { that.fd = -1; } -AutoCloseFD::AutoCloseFD(AutoCloseFD&& that) : fd{that.fd} -{ - that.fd = -1; +AutoCloseFD& AutoCloseFD::operator=(AutoCloseFD&& that) { + close(); + fd = that.fd; + that.fd = -1; + return *this; } - -AutoCloseFD& AutoCloseFD::operator =(AutoCloseFD&& that) -{ +AutoCloseFD::~AutoCloseFD() { + try { close(); - fd = that.fd; - that.fd = -1; - return *this; + } catch (...) { + ignoreException(); + } } +int AutoCloseFD::get() const { return fd; } -AutoCloseFD::~AutoCloseFD() -{ - try { - close(); - } catch (...) { - ignoreException(); - } +void AutoCloseFD::close() { + if (fd != -1) { + if (::close(fd) == -1) /* This should never happen. */ + throw SysError(format("closing file descriptor %1%") % fd); + } } +AutoCloseFD::operator bool() const { return fd != -1; } -int AutoCloseFD::get() const -{ - return fd; +int AutoCloseFD::release() { + int oldFD = fd; + fd = -1; + return oldFD; } - -void AutoCloseFD::close() -{ - if (fd != -1) { - if (::close(fd) == -1) - /* This should never happen. */ - throw SysError(format("closing file descriptor %1%") % fd); - } -} - - -AutoCloseFD::operator bool() const -{ - return fd != -1; -} - - -int AutoCloseFD::release() -{ - int oldFD = fd; - fd = -1; - return oldFD; -} - - -void Pipe::create() -{ - int fds[2]; +void Pipe::create() { + int fds[2]; #if HAVE_PIPE2 - if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); + if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); #else - if (pipe(fds) != 0) throw SysError("creating pipe"); - closeOnExec(fds[0]); - closeOnExec(fds[1]); + if (pipe(fds) != 0) throw SysError("creating pipe"); + closeOnExec(fds[0]); + closeOnExec(fds[1]); #endif - readSide = fds[0]; - writeSide = fds[1]; + readSide = fds[0]; + writeSide = fds[1]; } - - ////////////////////////////////////////////////////////////////////// +Pid::Pid() {} -Pid::Pid() -{ -} - - -Pid::Pid(pid_t pid) - : pid(pid) -{ -} - - -Pid::~Pid() -{ - if (pid != -1) kill(); -} - +Pid::Pid(pid_t pid) : pid(pid) {} -void Pid::operator =(pid_t pid) -{ - if (this->pid != -1 && this->pid != pid) kill(); - this->pid = pid; - killSignal = SIGKILL; // reset signal to default +Pid::~Pid() { + if (pid != -1) kill(); } - -Pid::operator pid_t() -{ - return pid; +void Pid::operator=(pid_t pid) { + if (this->pid != -1 && this->pid != pid) kill(); + this->pid = pid; + killSignal = SIGKILL; // reset signal to default } +Pid::operator pid_t() { return pid; } -int Pid::kill() -{ - assert(pid != -1); +int Pid::kill() { + assert(pid != -1); - debug(format("killing process %1%") % pid); + debug(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) { - /* On BSDs, killing a process group will return EPERM if all - processes in the group are zombies (or something like - that). So try to detect and ignore that situation. */ + /* 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) { + /* On BSDs, killing a process group will return EPERM if all + processes in the group are zombies (or something like + that). So try to detect and ignore that situation. */ #if __FreeBSD__ || __APPLE__ - if (errno != EPERM || ::kill(pid, 0) != 0) + if (errno != EPERM || ::kill(pid, 0) != 0) #endif - printError((SysError("killing process %d", pid).msg())); - } + printError((SysError("killing process %d", pid).msg())); + } - return wait(); + return wait(); } - -int Pid::wait() -{ - assert(pid != -1); - while (1) { - int status; - int res = waitpid(pid, &status, 0); - if (res == pid) { - pid = -1; - return status; - } - if (errno != EINTR) - throw SysError("cannot get child exit status"); - checkInterrupt(); +int Pid::wait() { + assert(pid != -1); + while (1) { + int status; + int res = waitpid(pid, &status, 0); + if (res == pid) { + pid = -1; + return status; } + if (errno != EINTR) throw SysError("cannot get child exit status"); + checkInterrupt(); + } } +void Pid::setSeparatePG(bool separatePG) { this->separatePG = separatePG; } -void Pid::setSeparatePG(bool separatePG) -{ - this->separatePG = separatePG; -} - - -void Pid::setKillSignal(int signal) -{ - this->killSignal = signal; -} - +void Pid::setKillSignal(int signal) { this->killSignal = signal; } -pid_t Pid::release() -{ - pid_t p = pid; - pid = -1; - return p; +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); -void killUser(uid_t uid) -{ - debug(format("killing all processes running under uid '%1%'") % uid); + assert(uid != 0); /* just to be safe... */ - assert(uid != 0); /* just to be safe... */ + /* The system call kill(-1, sig) sends the signal `sig' to all + users to which the current process can send signals. So we + fork a process, switch to uid, and send a mass kill. */ - /* The system call kill(-1, sig) sends the signal `sig' to all - users to which the current process can send signals. So we - fork a process, switch to uid, and send a mass kill. */ + ProcessOptions options; + options.allowVfork = false; - ProcessOptions options; - options.allowVfork = false; - - Pid pid = startProcess([&]() { - - if (setuid(uid) == -1) - throw SysError("setting uid"); + Pid pid = startProcess( + [&]() { + if (setuid(uid) == -1) throw SysError("setting uid"); while (true) { #ifdef __APPLE__ - /* OSX's kill syscall takes a third parameter that, among - other things, determines if kill(-1, signo) affects the - calling process. In the OSX libc, it's set to true, - which means "follow POSIX", which we don't want here - */ - if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; + /* OSX's kill syscall takes a third parameter that, among + other things, determines if kill(-1, signo) affects the + calling process. In the OSX libc, it's set to true, + which means "follow POSIX", which we don't want here + */ + if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; #else - if (kill(-1, SIGKILL) == 0) break; + if (kill(-1, SIGKILL) == 0) break; #endif - if (errno == ESRCH) break; /* no more processes */ - if (errno != EINTR) - throw SysError(format("cannot kill processes for uid '%1%'") % uid); + if (errno == ESRCH) break; /* no more processes */ + if (errno != EINTR) + throw SysError(format("cannot kill processes for uid '%1%'") % uid); } _exit(0); - }, options); + }, + options); - int status = pid.wait(); - if (status != 0) - throw Error(format("cannot kill processes for uid '%1%': %2%") % uid % statusToString(status)); + int status = pid.wait(); + if (status != 0) + throw Error(format("cannot kill processes for uid '%1%': %2%") % uid % + statusToString(status)); - /* !!! We should really do some check to make sure that there are - no processes left running under `uid', but there is no portable - way to do so (I think). The most reliable way may be `ps -eo - uid | grep -q $uid'. */ + /* !!! We should really do some check to make sure that there are + no processes left running under `uid', but there is no portable + way to do so (I think). The most reliable way may be `ps -eo + uid | grep -q $uid'. */ } - ////////////////////////////////////////////////////////////////////// - /* Wrapper around vfork to prevent the child process from clobbering the caller's stack frame in the parent. */ -static pid_t doFork(bool allowVfork, std::function<void()> fun) __attribute__((noinline)); static pid_t doFork(bool allowVfork, std::function<void()> fun) -{ + __attribute__((noinline)); +static pid_t doFork(bool allowVfork, std::function<void()> fun) { #ifdef __linux__ - pid_t pid = allowVfork ? vfork() : fork(); + pid_t pid = allowVfork ? vfork() : fork(); #else - pid_t pid = fork(); + pid_t pid = fork(); #endif - if (pid != 0) return pid; - fun(); - abort(); + if (pid != 0) return pid; + fun(); + abort(); } - -pid_t startProcess(std::function<void()> fun, const ProcessOptions & options) -{ - auto wrapper = [&]() { - if (!options.allowVfork) - logger = makeDefaultLogger(); - try { +pid_t startProcess(std::function<void()> fun, const ProcessOptions& options) { + auto wrapper = [&]() { + if (!options.allowVfork) logger = makeDefaultLogger(); + try { #if __linux__ - if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) - throw SysError("setting death signal"); + if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) + throw SysError("setting death signal"); #endif - restoreAffinity(); - fun(); - } catch (std::exception & e) { - try { - std::cerr << options.errorPrefix << e.what() << "\n"; - } catch (...) { } - } catch (...) { } - if (options.runExitHandlers) - exit(1); - else - _exit(1); - }; + restoreAffinity(); + fun(); + } catch (std::exception& e) { + try { + std::cerr << options.errorPrefix << e.what() << "\n"; + } catch (...) { + } + } catch (...) { + } + if (options.runExitHandlers) + exit(1); + else + _exit(1); + }; - pid_t pid = doFork(options.allowVfork, wrapper); - if (pid == -1) throw SysError("unable to fork"); + pid_t pid = doFork(options.allowVfork, wrapper); + if (pid == -1) throw SysError("unable to fork"); - return pid; + return pid; } - -std::vector<char *> stringsToCharPtrs(const Strings & ss) -{ - std::vector<char *> res; - for (auto & s : ss) res.push_back((char *) s.c_str()); - res.push_back(0); - return res; +std::vector<char*> stringsToCharPtrs(const Strings& ss) { + std::vector<char*> res; + for (auto& s : ss) res.push_back((char*)s.c_str()); + res.push_back(0); + return res; } +string runProgram(Path program, bool searchPath, const Strings& args, + const std::optional<std::string>& input) { + RunOptions opts(program, args); + opts.searchPath = searchPath; + opts.input = input; -string runProgram(Path program, bool searchPath, const Strings & args, - const std::optional<std::string> & input) -{ - RunOptions opts(program, args); - opts.searchPath = searchPath; - opts.input = input; - - auto res = runProgram(opts); + auto res = runProgram(opts); - if (!statusOk(res.first)) - throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first))); + if (!statusOk(res.first)) + throw ExecError(res.first, fmt("program '%1%' %2%", program, + statusToString(res.first))); - return res.second; + return res.second; } -std::pair<int, std::string> runProgram(const RunOptions & options_) -{ - RunOptions options(options_); - StringSink sink; - options.standardOut = &sink; +std::pair<int, std::string> runProgram(const RunOptions& options_) { + RunOptions options(options_); + StringSink sink; + options.standardOut = &sink; - int status = 0; + int status = 0; - try { - runProgram2(options); - } catch (ExecError & e) { - status = e.status; - } + try { + runProgram2(options); + } catch (ExecError& e) { + status = e.status; + } - return {status, std::move(*sink.s)}; + return {status, std::move(*sink.s)}; } -void runProgram2(const RunOptions & options) -{ - checkInterrupt(); +void runProgram2(const RunOptions& options) { + checkInterrupt(); - assert(!(options.standardIn && options.input)); + assert(!(options.standardIn && options.input)); - std::unique_ptr<Source> source_; - Source * source = options.standardIn; + std::unique_ptr<Source> source_; + Source* source = options.standardIn; - if (options.input) { - source_ = std::make_unique<StringSource>(*options.input); - source = source_.get(); - } + if (options.input) { + source_ = std::make_unique<StringSource>(*options.input); + source = source_.get(); + } - /* Create a pipe. */ - Pipe out, in; - if (options.standardOut) out.create(); - if (source) in.create(); - - ProcessOptions processOptions; - // vfork implies that the environment of the main process and the fork will - // be shared (technically this is undefined, but in practice that's the - // case), so we can't use it if we alter the environment - if (options.environment) - processOptions.allowVfork = false; - - /* Fork. */ - Pid pid = startProcess([&]() { - if (options.environment) - replaceEnv(*options.environment); - if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1) - throw SysError("dupping stdout"); + /* Create a pipe. */ + Pipe out, in; + if (options.standardOut) out.create(); + if (source) in.create(); + + ProcessOptions processOptions; + // vfork implies that the environment of the main process and the fork will + // be shared (technically this is undefined, but in practice that's the + // case), so we can't use it if we alter the environment + if (options.environment) processOptions.allowVfork = false; + + /* Fork. */ + Pid pid = startProcess( + [&]() { + if (options.environment) replaceEnv(*options.environment); + if (options.standardOut && + dup2(out.writeSide.get(), STDOUT_FILENO) == -1) + throw SysError("dupping stdout"); if (options.mergeStderrToStdout) - if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) - throw SysError("cannot dup stdout into stderr"); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) + throw SysError("cannot dup stdout into stderr"); if (source && dup2(in.readSide.get(), STDIN_FILENO) == -1) - throw SysError("dupping stdin"); + throw SysError("dupping stdin"); if (options.chdir && chdir((*options.chdir).c_str()) == -1) - throw SysError("chdir failed"); + throw SysError("chdir failed"); if (options.gid && setgid(*options.gid) == -1) - throw SysError("setgid failed"); + throw SysError("setgid failed"); /* Drop all other groups if we're setgid. */ if (options.gid && setgroups(0, 0) == -1) - throw SysError("setgroups failed"); + throw SysError("setgroups failed"); if (options.uid && setuid(*options.uid) == -1) - throw SysError("setuid failed"); + throw SysError("setuid failed"); Strings args_(options.args); args_.push_front(options.program); @@ -1073,480 +895,427 @@ void runProgram2(const RunOptions & options) restoreSignals(); if (options.searchPath) - execvp(options.program.c_str(), stringsToCharPtrs(args_).data()); + execvp(options.program.c_str(), stringsToCharPtrs(args_).data()); else - execv(options.program.c_str(), stringsToCharPtrs(args_).data()); + execv(options.program.c_str(), stringsToCharPtrs(args_).data()); throw SysError("executing '%1%'", options.program); - }, processOptions); + }, + processOptions); - out.writeSide = -1; + out.writeSide = -1; - std::thread writerThread; + std::thread writerThread; - std::promise<void> promise; - - Finally doJoin([&]() { - if (writerThread.joinable()) - writerThread.join(); - }); + std::promise<void> promise; + Finally doJoin([&]() { + if (writerThread.joinable()) writerThread.join(); + }); - if (source) { - in.readSide = -1; - writerThread = std::thread([&]() { - try { - std::vector<unsigned char> buf(8 * 1024); - while (true) { - size_t n; - try { - n = source->read(buf.data(), buf.size()); - } catch (EndOfFile &) { - break; - } - writeFull(in.writeSide.get(), buf.data(), n); - } - promise.set_value(); - } catch (...) { - promise.set_exception(std::current_exception()); - } - in.writeSide = -1; - }); - } + if (source) { + in.readSide = -1; + writerThread = std::thread([&]() { + try { + std::vector<unsigned char> buf(8 * 1024); + while (true) { + size_t n; + try { + n = source->read(buf.data(), buf.size()); + } catch (EndOfFile&) { + break; + } + writeFull(in.writeSide.get(), buf.data(), n); + } + promise.set_value(); + } catch (...) { + promise.set_exception(std::current_exception()); + } + in.writeSide = -1; + }); + } - if (options.standardOut) - drainFD(out.readSide.get(), *options.standardOut); + if (options.standardOut) drainFD(out.readSide.get(), *options.standardOut); - /* Wait for the child to finish. */ - int status = pid.wait(); + /* Wait for the child to finish. */ + int status = pid.wait(); - /* Wait for the writer thread to finish. */ - if (source) promise.get_future().get(); + /* Wait for the writer thread to finish. */ + if (source) promise.get_future().get(); - if (status) - throw ExecError(status, fmt("program '%1%' %2%", options.program, statusToString(status))); + if (status) + throw ExecError(status, fmt("program '%1%' %2%", options.program, + statusToString(status))); } - -void closeMostFDs(const set<int> & exceptions) -{ +void closeMostFDs(const set<int>& exceptions) { #if __linux__ - try { - for (auto & s : readDirectory("/proc/self/fd")) { - auto fd = std::stoi(s.name); - if (!exceptions.count(fd)) { - debug("closing leaked FD %d", fd); - close(fd); - } - } - return; - } catch (SysError &) { + try { + for (auto& s : readDirectory("/proc/self/fd")) { + auto fd = std::stoi(s.name); + if (!exceptions.count(fd)) { + debug("closing leaked FD %d", fd); + close(fd); + } } + return; + } catch (SysError&) { + } #endif - int maxFD = 0; - maxFD = sysconf(_SC_OPEN_MAX); - for (int fd = 0; fd < maxFD; ++fd) - if (!exceptions.count(fd)) - close(fd); /* ignore result */ + int maxFD = 0; + maxFD = sysconf(_SC_OPEN_MAX); + for (int fd = 0; fd < maxFD; ++fd) + if (!exceptions.count(fd)) close(fd); /* ignore result */ } - -void closeOnExec(int fd) -{ - int prev; - if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || - fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) - throw SysError("setting close-on-exec flag"); +void closeOnExec(int fd) { + int prev; + if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || + fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) + throw SysError("setting close-on-exec flag"); } - ////////////////////////////////////////////////////////////////////// - bool _isInterrupted = false; static thread_local bool interruptThrown = false; thread_local std::function<bool()> interruptCheck; -void setInterruptThrown() -{ - interruptThrown = true; -} +void setInterruptThrown() { interruptThrown = true; } -void _interrupted() -{ - /* Block user interrupts while an exception is being handled. - Throwing an exception while another exception is being handled - kills the program! */ - if (!interruptThrown && !std::uncaught_exceptions()) { - interruptThrown = true; - throw Interrupted("interrupted by the user"); - } +void _interrupted() { + /* Block user interrupts while an exception is being handled. + Throwing an exception while another exception is being handled + kills the program! */ + if (!interruptThrown && !std::uncaught_exceptions()) { + interruptThrown = true; + throw Interrupted("interrupted by the user"); + } } - ////////////////////////////////////////////////////////////////////// - -template<class C> C tokenizeString(const string & s, const string & separators) -{ - C result; - string::size_type pos = s.find_first_not_of(separators, 0); - while (pos != string::npos) { - string::size_type end = s.find_first_of(separators, pos + 1); - if (end == string::npos) end = s.size(); - string token(s, pos, end - pos); - result.insert(result.end(), token); - pos = s.find_first_not_of(separators, end); - } - return result; -} - -template Strings tokenizeString(const string & s, const string & separators); -template StringSet tokenizeString(const string & s, const string & separators); -template vector<string> tokenizeString(const string & s, const string & separators); - - -string concatStringsSep(const string & sep, const Strings & ss) -{ - string s; - for (auto & i : ss) { - if (s.size() != 0) s += sep; - s += i; - } - return s; -} - - -string concatStringsSep(const string & sep, const StringSet & ss) -{ - string s; - for (auto & i : ss) { - if (s.size() != 0) s += sep; - s += i; - } - return s; -} - - -string chomp(const string & s) -{ - size_t i = s.find_last_not_of(" \n\r\t"); - return i == string::npos ? "" : string(s, 0, i + 1); -} - - -string trim(const string & s, const string & whitespace) -{ - auto i = s.find_first_not_of(whitespace); - if (i == string::npos) return ""; - auto j = s.find_last_not_of(whitespace); - return string(s, i, j == string::npos ? j : j - i + 1); -} - - -string replaceStrings(const std::string & s, - const std::string & from, const std::string & to) -{ - if (from.empty()) return s; - string res = s; - size_t pos = 0; - while ((pos = res.find(from, pos)) != std::string::npos) { - res.replace(pos, from.size(), to); - pos += to.size(); - } - return res; -} - - -string statusToString(int status) -{ - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (WIFEXITED(status)) - return (format("failed with exit code %1%") % WEXITSTATUS(status)).str(); - else if (WIFSIGNALED(status)) { - int sig = WTERMSIG(status); +template <class C> +C tokenizeString(const string& s, const string& separators) { + C result; + string::size_type pos = s.find_first_not_of(separators, 0); + while (pos != string::npos) { + string::size_type end = s.find_first_of(separators, pos + 1); + if (end == string::npos) end = s.size(); + string token(s, pos, end - pos); + result.insert(result.end(), token); + pos = s.find_first_not_of(separators, end); + } + return result; +} + +template Strings tokenizeString(const string& s, const string& separators); +template StringSet tokenizeString(const string& s, const string& separators); +template vector<string> tokenizeString(const string& s, + const string& separators); + +string concatStringsSep(const string& sep, const Strings& ss) { + string s; + for (auto& i : ss) { + if (s.size() != 0) s += sep; + s += i; + } + return s; +} + +string concatStringsSep(const string& sep, const StringSet& ss) { + string s; + for (auto& i : ss) { + if (s.size() != 0) s += sep; + s += i; + } + return s; +} + +string chomp(const string& s) { + size_t i = s.find_last_not_of(" \n\r\t"); + return i == string::npos ? "" : string(s, 0, i + 1); +} + +string trim(const string& s, const string& whitespace) { + auto i = s.find_first_not_of(whitespace); + if (i == string::npos) return ""; + auto j = s.find_last_not_of(whitespace); + return string(s, i, j == string::npos ? j : j - i + 1); +} + +string replaceStrings(const std::string& s, const std::string& from, + const std::string& to) { + if (from.empty()) return s; + string res = s; + size_t pos = 0; + while ((pos = res.find(from, pos)) != std::string::npos) { + res.replace(pos, from.size(), to); + pos += to.size(); + } + return res; +} + +string statusToString(int status) { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (WIFEXITED(status)) + return (format("failed with exit code %1%") % WEXITSTATUS(status)).str(); + else if (WIFSIGNALED(status)) { + int sig = WTERMSIG(status); #if HAVE_STRSIGNAL - const char * description = strsignal(sig); - return (format("failed due to signal %1% (%2%)") % sig % description).str(); + const char* description = strsignal(sig); + return (format("failed due to signal %1% (%2%)") % sig % description) + .str(); #else - return (format("failed due to signal %1%") % sig).str(); + return (format("failed due to signal %1%") % sig).str(); #endif - } - else - return "died abnormally"; - } else return "succeeded"; + } else + return "died abnormally"; + } else + return "succeeded"; } - -bool statusOk(int status) -{ - return WIFEXITED(status) && WEXITSTATUS(status) == 0; +bool statusOk(int status) { + return WIFEXITED(status) && WEXITSTATUS(status) == 0; } - -bool hasPrefix(const string & s, const string & prefix) -{ - return s.compare(0, prefix.size(), prefix) == 0; +bool hasPrefix(const string& s, const string& prefix) { + return s.compare(0, prefix.size(), prefix) == 0; } - -bool hasSuffix(const string & s, const string & suffix) -{ - return s.size() >= suffix.size() && string(s, s.size() - suffix.size()) == suffix; -} - - -std::string toLower(const std::string & s) -{ - std::string r(s); - for (auto & c : r) - c = std::tolower(c); - return r; +bool hasSuffix(const string& s, const string& suffix) { + return s.size() >= suffix.size() && + string(s, s.size() - suffix.size()) == suffix; } - -std::string shellEscape(const std::string & s) -{ - std::string r = "'"; - for (auto & i : s) - if (i == '\'') r += "'\\''"; else r += i; - r += '\''; - return r; +std::string toLower(const std::string& s) { + std::string r(s); + for (auto& c : r) c = std::tolower(c); + return r; } - -void ignoreException() -{ - try { - throw; - } catch (std::exception & e) { - printError(format("error (ignored): %1%") % e.what()); +std::string shellEscape(const std::string& s) { + std::string r = "'"; + for (auto& i : s) + if (i == '\'') + r += "'\\''"; + else + r += i; + r += '\''; + return r; +} + +void ignoreException() { + try { + throw; + } catch (std::exception& e) { + printError(format("error (ignored): %1%") % e.what()); + } +} + +std::string filterANSIEscapes(const std::string& s, bool filterAll, + unsigned int width) { + std::string t, e; + size_t w = 0; + auto i = s.begin(); + + while (w < (size_t)width && i != s.end()) { + if (*i == '\e') { + std::string e; + e += *i++; + char last = 0; + + if (i != s.end() && *i == '[') { + e += *i++; + // eat parameter bytes + while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++; + // eat intermediate bytes + while (i != s.end() && *i >= 0x20 && *i <= 0x2f) e += *i++; + // eat final byte + if (i != s.end() && *i >= 0x40 && *i <= 0x7e) e += last = *i++; + } else { + if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++; + } + + if (!filterAll && last == 'm') t += e; } -} - - -std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned int width) -{ - std::string t, e; - size_t w = 0; - auto i = s.begin(); - - while (w < (size_t) width && i != s.end()) { - - if (*i == '\e') { - std::string e; - e += *i++; - char last = 0; - if (i != s.end() && *i == '[') { - e += *i++; - // eat parameter bytes - while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++; - // eat intermediate bytes - while (i != s.end() && *i >= 0x20 && *i <= 0x2f) e += *i++; - // eat final byte - if (i != s.end() && *i >= 0x40 && *i <= 0x7e) e += last = *i++; - } else { - if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++; - } - - if (!filterAll && last == 'm') - t += e; - } - - else if (*i == '\t') { - i++; t += ' '; w++; - while (w < (size_t) width && w % 8) { - t += ' '; w++; - } - } + else if (*i == '\t') { + i++; + t += ' '; + w++; + while (w < (size_t)width && w % 8) { + t += ' '; + w++; + } + } - else if (*i == '\r') - // do nothing for now - i++; + else if (*i == '\r') + // do nothing for now + i++; - else { - t += *i++; w++; - } + else { + t += *i++; + w++; } + } - return t; + return t; } +static char base64Chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - -string base64Encode(const string & s) -{ - string res; - int data = 0, nbits = 0; +string base64Encode(const string& s) { + string res; + int data = 0, nbits = 0; - for (char c : s) { - data = data << 8 | (unsigned char) c; - nbits += 8; - while (nbits >= 6) { - nbits -= 6; - res.push_back(base64Chars[data >> nbits & 0x3f]); - } + for (char c : s) { + data = data << 8 | (unsigned char)c; + nbits += 8; + while (nbits >= 6) { + nbits -= 6; + res.push_back(base64Chars[data >> nbits & 0x3f]); } + } - if (nbits) res.push_back(base64Chars[data << (6 - nbits) & 0x3f]); - while (res.size() % 4) res.push_back('='); + if (nbits) res.push_back(base64Chars[data << (6 - nbits) & 0x3f]); + while (res.size() % 4) res.push_back('='); - return res; + return res; } +string base64Decode(const string& s) { + bool init = false; + char decode[256]; + if (!init) { + // FIXME: not thread-safe. + memset(decode, -1, sizeof(decode)); + for (int i = 0; i < 64; i++) decode[(int)base64Chars[i]] = i; + init = true; + } -string base64Decode(const string & s) -{ - bool init = false; - char decode[256]; - if (!init) { - // FIXME: not thread-safe. - memset(decode, -1, sizeof(decode)); - for (int i = 0; i < 64; i++) - decode[(int) base64Chars[i]] = i; - init = true; - } - - string res; - unsigned int d = 0, bits = 0; + string res; + unsigned int d = 0, bits = 0; - for (char c : s) { - if (c == '=') break; - if (c == '\n') continue; + for (char c : s) { + if (c == '=') break; + if (c == '\n') continue; - char digit = decode[(unsigned char) c]; - if (digit == -1) - throw Error("invalid character in Base64 string"); + char digit = decode[(unsigned char)c]; + if (digit == -1) throw Error("invalid character in Base64 string"); - bits += 6; - d = d << 6 | digit; - if (bits >= 8) { - res.push_back(d >> (bits - 8) & 0xff); - bits -= 8; - } + bits += 6; + d = d << 6 | digit; + if (bits >= 8) { + res.push_back(d >> (bits - 8) & 0xff); + bits -= 8; } + } - return res; + return res; } - -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(); - } +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(); + } } - static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}}; - -static void updateWindowSize() -{ - struct winsize ws; - if (ioctl(2, TIOCGWINSZ, &ws) == 0) { - auto windowSize_(windowSize.lock()); - windowSize_->first = ws.ws_row; - windowSize_->second = ws.ws_col; - } +static void updateWindowSize() { + struct winsize ws; + if (ioctl(2, TIOCGWINSZ, &ws) == 0) { + auto windowSize_(windowSize.lock()); + windowSize_->first = ws.ws_row; + windowSize_->second = ws.ws_col; + } } - -std::pair<unsigned short, unsigned short> getWindowSize() -{ - return *windowSize.lock(); +std::pair<unsigned short, unsigned short> getWindowSize() { + return *windowSize.lock(); } - static Sync<std::list<std::function<void()>>> _interruptCallbacks; -static void signalHandlerThread(sigset_t set) -{ - while (true) { - int signal = 0; - sigwait(&set, &signal); +static void signalHandlerThread(sigset_t set) { + while (true) { + int signal = 0; + sigwait(&set, &signal); - if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP) - triggerInterrupt(); + if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP) + triggerInterrupt(); - else if (signal == SIGWINCH) { - updateWindowSize(); - } + else if (signal == SIGWINCH) { + updateWindowSize(); } + } } -void triggerInterrupt() -{ - _isInterrupted = true; +void triggerInterrupt() { + _isInterrupted = true; - { - auto interruptCallbacks(_interruptCallbacks.lock()); - for (auto & callback : *interruptCallbacks) { - try { - callback(); - } catch (...) { - ignoreException(); - } - } + { + auto interruptCallbacks(_interruptCallbacks.lock()); + for (auto& callback : *interruptCallbacks) { + try { + callback(); + } catch (...) { + ignoreException(); + } } + } } static sigset_t savedSignalMask; -void startSignalHandlerThread() -{ - updateWindowSize(); +void startSignalHandlerThread() { + updateWindowSize(); - if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) - throw SysError("quering signal mask"); + if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) + throw SysError("quering signal mask"); - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGINT); - sigaddset(&set, SIGTERM); - sigaddset(&set, SIGHUP); - sigaddset(&set, SIGPIPE); - sigaddset(&set, SIGWINCH); - if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) - throw SysError("blocking signals"); + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGPIPE); + sigaddset(&set, SIGWINCH); + if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) + throw SysError("blocking signals"); - std::thread(signalHandlerThread, set).detach(); + std::thread(signalHandlerThread, set).detach(); } -void restoreSignals() -{ - if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) - throw SysError("restoring signals"); +void restoreSignals() { + if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) + throw SysError("restoring signals"); } /* RAII helper to automatically deregister a callback. */ -struct InterruptCallbackImpl : InterruptCallback -{ - std::list<std::function<void()>>::iterator it; - ~InterruptCallbackImpl() override - { - _interruptCallbacks.lock()->erase(it); - } +struct InterruptCallbackImpl : InterruptCallback { + std::list<std::function<void()>>::iterator it; + ~InterruptCallbackImpl() override { _interruptCallbacks.lock()->erase(it); } }; -std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback) -{ - auto interruptCallbacks(_interruptCallbacks.lock()); - interruptCallbacks->push_back(callback); +std::unique_ptr<InterruptCallback> createInterruptCallback( + std::function<void()> callback) { + auto interruptCallbacks(_interruptCallbacks.lock()); + interruptCallbacks->push_back(callback); - auto res = std::make_unique<InterruptCallbackImpl>(); - res->it = interruptCallbacks->end(); - res->it--; + auto res = std::make_unique<InterruptCallbackImpl>(); + res->it = interruptCallbacks->end(); + res->it--; - return std::unique_ptr<InterruptCallback>(res.release()); + return std::unique_ptr<InterruptCallback>(res.release()); } -} +} // namespace nix diff --git a/third_party/nix/src/libutil/util.hh b/third_party/nix/src/libutil/util.hh index f057fdb2c041..8a23090def97 100644 --- a/third_party/nix/src/libutil/util.hh +++ b/third_party/nix/src/libutil/util.hh @@ -1,21 +1,19 @@ #pragma once -#include "types.hh" -#include "logging.hh" - -#include <sys/types.h> -#include <sys/stat.h> #include <dirent.h> -#include <unistd.h> #include <signal.h> - +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <cstdio> #include <functional> +#include <future> #include <limits> -#include <cstdio> #include <map> -#include <sstream> #include <optional> -#include <future> +#include <sstream> +#include "logging.hh" +#include "types.hh" #ifndef HAVE_STRUCT_DIRENT_D_TYPE #define DT_UNKNOWN 0 @@ -29,13 +27,11 @@ namespace nix { struct Sink; struct Source; - /* The system for which Nix is compiled. */ extern const std::string nativeSystem; - /* Return an environment variable. */ -string getEnv(const string & key, const string & def = ""); +string getEnv(const string& key, const string& def = ""); /* Get the entire environment. */ std::map<std::string, std::string> getEnv(); @@ -52,62 +48,61 @@ Path absPath(Path path, Path dir = ""); double or trailing slashes. Optionally resolves all symlink components such that each component of the resulting path is *not* a symbolic link. */ -Path canonPath(const Path & path, bool resolveSymlinks = false); +Path canonPath(const Path& path, bool resolveSymlinks = false); /* Return the directory part of the given canonical path, i.e., everything before the final `/'. If the path is the root or an immediate child thereof (e.g., `/foo'), this means an empty string is returned. */ -Path dirOf(const Path & path); +Path dirOf(const Path& path); /* Return the base name of the given canonical path, i.e., everything following the final `/'. */ -string baseNameOf(const Path & path); +string baseNameOf(const Path& path); /* Check whether 'path' is a descendant of 'dir'. */ -bool isInDir(const Path & path, const Path & dir); +bool isInDir(const Path& path, const Path& dir); /* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */ -bool isDirOrInDir(const Path & path, const Path & dir); +bool isDirOrInDir(const Path& path, const Path& dir); /* Get status of `path'. */ -struct stat lstat(const Path & path); +struct stat lstat(const Path& path); /* Return true iff the given path exists. */ -bool pathExists(const Path & path); +bool pathExists(const Path& path); /* Read the contents (target) of a symbolic link. The result is not in any way canonicalised. */ -Path readLink(const Path & path); +Path readLink(const Path& path); -bool isLink(const Path & path); +bool isLink(const Path& path); /* Read the contents of a directory. The entries `.' and `..' are removed. */ -struct DirEntry -{ - string name; - ino_t ino; - unsigned char type; // one of DT_* - DirEntry(const string & name, ino_t ino, unsigned char type) - : name(name), ino(ino), type(type) { } +struct DirEntry { + string name; + ino_t ino; + unsigned char type; // one of DT_* + DirEntry(const string& name, ino_t ino, unsigned char type) + : name(name), ino(ino), type(type) {} }; typedef vector<DirEntry> DirEntries; -DirEntries readDirectory(const Path & path); +DirEntries readDirectory(const Path& path); -unsigned char getFileType(const Path & path); +unsigned char getFileType(const Path& path); /* Read the contents of a file into a string. */ string readFile(int fd); -string readFile(const Path & path, bool drain = false); -void readFile(const Path & path, Sink & sink); +string readFile(const Path& path, bool drain = false); +void readFile(const Path& path, Sink& sink); /* Write a string to a file. */ -void writeFile(const Path & path, const string & s, mode_t mode = 0666); +void writeFile(const Path& path, const string& s, mode_t mode = 0666); -void writeFile(const Path & path, Source & source, mode_t mode = 0666); +void writeFile(const Path& path, Source& source, mode_t mode = 0666); /* Read a line from a file descriptor. */ string readLine(int fd); @@ -118,13 +113,14 @@ void writeLine(int fd, string s); /* Delete a path; i.e., in the case of a directory, it is deleted recursively. It's not an error if the path does not exist. The second variant returns the number of bytes and blocks freed. */ -void deletePath(const Path & path); +void deletePath(const Path& path); -void deletePath(const Path & path, unsigned long long & bytesFreed); +void deletePath(const Path& path, unsigned long long& bytesFreed); /* Create a temporary directory. */ -Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", - bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755); +Path createTempDir(const Path& tmpRoot = "", const Path& prefix = "nix", + bool includePid = true, bool useGlobalCounter = true, + mode_t mode = 0755); std::string getUserName(); @@ -145,178 +141,161 @@ Path getDataDir(); /* Create a directory and all its parents, if necessary. Returns the list of created directories, in order of creation. */ -Paths createDirs(const Path & path); +Paths createDirs(const Path& path); /* Create a symlink. */ -void createSymlink(const Path & target, const Path & link); +void createSymlink(const Path& target, const Path& link); /* Atomically create or replace a symlink. */ -void replaceSymlink(const Path & target, const Path & link); - +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, bool allowInterrupts = true); -void writeFull(int fd, const string & s, bool allowInterrupts = true); +void readFull(int fd, unsigned char* buf, size_t count); +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) + /* Read a file descriptor until EOF occurs. */ + string drainFD(int fd, bool block = true); -/* Read a file descriptor until EOF occurs. */ -string drainFD(int fd, bool block = true); - -void drainFD(int fd, Sink & sink, bool block = true); - +void drainFD(int fd, Sink& sink, bool block = true); /* Automatic cleanup of resources. */ - -class AutoDelete -{ - Path path; - bool del; - bool recursive; -public: - AutoDelete(); - AutoDelete(const Path & p, bool recursive = true); - ~AutoDelete(); - void cancel(); - void reset(const Path & p, bool recursive = true); - operator Path() const { return path; } +class AutoDelete { + Path path; + bool del; + bool recursive; + + public: + AutoDelete(); + AutoDelete(const Path& p, bool recursive = true); + ~AutoDelete(); + void cancel(); + void reset(const Path& p, bool recursive = true); + operator Path() const { return path; } }; - -class AutoCloseFD -{ - int fd; - void close(); -public: - AutoCloseFD(); - AutoCloseFD(int fd); - AutoCloseFD(const AutoCloseFD & fd) = delete; - AutoCloseFD(AutoCloseFD&& fd); - ~AutoCloseFD(); - AutoCloseFD& operator =(const AutoCloseFD & fd) = delete; - AutoCloseFD& operator =(AutoCloseFD&& fd); - int get() const; - explicit operator bool() const; - int release(); +class AutoCloseFD { + int fd; + void close(); + + public: + AutoCloseFD(); + AutoCloseFD(int fd); + AutoCloseFD(const AutoCloseFD& fd) = delete; + AutoCloseFD(AutoCloseFD&& fd); + ~AutoCloseFD(); + AutoCloseFD& operator=(const AutoCloseFD& fd) = delete; + AutoCloseFD& operator=(AutoCloseFD&& fd); + int get() const; + explicit operator bool() const; + int release(); }; - -class Pipe -{ -public: - AutoCloseFD readSide, writeSide; - void create(); +class Pipe { + public: + AutoCloseFD readSide, writeSide; + void create(); }; - -struct DIRDeleter -{ - void operator()(DIR * dir) const { - closedir(dir); - } +struct DIRDeleter { + void operator()(DIR* dir) const { closedir(dir); } }; typedef std::unique_ptr<DIR, DIRDeleter> AutoCloseDir; - -class Pid -{ - pid_t pid = -1; - bool separatePG = false; - int killSignal = SIGKILL; -public: - Pid(); - Pid(pid_t pid); - ~Pid(); - void operator =(pid_t pid); - operator pid_t(); - int kill(); - int wait(); - - void setSeparatePG(bool separatePG); - void setKillSignal(int signal); - pid_t release(); +class Pid { + pid_t pid = -1; + bool separatePG = false; + int killSignal = SIGKILL; + + public: + Pid(); + Pid(pid_t pid); + ~Pid(); + void operator=(pid_t pid); + operator pid_t(); + int kill(); + int wait(); + + void setSeparatePG(bool separatePG); + void setKillSignal(int signal); + pid_t release(); }; - /* Kill all processes running under the specified uid by sending them a SIGKILL. */ void killUser(uid_t uid); - /* Fork a process that runs the given function, and return the child pid to the caller. */ -struct ProcessOptions -{ - string errorPrefix = "error: "; - bool dieWithParent = true; - bool runExitHandlers = false; - bool allowVfork = true; +struct ProcessOptions { + string errorPrefix = "error: "; + bool dieWithParent = true; + bool runExitHandlers = false; + bool allowVfork = true; }; -pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions()); - +pid_t startProcess(std::function<void()> fun, + const ProcessOptions& options = ProcessOptions()); /* Run a program and return its stdout in a string (i.e., like the shell backtick operator). */ string runProgram(Path program, bool searchPath = false, - const Strings & args = Strings(), - const std::optional<std::string> & input = {}); - -struct RunOptions -{ - std::optional<uid_t> uid; - std::optional<uid_t> gid; - std::optional<Path> chdir; - std::optional<std::map<std::string, std::string>> environment; - Path program; - bool searchPath = true; - Strings args; - std::optional<std::string> input; - Source * standardIn = nullptr; - Sink * standardOut = nullptr; - bool mergeStderrToStdout = false; - bool _killStderr = false; - - RunOptions(const Path & program, const Strings & args) - : program(program), args(args) { }; - - RunOptions & killStderr(bool v) { _killStderr = true; return *this; } + const Strings& args = Strings(), + const std::optional<std::string>& input = {}); + +struct RunOptions { + std::optional<uid_t> uid; + std::optional<uid_t> gid; + std::optional<Path> chdir; + std::optional<std::map<std::string, std::string>> environment; + Path program; + bool searchPath = true; + Strings args; + std::optional<std::string> input; + Source* standardIn = nullptr; + Sink* standardOut = nullptr; + bool mergeStderrToStdout = false; + bool _killStderr = false; + + RunOptions(const Path& program, const Strings& args) + : program(program), args(args){}; + + RunOptions& killStderr(bool v) { + _killStderr = true; + return *this; + } }; -std::pair<int, std::string> runProgram(const RunOptions & options); +std::pair<int, std::string> runProgram(const RunOptions& options); -void runProgram2(const RunOptions & options); +void runProgram2(const RunOptions& options); +class ExecError : public Error { + public: + int status; -class ExecError : public Error -{ -public: - int status; - - template<typename... Args> - ExecError(int status, Args... args) - : Error(args...), status(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 list of strings. */ -std::vector<char *> stringsToCharPtrs(const Strings & ss); +std::vector<char*> stringsToCharPtrs(const Strings& ss); /* Close all file descriptors except those listed in the given set. Good practice in child processes. */ -void closeMostFDs(const set<int> & exceptions); +void closeMostFDs(const set<int>& exceptions); /* Set the close-on-exec flag for the given file descriptor. */ void closeOnExec(int fd); - /* User interruption. */ extern bool _isInterrupted; @@ -327,40 +306,32 @@ void setInterruptThrown(); void _interrupted(); -void inline checkInterrupt() -{ - if (_isInterrupted || (interruptCheck && interruptCheck())) - _interrupted(); +void inline checkInterrupt() { + if (_isInterrupted || (interruptCheck && interruptCheck())) _interrupted(); } MakeError(Interrupted, BaseError) + MakeError(FormatError, Error) -MakeError(FormatError, Error) - - -/* String tokenizer. */ -template<class C> C tokenizeString(const string & s, const string & separators = " \t\n\r"); - + /* String tokenizer. */ + template <class C> + C tokenizeString(const string& s, const string& separators = " \t\n\r"); /* Concatenate the given strings with a separator between the elements. */ -string concatStringsSep(const string & sep, const Strings & ss); -string concatStringsSep(const string & sep, const StringSet & ss); - +string concatStringsSep(const string& sep, const Strings& ss); +string concatStringsSep(const string& sep, const StringSet& ss); /* Remove trailing whitespace from a string. */ -string chomp(const string & s); - +string chomp(const string& s); /* Remove whitespace from the start and end of a string. */ -string trim(const string & s, const string & whitespace = " \n\r\t"); - +string trim(const string& s, const string& whitespace = " \n\r\t"); /* Replace all occurrences of a string inside another string. */ -string replaceStrings(const std::string & s, - const std::string & from, const std::string & to); - +string replaceStrings(const std::string& s, const std::string& from, + const std::string& to); /* Convert the exit status of a child as returned by wait() into an error string. */ @@ -368,47 +339,40 @@ string statusToString(int status); bool statusOk(int status); - /* Parse a string into an integer. */ -template<class N> bool string2Int(const string & s, N & n) -{ - if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed) - return false; - std::istringstream str(s); - str >> n; - return str && str.get() == EOF; +template <class N> +bool string2Int(const string& s, N& n) { + if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed) + return false; + std::istringstream str(s); + str >> n; + return str && str.get() == EOF; } /* Parse a string into a float. */ -template<class N> bool string2Float(const string & s, N & n) -{ - std::istringstream str(s); - str >> n; - return str && str.get() == EOF; +template <class N> +bool string2Float(const string& s, N& n) { + std::istringstream str(s); + str >> n; + return str && str.get() == EOF; } - /* Return true iff `s' starts with `prefix'. */ -bool hasPrefix(const string & s, const string & prefix); - +bool hasPrefix(const string& s, const string& prefix); /* Return true iff `s' ends in `suffix'. */ -bool hasSuffix(const string & s, const string & suffix); - +bool hasSuffix(const string& s, const string& suffix); /* Convert a string to lower case. */ -std::string toLower(const std::string & s); - +std::string toLower(const std::string& s); /* Escape a string as a shell word. */ -std::string shellEscape(const std::string & s); - +std::string shellEscape(const std::string& s); /* Exception handling in destructors: print an error message, then ignore the exception. */ void ignoreException(); - /* Some ANSI escape sequences. */ #define ANSI_NORMAL "\e[0m" #define ANSI_BOLD "\e[1m" @@ -417,71 +381,61 @@ void ignoreException(); #define ANSI_GREEN "\e[32;1m" #define ANSI_BLUE "\e[34;1m" - /* Truncate a string to 'width' printable characters. If 'filterAll' is true, all ANSI escape sequences are filtered out. Otherwise, some escape sequences (such as colour setting) are copied but not included in the character count. Also, tabs are expanded to spaces. */ -std::string filterANSIEscapes(const std::string & s, - bool filterAll = false, +std::string filterANSIEscapes( + const std::string& s, bool filterAll = false, unsigned int width = std::numeric_limits<unsigned int>::max()); - /* Base64 encoding/decoding. */ -string base64Encode(const string & s); -string base64Decode(const string & s); - +string base64Encode(const string& s); +string base64Decode(const string& s); /* Get a value for the specified key from an associate container, or a default value if the key doesn't exist. */ template <class T> -string get(const T & map, const string & key, const string & def = "") -{ - auto i = map.find(key); - return i == map.end() ? def : i->second; +string get(const T& map, const string& key, const string& def = "") { + auto i = map.find(key); + return i == map.end() ? def : i->second; } - /* A callback is a wrapper around a lambda that accepts a valid of type T or an exception. (We abuse std::future<T> to pass the value or exception.) */ -template<typename T> -class Callback -{ - std::function<void(std::future<T>)> fun; - std::atomic_flag done = ATOMIC_FLAG_INIT; - -public: - - Callback(std::function<void(std::future<T>)> fun) : fun(fun) { } - - Callback(Callback && callback) : fun(std::move(callback.fun)) - { - auto prev = callback.done.test_and_set(); - if (prev) done.test_and_set(); - } - - void operator()(T && t) noexcept - { - auto prev = done.test_and_set(); - assert(!prev); - std::promise<T> promise; - promise.set_value(std::move(t)); - fun(promise.get_future()); - } - - void rethrow(const std::exception_ptr & exc = std::current_exception()) noexcept - { - auto prev = done.test_and_set(); - assert(!prev); - std::promise<T> promise; - promise.set_exception(exc); - fun(promise.get_future()); - } +template <typename T> +class Callback { + std::function<void(std::future<T>)> fun; + std::atomic_flag done = ATOMIC_FLAG_INIT; + + public: + Callback(std::function<void(std::future<T>)> fun) : fun(fun) {} + + Callback(Callback&& callback) : fun(std::move(callback.fun)) { + auto prev = callback.done.test_and_set(); + if (prev) done.test_and_set(); + } + + void operator()(T&& t) noexcept { + auto prev = done.test_and_set(); + assert(!prev); + std::promise<T> promise; + promise.set_value(std::move(t)); + fun(promise.get_future()); + } + + void rethrow( + const std::exception_ptr& exc = std::current_exception()) noexcept { + auto prev = done.test_and_set(); + assert(!prev); + std::promise<T> promise; + promise.set_exception(exc); + fun(promise.get_future()); + } }; - /* Start a thread that handles various signals. Also block those signals on the current thread (and thus any threads created by it). */ void startSignalHandlerThread(); @@ -489,9 +443,8 @@ void startSignalHandlerThread(); /* Restore default signal handling. */ void restoreSignals(); -struct InterruptCallback -{ - virtual ~InterruptCallback() { }; +struct InterruptCallback { + virtual ~InterruptCallback(){}; }; /* Register a function that gets called on SIGINT (in a non-signal @@ -504,39 +457,34 @@ void triggerInterrupt(); /* A RAII class that causes the current thread to receive SIGUSR1 when the signal handler thread receives SIGINT. That is, this allows SIGINT to be multiplexed to multiple threads. */ -struct ReceiveInterrupts -{ - pthread_t target; - std::unique_ptr<InterruptCallback> callback; - - ReceiveInterrupts() - : target(pthread_self()) - , callback(createInterruptCallback([&]() { pthread_kill(target, SIGUSR1); })) - { } +struct ReceiveInterrupts { + pthread_t target; + std::unique_ptr<InterruptCallback> callback; + + ReceiveInterrupts() + : target(pthread_self()), callback(createInterruptCallback([&]() { + pthread_kill(target, SIGUSR1); + })) {} }; - - /* A RAII helper that increments a counter on construction and decrements it on destruction. */ -template<typename T> -struct MaintainCount -{ - T & counter; - long delta; - MaintainCount(T & counter, long delta = 1) : counter(counter), delta(delta) { counter += delta; } - ~MaintainCount() { counter -= delta; } +template <typename T> +struct MaintainCount { + T& counter; + long delta; + MaintainCount(T& counter, long delta = 1) : counter(counter), delta(delta) { + counter += delta; + } + ~MaintainCount() { counter -= delta; } }; - /* Return the number of rows and columns of the terminal. */ std::pair<unsigned short, unsigned short> getWindowSize(); - /* Used in various places. */ -typedef std::function<bool(const Path & path)> PathFilter; +typedef std::function<bool(const Path& path)> PathFilter; extern PathFilter defaultPathFilter; - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/xml-writer.cc b/third_party/nix/src/libutil/xml-writer.cc index e5cc2e9fc719..cabf7812128e 100644 --- a/third_party/nix/src/libutil/xml-writer.cc +++ b/third_party/nix/src/libutil/xml-writer.cc @@ -1,94 +1,77 @@ -#include <assert.h> - #include "xml-writer.hh" - +#include <assert.h> namespace nix { - - -XMLWriter::XMLWriter(bool indent, std::ostream & output) - : output(output), indent(indent) -{ - output << "<?xml version='1.0' encoding='utf-8'?>" << std::endl; - closed = false; -} - -XMLWriter::~XMLWriter() -{ - close(); +XMLWriter::XMLWriter(bool indent, std::ostream& output) + : output(output), indent(indent) { + output << "<?xml version='1.0' encoding='utf-8'?>" << std::endl; + closed = false; } +XMLWriter::~XMLWriter() { close(); } -void XMLWriter::close() -{ - if (closed) return; - while (!pendingElems.empty()) closeElement(); - closed = true; +void XMLWriter::close() { + if (closed) return; + while (!pendingElems.empty()) closeElement(); + closed = true; } - -void XMLWriter::indent_(size_t depth) -{ - if (!indent) return; - output << string(depth * 2, ' '); +void XMLWriter::indent_(size_t depth) { + if (!indent) return; + output << string(depth * 2, ' '); } - -void XMLWriter::openElement(const string & name, - const XMLAttrs & attrs) -{ - assert(!closed); - indent_(pendingElems.size()); - output << "<" << name; - writeAttrs(attrs); - output << ">"; - if (indent) output << std::endl; - pendingElems.push_back(name); +void XMLWriter::openElement(const string& name, const XMLAttrs& attrs) { + assert(!closed); + indent_(pendingElems.size()); + output << "<" << name; + writeAttrs(attrs); + output << ">"; + if (indent) output << std::endl; + pendingElems.push_back(name); } - -void XMLWriter::closeElement() -{ - assert(!pendingElems.empty()); - indent_(pendingElems.size() - 1); - output << "</" << pendingElems.back() << ">"; - if (indent) output << std::endl; - pendingElems.pop_back(); - if (pendingElems.empty()) closed = true; +void XMLWriter::closeElement() { + assert(!pendingElems.empty()); + indent_(pendingElems.size() - 1); + output << "</" << pendingElems.back() << ">"; + if (indent) output << std::endl; + pendingElems.pop_back(); + if (pendingElems.empty()) closed = true; } - -void XMLWriter::writeEmptyElement(const string & name, - const XMLAttrs & attrs) -{ - assert(!closed); - indent_(pendingElems.size()); - output << "<" << name; - writeAttrs(attrs); - output << " />"; - if (indent) output << std::endl; +void XMLWriter::writeEmptyElement(const string& name, const XMLAttrs& attrs) { + assert(!closed); + indent_(pendingElems.size()); + output << "<" << name; + writeAttrs(attrs); + output << " />"; + if (indent) output << std::endl; } - -void XMLWriter::writeAttrs(const XMLAttrs & attrs) -{ - for (auto & i : attrs) { - output << " " << i.first << "=\""; - for (size_t j = 0; j < i.second.size(); ++j) { - char c = i.second[j]; - if (c == '"') output << """; - else if (c == '<') output << "<"; - else if (c == '>') output << ">"; - else if (c == '&') output << "&"; - /* Escape newlines to prevent attribute normalisation (see - XML spec, section 3.3.3. */ - else if (c == '\n') output << "
"; - else output << c; - } - output << "\""; +void XMLWriter::writeAttrs(const XMLAttrs& attrs) { + for (auto& i : attrs) { + output << " " << i.first << "=\""; + for (size_t j = 0; j < i.second.size(); ++j) { + char c = i.second[j]; + if (c == '"') + output << """; + else if (c == '<') + output << "<"; + else if (c == '>') + output << ">"; + else if (c == '&') + output << "&"; + /* Escape newlines to prevent attribute normalisation (see + XML spec, section 3.3.3. */ + else if (c == '\n') + output << "
"; + else + output << c; } + output << "\""; + } } - -} +} // namespace nix diff --git a/third_party/nix/src/libutil/xml-writer.hh b/third_party/nix/src/libutil/xml-writer.hh index b98b445265a2..3a2d9a66d8e1 100644 --- a/third_party/nix/src/libutil/xml-writer.hh +++ b/third_party/nix/src/libutil/xml-writer.hh @@ -1,69 +1,56 @@ #pragma once #include <iostream> -#include <string> #include <list> #include <map> - +#include <string> namespace nix { -using std::string; -using std::map; using std::list; - +using std::map; +using std::string; typedef map<string, string> XMLAttrs; +class XMLWriter { + private: + std::ostream& output; -class XMLWriter -{ -private: - - std::ostream & output; - - bool indent; - bool closed; + bool indent; + bool closed; - list<string> pendingElems; + list<string> pendingElems; -public: + public: + XMLWriter(bool indent, std::ostream& output); + ~XMLWriter(); - XMLWriter(bool indent, std::ostream & output); - ~XMLWriter(); + void close(); - void close(); + void openElement(const string& name, const XMLAttrs& attrs = XMLAttrs()); + void closeElement(); - void openElement(const string & name, - const XMLAttrs & attrs = XMLAttrs()); - void closeElement(); + void writeEmptyElement(const string& name, + const XMLAttrs& attrs = XMLAttrs()); - void writeEmptyElement(const string & name, - const XMLAttrs & attrs = XMLAttrs()); + private: + void writeAttrs(const XMLAttrs& attrs); -private: - void writeAttrs(const XMLAttrs & attrs); - - void indent_(size_t depth); + void indent_(size_t depth); }; - -class XMLOpenElement -{ -private: - XMLWriter & writer; -public: - XMLOpenElement(XMLWriter & writer, const string & name, - const XMLAttrs & attrs = XMLAttrs()) - : writer(writer) - { - writer.openElement(name, attrs); - } - ~XMLOpenElement() - { - writer.closeElement(); - } +class XMLOpenElement { + private: + XMLWriter& writer; + + public: + XMLOpenElement(XMLWriter& writer, const string& name, + const XMLAttrs& attrs = XMLAttrs()) + : writer(writer) { + writer.openElement(name, attrs); + } + ~XMLOpenElement() { writer.closeElement(); } }; - -} +} // namespace nix |