mirror of
https://github.com/NixOS/nix.git
synced 2025-11-11 21:16:02 +01:00
Merge remote-tracking branch 'upstream/master' into auto-uid-allocation
This commit is contained in:
commit
dc92b01885
417 changed files with 22620 additions and 35795 deletions
|
|
@ -10,6 +10,7 @@ std::map<std::string, nlohmann::json> BaseSetting<T>::toJSONObject()
|
|||
auto obj = AbstractSetting::toJSONObject();
|
||||
obj.emplace("value", value);
|
||||
obj.emplace("defaultValue", defaultValue);
|
||||
obj.emplace("documentDefault", documentDefault);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
#include "types.hh"
|
||||
#include "util.hh"
|
||||
#include "affinity.hh"
|
||||
|
||||
#if __linux__
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
#if __linux__
|
||||
static bool didSaveAffinity = false;
|
||||
static cpu_set_t savedAffinity;
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const cpu_set_t &cset)
|
||||
{
|
||||
auto count = CPU_COUNT(&cset);
|
||||
for (int i=0; i < count; ++i)
|
||||
{
|
||||
os << (CPU_ISSET(i,&cset) ? "1" : "0");
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
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("failed to lock thread to CPU %1%", cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int lockToCurrentCPU()
|
||||
{
|
||||
#if __linux__
|
||||
int cpu = sched_getcpu();
|
||||
if (cpu != -1) setAffinityTo(cpu);
|
||||
return cpu;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void restoreAffinity()
|
||||
{
|
||||
#if __linux__
|
||||
if (!didSaveAffinity) return;
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << savedAffinity;
|
||||
printError("failed to restore CPU affinity %1%", oss.str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace nix {
|
||||
|
||||
void setAffinityTo(int cpu);
|
||||
int lockToCurrentCPU();
|
||||
void restoreAffinity();
|
||||
|
||||
}
|
||||
|
|
@ -37,12 +37,12 @@ static GlobalConfig::Register rArchiveSettings(&archiveSettings);
|
|||
|
||||
const std::string narVersionMagic1 = "nix-archive-1";
|
||||
|
||||
static string caseHackSuffix = "~nix~case~hack~";
|
||||
static std::string caseHackSuffix = "~nix~case~hack~";
|
||||
|
||||
PathFilter defaultPathFilter = [](const Path &) { return true; };
|
||||
|
||||
|
||||
static void dumpContents(const Path & path, size_t size,
|
||||
static void dumpContents(const Path & path, off_t size,
|
||||
Sink & sink)
|
||||
{
|
||||
sink << "contents" << size;
|
||||
|
|
@ -76,7 +76,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
|||
sink << "type" << "regular";
|
||||
if (st.st_mode & S_IXUSR)
|
||||
sink << "executable" << "";
|
||||
dumpContents(path, (size_t) st.st_size, sink);
|
||||
dumpContents(path, st.st_size, sink);
|
||||
}
|
||||
|
||||
else if (S_ISDIR(st.st_mode)) {
|
||||
|
|
@ -84,22 +84,21 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
|||
|
||||
/* If we're on a case-insensitive system like macOS, undo
|
||||
the case hack applied by restorePath(). */
|
||||
std::map<string, string> unhacked;
|
||||
std::map<std::string, std::string> unhacked;
|
||||
for (auto & i : readDirectory(path))
|
||||
if (archiveSettings.useCaseHack) {
|
||||
string name(i.name);
|
||||
std::string name(i.name);
|
||||
size_t pos = i.name.find(caseHackSuffix);
|
||||
if (pos != string::npos) {
|
||||
if (pos != std::string::npos) {
|
||||
debug(format("removing case hack suffix from '%1%'") % (path + "/" + i.name));
|
||||
name.erase(pos);
|
||||
}
|
||||
if (unhacked.find(name) != unhacked.end())
|
||||
if (!unhacked.emplace(name, i.name).second)
|
||||
throw Error("file name collision in between '%1%' and '%2%'",
|
||||
(path + "/" + unhacked[name]),
|
||||
(path + "/" + i.name));
|
||||
unhacked[name] = i.name;
|
||||
} else
|
||||
unhacked[i.name] = i.name;
|
||||
unhacked.emplace(i.name, i.name);
|
||||
|
||||
for (auto & i : unhacked)
|
||||
if (filter(path + "/" + i.first)) {
|
||||
|
|
@ -125,13 +124,13 @@ void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
|
|||
}
|
||||
|
||||
|
||||
void dumpString(const std::string & s, Sink & sink)
|
||||
void dumpString(std::string_view s, Sink & sink)
|
||||
{
|
||||
sink << narVersionMagic1 << "(" << "type" << "regular" << "contents" << s << ")";
|
||||
}
|
||||
|
||||
|
||||
static SerialisationError badArchive(string s)
|
||||
static SerialisationError badArchive(const std::string & s)
|
||||
{
|
||||
return SerialisationError("bad archive: " + s);
|
||||
}
|
||||
|
|
@ -172,7 +171,7 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path)
|
|||
|
||||
struct CaseInsensitiveCompare
|
||||
{
|
||||
bool operator() (const string & a, const string & b) const
|
||||
bool operator() (const std::string & a, const std::string & b) const
|
||||
{
|
||||
return strcasecmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
|
|
@ -181,7 +180,7 @@ struct CaseInsensitiveCompare
|
|||
|
||||
static void parse(ParseSink & sink, Source & source, const Path & path)
|
||||
{
|
||||
string s;
|
||||
std::string s;
|
||||
|
||||
s = readString(source);
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
|
@ -202,7 +201,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
|
|||
else if (s == "type") {
|
||||
if (type != tpUnknown)
|
||||
throw badArchive("multiple type fields");
|
||||
string t = readString(source);
|
||||
std::string t = readString(source);
|
||||
|
||||
if (t == "regular") {
|
||||
type = tpRegular;
|
||||
|
|
@ -233,7 +232,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
|
|||
}
|
||||
|
||||
else if (s == "entry" && type == tpDirectory) {
|
||||
string name, prevName;
|
||||
std::string name, prevName;
|
||||
|
||||
s = readString(source);
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
|
@ -247,7 +246,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
|
|||
break;
|
||||
} else if (s == "name") {
|
||||
name = readString(source);
|
||||
if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
|
||||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos)
|
||||
throw Error("NAR contains invalid file name '%1%'", name);
|
||||
if (name <= prevName)
|
||||
throw Error("NAR directory is not sorted");
|
||||
|
|
@ -270,7 +269,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
|
|||
}
|
||||
|
||||
else if (s == "target" && type == tpSymlink) {
|
||||
string target = readString(source);
|
||||
std::string target = readString(source);
|
||||
sink.createSymlink(path, target);
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +281,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
|
|||
|
||||
void parseDump(ParseSink & sink, Source & source)
|
||||
{
|
||||
string version;
|
||||
std::string version;
|
||||
try {
|
||||
version = readString(source, narVersionMagic1.size());
|
||||
} catch (SerialisationError & e) {
|
||||
|
|
@ -346,7 +345,7 @@ struct RestoreSink : ParseSink
|
|||
writeFull(fd.get(), data);
|
||||
}
|
||||
|
||||
void createSymlink(const Path & path, const string & target) override
|
||||
void createSymlink(const Path & path, const std::string & target) override
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
nix::createSymlink(target, p);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace nix {
|
|||
void dumpPath(const Path & path, Sink & sink,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
|
||||
void dumpString(const std::string & s, Sink & sink);
|
||||
void dumpString(std::string_view s, Sink & sink);
|
||||
|
||||
/* FIXME: fix this API, it sucks. */
|
||||
struct ParseSink
|
||||
|
|
@ -60,7 +60,7 @@ struct ParseSink
|
|||
virtual void preallocateContents(uint64_t size) { };
|
||||
virtual void receiveContents(std::string_view data) { };
|
||||
|
||||
virtual void createSymlink(const Path & path, const string & target) { };
|
||||
virtual void createSymlink(const Path & path, const std::string & target) { };
|
||||
};
|
||||
|
||||
/* If the NAR archive contains a single file at top-level, then save
|
||||
|
|
@ -82,7 +82,7 @@ struct RetrieveRegularNARSink : ParseSink
|
|||
sink(data);
|
||||
}
|
||||
|
||||
void createSymlink(const Path & path, const string & target) override
|
||||
void createSymlink(const Path & path, const std::string & target) override
|
||||
{
|
||||
regular = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ void Completions::add(std::string completion, std::string description)
|
|||
bool Completion::operator<(const Completion & other) const
|
||||
{ return completion < other.completion || (completion == other.completion && description < other.description); }
|
||||
|
||||
bool pathCompletions = false;
|
||||
CompletionType completionType = ctNormal;
|
||||
std::shared_ptr<Completions> completions;
|
||||
|
||||
std::string completionMarker = "___COMPLETE___";
|
||||
|
|
@ -76,13 +76,13 @@ void Args::parseCmdline(const Strings & _cmdline)
|
|||
/* 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];
|
||||
*pos = (std::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]);
|
||||
cmdline.insert(next, (std::string) "-" + arg[j]);
|
||||
else {
|
||||
cmdline.insert(next, string(arg, j));
|
||||
cmdline.insert(next, std::string(arg, j));
|
||||
break;
|
||||
}
|
||||
arg = *pos;
|
||||
|
|
@ -139,7 +139,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
return true;
|
||||
};
|
||||
|
||||
if (string(*pos, 0, 2) == "--") {
|
||||
if (std::string(*pos, 0, 2) == "--") {
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
for (auto & [name, flag] : longFlags) {
|
||||
if (!hiddenCategories.count(flag->category)
|
||||
|
|
@ -147,12 +147,12 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
completions->add("--" + name, flag->description);
|
||||
}
|
||||
}
|
||||
auto i = longFlags.find(string(*pos, 2));
|
||||
auto i = longFlags.find(std::string(*pos, 2));
|
||||
if (i == longFlags.end()) return false;
|
||||
return process("--" + i->first, *i->second);
|
||||
}
|
||||
|
||||
if (string(*pos, 0, 1) == "-" && pos->size() == 2) {
|
||||
if (std::string(*pos, 0, 1) == "-" && pos->size() == 2) {
|
||||
auto c = (*pos)[1];
|
||||
auto i = shortFlags.find(c);
|
||||
if (i == shortFlags.end()) return false;
|
||||
|
|
@ -277,7 +277,7 @@ Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, std::optional<
|
|||
|
||||
static void _completePath(std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
pathCompletions = true;
|
||||
completionType = ctFilenames;
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ public:
|
|||
}
|
||||
|
||||
/* Expect a string argument. */
|
||||
void expectArg(const std::string & label, string * dest, bool optional = false)
|
||||
void expectArg(const std::string & label, std::string * dest, bool optional = false)
|
||||
{
|
||||
expectArgs({
|
||||
.label = label,
|
||||
|
|
@ -237,7 +237,13 @@ public:
|
|||
void add(std::string completion, std::string description = "");
|
||||
};
|
||||
extern std::shared_ptr<Completions> completions;
|
||||
extern bool pathCompletions;
|
||||
|
||||
enum CompletionType {
|
||||
ctNormal,
|
||||
ctFilenames,
|
||||
ctAttrs
|
||||
};
|
||||
extern CompletionType completionType;
|
||||
|
||||
std::optional<std::string> needsCompletion(std::string_view s);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
static const int COMPRESSION_LEVEL_DEFAULT = -1;
|
||||
|
||||
// Don't feed brotli too much at once.
|
||||
struct ChunkedCompressionSink : CompressionSink
|
||||
{
|
||||
|
|
@ -65,14 +67,16 @@ struct ArchiveCompressionSink : CompressionSink
|
|||
Sink & nextSink;
|
||||
struct archive * archive;
|
||||
|
||||
ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel) : nextSink(nextSink) {
|
||||
ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink)
|
||||
{
|
||||
archive = archive_write_new();
|
||||
if (!archive) throw Error("failed to initialize libarchive");
|
||||
check(archive_write_add_filter_by_name(archive, format.c_str()), "couldn't initialize compression (%s)");
|
||||
check(archive_write_set_format_raw(archive));
|
||||
if (format == "xz" && parallel) {
|
||||
if (parallel)
|
||||
check(archive_write_set_filter_option(archive, format.c_str(), "threads", "0"));
|
||||
}
|
||||
if (level != COMPRESSION_LEVEL_DEFAULT)
|
||||
check(archive_write_set_filter_option(archive, format.c_str(), "compression-level", std::to_string(level).c_str()));
|
||||
// disable internal buffering
|
||||
check(archive_write_set_bytes_per_block(archive, 0));
|
||||
// disable output padding
|
||||
|
|
@ -126,7 +130,11 @@ private:
|
|||
struct NoneSink : CompressionSink
|
||||
{
|
||||
Sink & nextSink;
|
||||
NoneSink(Sink & nextSink) : nextSink(nextSink) { }
|
||||
NoneSink(Sink & nextSink, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink)
|
||||
{
|
||||
if (level != COMPRESSION_LEVEL_DEFAULT)
|
||||
warn("requested compression level '%d' not supported by compression method 'none'", level);
|
||||
}
|
||||
void finish() override { flush(); }
|
||||
void write(std::string_view data) override { nextSink(data); }
|
||||
};
|
||||
|
|
@ -182,13 +190,13 @@ struct BrotliDecompressionSink : ChunkedCompressionSink
|
|||
}
|
||||
};
|
||||
|
||||
ref<std::string> decompress(const std::string & method, const std::string & in)
|
||||
std::string decompress(const std::string & method, std::string_view in)
|
||||
{
|
||||
StringSink ssink;
|
||||
auto sink = makeDecompressionSink(method, ssink);
|
||||
(*sink)(in);
|
||||
sink->finish();
|
||||
return ssink.s;
|
||||
return std::move(ssink.s);
|
||||
}
|
||||
|
||||
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink)
|
||||
|
|
@ -257,13 +265,13 @@ struct BrotliCompressionSink : ChunkedCompressionSink
|
|||
}
|
||||
};
|
||||
|
||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel)
|
||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel, int level)
|
||||
{
|
||||
std::vector<std::string> la_supports = {
|
||||
"bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", "lzip", "lzma", "lzop", "xz", "zstd"
|
||||
};
|
||||
if (std::find(la_supports.begin(), la_supports.end(), method) != la_supports.end()) {
|
||||
return make_ref<ArchiveCompressionSink>(nextSink, method, parallel);
|
||||
return make_ref<ArchiveCompressionSink>(nextSink, method, parallel, level);
|
||||
}
|
||||
if (method == "none")
|
||||
return make_ref<NoneSink>(nextSink);
|
||||
|
|
@ -273,13 +281,13 @@ ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & next
|
|||
throw UnknownCompressionMethod("unknown compression method '%s'", method);
|
||||
}
|
||||
|
||||
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel)
|
||||
std::string compress(const std::string & method, std::string_view in, const bool parallel, int level)
|
||||
{
|
||||
StringSink ssink;
|
||||
auto sink = makeCompressionSink(method, ssink, parallel);
|
||||
auto sink = makeCompressionSink(method, ssink, parallel, level);
|
||||
(*sink)(in);
|
||||
sink->finish();
|
||||
return ssink.s;
|
||||
return std::move(ssink.s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ struct CompressionSink : BufferedSink, FinishSink
|
|||
using FinishSink::finish;
|
||||
};
|
||||
|
||||
ref<std::string> decompress(const std::string & method, const std::string & in);
|
||||
std::string decompress(const std::string & method, std::string_view in);
|
||||
|
||||
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink);
|
||||
|
||||
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel = false);
|
||||
std::string compress(const std::string & method, std::string_view in, const bool parallel = false, int level = -1);
|
||||
|
||||
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, int level = -1);
|
||||
|
||||
MakeError(UnknownCompressionMethod, Error);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "config.hh"
|
||||
#include "args.hh"
|
||||
#include "abstract-setting-to-json.hh"
|
||||
#include "experimental-features.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
|
@ -80,16 +81,16 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
|
|||
unsigned int pos = 0;
|
||||
|
||||
while (pos < contents.size()) {
|
||||
string line;
|
||||
std::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);
|
||||
auto hash = line.find('#');
|
||||
if (hash != std::string::npos)
|
||||
line = std::string(line, 0, hash);
|
||||
|
||||
vector<string> tokens = tokenizeString<vector<string> >(line);
|
||||
auto tokens = tokenizeString<std::vector<std::string>>(line);
|
||||
if (tokens.empty()) continue;
|
||||
|
||||
if (tokens.size() < 2)
|
||||
|
|
@ -119,9 +120,9 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
|
|||
if (tokens[1] != "=")
|
||||
throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
|
||||
|
||||
string name = tokens[0];
|
||||
std::string name = tokens[0];
|
||||
|
||||
vector<string>::iterator i = tokens.begin();
|
||||
auto i = tokens.begin();
|
||||
advance(i, 2);
|
||||
|
||||
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
|
||||
|
|
@ -131,7 +132,7 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
|
|||
void AbstractConfig::applyConfigFile(const Path & path)
|
||||
{
|
||||
try {
|
||||
string contents = readFile(path);
|
||||
std::string contents = readFile(path);
|
||||
applyConfig(contents, path);
|
||||
} catch (SysError &) { }
|
||||
}
|
||||
|
|
@ -177,11 +178,6 @@ AbstractSetting::AbstractSetting(
|
|||
{
|
||||
}
|
||||
|
||||
void AbstractSetting::setDefault(const std::string & str)
|
||||
{
|
||||
if (!overridden) set(str);
|
||||
}
|
||||
|
||||
nlohmann::json AbstractSetting::toJSON()
|
||||
{
|
||||
return nlohmann::json(toJSONObject());
|
||||
|
|
@ -318,6 +314,31 @@ template<> std::string BaseSetting<StringSet>::to_string() const
|
|||
return concatStringsSep(" ", value);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<std::set<ExperimentalFeature>>::set(const std::string & str, bool append)
|
||||
{
|
||||
if (!append) value.clear();
|
||||
for (auto & s : tokenizeString<StringSet>(str)) {
|
||||
auto thisXpFeature = parseExperimentalFeature(s);
|
||||
if (thisXpFeature)
|
||||
value.insert(thisXpFeature.value());
|
||||
else
|
||||
warn("unknown experimental feature '%s'", s);
|
||||
}
|
||||
}
|
||||
|
||||
template<> bool BaseSetting<std::set<ExperimentalFeature>>::isAppendable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() const
|
||||
{
|
||||
StringSet stringifiedXpFeatures;
|
||||
for (auto & feature : value)
|
||||
stringifiedXpFeatures.insert(std::string(showExperimentalFeature(feature)));
|
||||
return concatStringsSep(" ", stringifiedXpFeatures);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<StringMap>::set(const std::string & str, bool append)
|
||||
{
|
||||
if (!append) value.clear();
|
||||
|
|
@ -353,6 +374,7 @@ template class BaseSetting<std::string>;
|
|||
template class BaseSetting<Strings>;
|
||||
template class BaseSetting<StringSet>;
|
||||
template class BaseSetting<StringMap>;
|
||||
template class BaseSetting<std::set<ExperimentalFeature>>;
|
||||
|
||||
void PathSetting::set(const std::string & str, bool append)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -194,8 +194,6 @@ public:
|
|||
|
||||
bool overridden = false;
|
||||
|
||||
void setDefault(const std::string & str);
|
||||
|
||||
protected:
|
||||
|
||||
AbstractSetting(
|
||||
|
|
@ -234,16 +232,19 @@ protected:
|
|||
|
||||
T value;
|
||||
const T defaultValue;
|
||||
const bool documentDefault;
|
||||
|
||||
public:
|
||||
|
||||
BaseSetting(const T & def,
|
||||
const bool documentDefault,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {})
|
||||
: AbstractSetting(name, description, aliases)
|
||||
, value(def)
|
||||
, defaultValue(def)
|
||||
, documentDefault(documentDefault)
|
||||
{ }
|
||||
|
||||
operator const T &() const { return value; }
|
||||
|
|
@ -253,6 +254,7 @@ public:
|
|||
bool operator !=(const T & v2) const { return value != v2; }
|
||||
void operator =(const T & v) { assign(v); }
|
||||
virtual void assign(const T & v) { value = v; }
|
||||
void setDefault(const T & v) { if (!overridden) value = v; }
|
||||
|
||||
void set(const std::string & str, bool append = false) override;
|
||||
|
||||
|
|
@ -289,8 +291,9 @@ public:
|
|||
const T & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {})
|
||||
: BaseSetting<T>(def, name, description, aliases)
|
||||
const std::set<std::string> & aliases = {},
|
||||
const bool documentDefault = true)
|
||||
: BaseSetting<T>(def, documentDefault, name, description, aliases)
|
||||
{
|
||||
options->addSetting(this);
|
||||
}
|
||||
|
|
@ -312,7 +315,7 @@ public:
|
|||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {})
|
||||
: BaseSetting<Path>(def, name, description, aliases)
|
||||
: BaseSetting<Path>(def, true, name, description, aliases)
|
||||
, allowEmpty(allowEmpty)
|
||||
{
|
||||
options->addSetting(this);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ BaseError & BaseError::addTrace(std::optional<ErrPos> e, hintformat hint)
|
|||
|
||||
// c++ std::exception descendants must have a 'const char* what()' function.
|
||||
// This stringifies the error and caches it for use by what(), or similarly by msg().
|
||||
const string & BaseError::calcWhat() const
|
||||
const std::string & BaseError::calcWhat() const
|
||||
{
|
||||
if (what_.has_value())
|
||||
return *what_;
|
||||
|
|
@ -25,21 +25,21 @@ const string & BaseError::calcWhat() const
|
|||
err.name = sname();
|
||||
|
||||
std::ostringstream oss;
|
||||
showErrorInfo(oss, err, false);
|
||||
showErrorInfo(oss, err, loggerSettings.showTrace);
|
||||
what_ = oss.str();
|
||||
|
||||
return *what_;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<string> ErrorInfo::programName = std::nullopt;
|
||||
std::optional<std::string> ErrorInfo::programName = std::nullopt;
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const hintformat & hf)
|
||||
{
|
||||
return os << hf.str();
|
||||
}
|
||||
|
||||
string showErrPos(const ErrPos & errPos)
|
||||
std::string showErrPos(const ErrPos & errPos)
|
||||
{
|
||||
if (errPos.line > 0) {
|
||||
if (errPos.column > 0) {
|
||||
|
|
@ -68,7 +68,7 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
|||
|
||||
// count the newlines.
|
||||
int count = 0;
|
||||
string line;
|
||||
std::string line;
|
||||
int pl = errPos.line - 1;
|
||||
do
|
||||
{
|
||||
|
|
@ -100,7 +100,7 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
|||
std::istringstream iss(errPos.file);
|
||||
// count the newlines.
|
||||
int count = 0;
|
||||
string line;
|
||||
std::string line;
|
||||
int pl = errPos.line - 1;
|
||||
|
||||
LinesOfCode loc;
|
||||
|
|
@ -132,7 +132,7 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
|||
|
||||
// print lines of code to the ostream, indicating the error column.
|
||||
void printCodeLines(std::ostream & out,
|
||||
const string & prefix,
|
||||
const std::string & prefix,
|
||||
const ErrPos & errPos,
|
||||
const LinesOfCode & loc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,16 +61,16 @@ typedef enum {
|
|||
|
||||
// the lines of code surrounding an error.
|
||||
struct LinesOfCode {
|
||||
std::optional<string> prevLineOfCode;
|
||||
std::optional<string> errLineOfCode;
|
||||
std::optional<string> nextLineOfCode;
|
||||
std::optional<std::string> prevLineOfCode;
|
||||
std::optional<std::string> errLineOfCode;
|
||||
std::optional<std::string> nextLineOfCode;
|
||||
};
|
||||
|
||||
// ErrPos indicates the location of an error in a nix file.
|
||||
struct ErrPos {
|
||||
int line = 0;
|
||||
int column = 0;
|
||||
string file;
|
||||
std::string file;
|
||||
FileOrigin origin;
|
||||
|
||||
operator bool() const
|
||||
|
|
@ -80,7 +80,7 @@ struct ErrPos {
|
|||
|
||||
// convert from the Pos struct, found in libexpr.
|
||||
template <class P>
|
||||
ErrPos& operator=(const P &pos)
|
||||
ErrPos & operator=(const P & pos)
|
||||
{
|
||||
origin = pos.origin;
|
||||
line = pos.line;
|
||||
|
|
@ -94,7 +94,7 @@ struct ErrPos {
|
|||
}
|
||||
|
||||
template <class P>
|
||||
ErrPos(const P &p)
|
||||
ErrPos(const P & p)
|
||||
{
|
||||
*this = p;
|
||||
}
|
||||
|
|
@ -107,15 +107,15 @@ struct Trace {
|
|||
|
||||
struct ErrorInfo {
|
||||
Verbosity level;
|
||||
string name; // FIXME: rename
|
||||
std::string name; // FIXME: rename
|
||||
hintformat msg;
|
||||
std::optional<ErrPos> errPos;
|
||||
std::list<Trace> traces;
|
||||
|
||||
static std::optional<string> programName;
|
||||
static std::optional<std::string> programName;
|
||||
};
|
||||
|
||||
std::ostream& showErrorInfo(std::ostream &out, const ErrorInfo &einfo, bool showTrace);
|
||||
std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace);
|
||||
|
||||
/* BaseError should generally not be caught, as it has Interrupted as
|
||||
a subclass. Catch Error instead. */
|
||||
|
|
@ -124,8 +124,8 @@ class BaseError : public std::exception
|
|||
protected:
|
||||
mutable ErrorInfo err;
|
||||
|
||||
mutable std::optional<string> what_;
|
||||
const string& calcWhat() const;
|
||||
mutable std::optional<std::string> what_;
|
||||
const std::string & calcWhat() const;
|
||||
|
||||
public:
|
||||
unsigned int status = 1; // exit status
|
||||
|
|
@ -137,7 +137,7 @@ public:
|
|||
{ }
|
||||
|
||||
template<typename... Args>
|
||||
BaseError(const std::string & fs, const Args & ... args)
|
||||
explicit BaseError(const std::string & fs, const Args & ... args)
|
||||
: err { .level = lvlError, .msg = hintfmt(fs, args...) }
|
||||
{ }
|
||||
|
||||
|
|
@ -162,11 +162,11 @@ public:
|
|||
const char * what() const noexcept override { return calcWhat().c_str(); }
|
||||
#endif
|
||||
|
||||
const string & msg() const { return calcWhat(); }
|
||||
const std::string & msg() const { return calcWhat(); }
|
||||
const ErrorInfo & info() const { calcWhat(); return err; }
|
||||
|
||||
template<typename... Args>
|
||||
BaseError & addTrace(std::optional<ErrPos> e, const string &fs, const Args & ... args)
|
||||
BaseError & addTrace(std::optional<ErrPos> e, const std::string & fs, const Args & ... args)
|
||||
{
|
||||
return addTrace(e, hintfmt(fs, args...));
|
||||
}
|
||||
|
|
|
|||
61
src/libutil/experimental-features.cc
Normal file
61
src/libutil/experimental-features.cc
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#include "experimental-features.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::map<ExperimentalFeature, std::string> stringifiedXpFeatures = {
|
||||
{ Xp::CaDerivations, "ca-derivations" },
|
||||
{ Xp::Flakes, "flakes" },
|
||||
{ Xp::NixCommand, "nix-command" },
|
||||
{ Xp::RecursiveNix, "recursive-nix" },
|
||||
{ Xp::NoUrlLiterals, "no-url-literals" },
|
||||
{ Xp::AutoAllocateUids, "auto-allocate-uids" },
|
||||
{ Xp::SystemdCgroup, "systemd-cgroup" },
|
||||
};
|
||||
|
||||
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name)
|
||||
{
|
||||
using ReverseXpMap = std::map<std::string_view, ExperimentalFeature>;
|
||||
|
||||
static auto reverseXpMap = []()
|
||||
{
|
||||
auto reverseXpMap = std::make_unique<ReverseXpMap>();
|
||||
for (auto & [feature, name] : stringifiedXpFeatures)
|
||||
(*reverseXpMap)[name] = feature;
|
||||
return reverseXpMap;
|
||||
}();
|
||||
|
||||
if (auto feature = get(*reverseXpMap, name))
|
||||
return *feature;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string_view showExperimentalFeature(const ExperimentalFeature feature)
|
||||
{
|
||||
return stringifiedXpFeatures.at(feature);
|
||||
}
|
||||
|
||||
std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> & rawFeatures)
|
||||
{
|
||||
std::set<ExperimentalFeature> res;
|
||||
for (auto & rawFeature : rawFeatures) {
|
||||
if (auto feature = parseExperimentalFeature(rawFeature))
|
||||
res.insert(*feature);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature)
|
||||
: Error("experimental Nix feature '%1%' is disabled; use '--extra-experimental-features %1%' to override", showExperimentalFeature(feature))
|
||||
, missingFeature(feature)
|
||||
{}
|
||||
|
||||
std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & feature)
|
||||
{
|
||||
return str << showExperimentalFeature(feature);
|
||||
}
|
||||
|
||||
}
|
||||
58
src/libutil/experimental-features.hh
Normal file
58
src/libutil/experimental-features.hh
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include "comparator.hh"
|
||||
#include "error.hh"
|
||||
#include "nlohmann/json_fwd.hpp"
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* The list of available experimental features.
|
||||
*
|
||||
* If you update this, don’t forget to also change the map defining their
|
||||
* string representation in the corresponding `.cc` file.
|
||||
**/
|
||||
enum struct ExperimentalFeature
|
||||
{
|
||||
CaDerivations,
|
||||
Flakes,
|
||||
NixCommand,
|
||||
RecursiveNix,
|
||||
NoUrlLiterals,
|
||||
AutoAllocateUids,
|
||||
SystemdCgroup,
|
||||
};
|
||||
|
||||
/**
|
||||
* Just because writing `ExperimentalFeature::CaDerivations` is way too long
|
||||
*/
|
||||
using Xp = ExperimentalFeature;
|
||||
|
||||
const std::optional<ExperimentalFeature> parseExperimentalFeature(
|
||||
const std::string_view & name);
|
||||
std::string_view showExperimentalFeature(const ExperimentalFeature);
|
||||
|
||||
std::ostream & operator<<(
|
||||
std::ostream & str,
|
||||
const ExperimentalFeature & feature);
|
||||
|
||||
/**
|
||||
* Parse a set of strings to the corresponding set of experimental features,
|
||||
* ignoring (but warning for) any unkwown feature.
|
||||
*/
|
||||
std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> &);
|
||||
|
||||
class MissingExperimentalFeature : public Error
|
||||
{
|
||||
public:
|
||||
ExperimentalFeature missingFeature;
|
||||
|
||||
MissingExperimentalFeature(ExperimentalFeature);
|
||||
virtual const char * sname() const override
|
||||
{
|
||||
return "MissingExperimentalFeature";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
46
src/libutil/fmt.cc
Normal file
46
src/libutil/fmt.cc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "fmt.hh"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string hiliteMatches(
|
||||
std::string_view s,
|
||||
std::vector<std::smatch> matches,
|
||||
std::string_view prefix,
|
||||
std::string_view postfix)
|
||||
{
|
||||
// Avoid copy on zero matches
|
||||
if (matches.size() == 0)
|
||||
return (std::string) s;
|
||||
|
||||
std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {
|
||||
return a.position() < b.position();
|
||||
});
|
||||
|
||||
std::string out;
|
||||
ssize_t last_end = 0;
|
||||
|
||||
for (auto it = matches.begin(); it != matches.end();) {
|
||||
auto m = *it;
|
||||
size_t start = m.position();
|
||||
out.append(s.substr(last_end, m.position() - last_end));
|
||||
// Merge continous matches
|
||||
ssize_t end = start + m.length();
|
||||
while (++it != matches.end() && (*it).position() <= end) {
|
||||
auto n = *it;
|
||||
ssize_t nend = start + (n.position() - start + n.length());
|
||||
if (nend > end)
|
||||
end = nend;
|
||||
}
|
||||
out.append(prefix);
|
||||
out.append(s.substr(start, end - start));
|
||||
out.append(postfix);
|
||||
last_end = end;
|
||||
}
|
||||
|
||||
out.append(s.substr(last_end));
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <boost/format.hpp>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include "ansicolor.hh"
|
||||
|
||||
|
||||
|
|
@ -9,7 +10,6 @@ namespace nix {
|
|||
|
||||
|
||||
/* Inherit some names from other namespaces for convenience. */
|
||||
using std::string;
|
||||
using boost::format;
|
||||
|
||||
|
||||
|
|
@ -20,8 +20,8 @@ struct nop { template<typename... T> nop(T...) {} };
|
|||
|
||||
struct FormatOrString
|
||||
{
|
||||
string s;
|
||||
FormatOrString(const string & s) : s(s) { };
|
||||
std::string s;
|
||||
FormatOrString(std::string s) : s(std::move(s)) { };
|
||||
template<class F>
|
||||
FormatOrString(const F & f) : s(f.str()) { };
|
||||
FormatOrString(const char * s) : s(s) { };
|
||||
|
|
@ -101,7 +101,7 @@ std::ostream & operator<<(std::ostream & out, const normaltxt<T> & y)
|
|||
class hintformat
|
||||
{
|
||||
public:
|
||||
hintformat(const string & format) : fmt(format)
|
||||
hintformat(const std::string & format) : fmt(format)
|
||||
{
|
||||
fmt.exceptions(boost::io::all_error_bits ^
|
||||
boost::io::too_many_args_bit ^
|
||||
|
|
@ -154,4 +154,16 @@ inline hintformat hintfmt(std::string plain_string)
|
|||
// we won't be receiving any args in this case, so just print the original string
|
||||
return hintfmt("%s", normaltxt(plain_string));
|
||||
}
|
||||
|
||||
/* Highlight all the given matches in the given string `s` by wrapping
|
||||
them between `prefix` and `postfix`.
|
||||
|
||||
If some matches overlap, then their union will be wrapped rather
|
||||
than the individual matches. */
|
||||
std::string hiliteMatches(
|
||||
std::string_view s,
|
||||
std::vector<std::smatch> matches,
|
||||
std::string_view prefix,
|
||||
std::string_view postfix);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,31 +66,31 @@ bool Hash::operator < (const Hash & h) const
|
|||
}
|
||||
|
||||
|
||||
const string base16Chars = "0123456789abcdef";
|
||||
const std::string base16Chars = "0123456789abcdef";
|
||||
|
||||
|
||||
static string printHash16(const Hash & hash)
|
||||
static std::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);
|
||||
return std::string(buf, hash.hashSize * 2);
|
||||
}
|
||||
|
||||
|
||||
// omitted: E O U T
|
||||
const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
|
||||
const std::string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
|
||||
|
||||
|
||||
static string printHash32(const Hash & hash)
|
||||
static std::string printHash32(const Hash & hash)
|
||||
{
|
||||
assert(hash.hashSize);
|
||||
size_t len = hash.base32Len();
|
||||
assert(len);
|
||||
|
||||
string s;
|
||||
std::string s;
|
||||
s.reserve(len);
|
||||
|
||||
for (int n = (int) len - 1; n >= 0; n--) {
|
||||
|
|
@ -107,7 +107,7 @@ static string printHash32(const Hash & hash)
|
|||
}
|
||||
|
||||
|
||||
string printHash16or32(const Hash & hash)
|
||||
std::string printHash16or32(const Hash & hash)
|
||||
{
|
||||
assert(hash.type);
|
||||
return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false);
|
||||
|
|
@ -151,7 +151,8 @@ Hash Hash::parseSRI(std::string_view original) {
|
|||
}
|
||||
|
||||
// Mutates the string to eliminate the prefixes when found
|
||||
static std::pair<std::optional<HashType>, bool> getParsedTypeAndSRI(std::string_view & rest) {
|
||||
static std::pair<std::optional<HashType>, bool> getParsedTypeAndSRI(std::string_view & rest)
|
||||
{
|
||||
bool isSRI = false;
|
||||
|
||||
// Parse the has type before the separater, if there was one.
|
||||
|
|
@ -259,7 +260,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI)
|
|||
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
|
||||
}
|
||||
|
||||
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
|
||||
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
|
||||
{
|
||||
if (hashStr.empty()) {
|
||||
if (!ht)
|
||||
|
|
@ -402,7 +403,7 @@ HashType parseHashType(std::string_view s)
|
|||
throw UsageError("unknown hash algorithm '%1%'", s);
|
||||
}
|
||||
|
||||
string printHashType(HashType ht)
|
||||
std::string printHashType(HashType ht)
|
||||
{
|
||||
switch (ht) {
|
||||
case htMD5: return "md5";
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const int sha512HashSize = 64;
|
|||
|
||||
extern std::set<std::string> hashTypes;
|
||||
|
||||
extern const string base32Chars;
|
||||
extern const std::string base32Chars;
|
||||
|
||||
enum Base : int { Base64, Base32, Base16, SRI };
|
||||
|
||||
|
|
@ -107,10 +107,10 @@ public:
|
|||
};
|
||||
|
||||
/* Helper that defaults empty hashes to the 0 hash. */
|
||||
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht);
|
||||
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
|
||||
|
||||
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
|
||||
string printHash16or32(const Hash & hash);
|
||||
std::string printHash16or32(const Hash & hash);
|
||||
|
||||
/* Compute the hash of the given string. */
|
||||
Hash hashString(HashType ht, std::string_view s);
|
||||
|
|
@ -135,7 +135,7 @@ HashType parseHashType(std::string_view s);
|
|||
std::optional<HashType> parseHashTypeOpt(std::string_view s);
|
||||
|
||||
/* And the reverse. */
|
||||
string printHashType(HashType ht);
|
||||
std::string printHashType(HashType ht);
|
||||
|
||||
|
||||
union Ctx;
|
||||
|
|
|
|||
|
|
@ -7,16 +7,38 @@ 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 << '"';
|
||||
constexpr size_t BUF_SIZE = 4096;
|
||||
char buf[BUF_SIZE + 7]; // BUF_SIZE + largest single sequence of puts
|
||||
size_t bufPos = 0;
|
||||
|
||||
const auto flush = [&] {
|
||||
str.write(buf, bufPos);
|
||||
bufPos = 0;
|
||||
};
|
||||
const auto put = [&] (char c) {
|
||||
buf[bufPos++] = c;
|
||||
};
|
||||
|
||||
put('"');
|
||||
for (auto i = start; i != end; i++) {
|
||||
if (bufPos >= BUF_SIZE) flush();
|
||||
if (*i == '\"' || *i == '\\') { put('\\'); put(*i); }
|
||||
else if (*i == '\n') { put('\\'); put('n'); }
|
||||
else if (*i == '\r') { put('\\'); put('r'); }
|
||||
else if (*i == '\t') { put('\\'); put('t'); }
|
||||
else if (*i >= 0 && *i < 32) {
|
||||
const char hex[17] = "0123456789abcdef";
|
||||
put('\\');
|
||||
put('u');
|
||||
put(hex[(uint16_t(*i) >> 12) & 0xf]);
|
||||
put(hex[(uint16_t(*i) >> 8) & 0xf]);
|
||||
put(hex[(uint16_t(*i) >> 4) & 0xf]);
|
||||
put(hex[(uint16_t(*i) >> 0) & 0xf]);
|
||||
}
|
||||
else put(*i);
|
||||
}
|
||||
put('"');
|
||||
flush();
|
||||
}
|
||||
|
||||
void toJSON(std::ostream & str, const char * s)
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ void warnOnce(bool & haveWarned, const FormatOrString & fs)
|
|||
}
|
||||
}
|
||||
|
||||
void writeToStderr(const string & s)
|
||||
void writeToStderr(std::string_view s)
|
||||
{
|
||||
try {
|
||||
writeFull(STDERR_FILENO, s, false);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ struct LoggerSettings : Config
|
|||
Setting<bool> showTrace{
|
||||
this, false, "show-trace",
|
||||
R"(
|
||||
Where Nix should print out a stack trace in case of Nix
|
||||
Whether Nix should print out a stack trace in case of Nix
|
||||
expression evaluation errors.
|
||||
)"};
|
||||
};
|
||||
|
|
@ -189,13 +189,14 @@ extern Verbosity verbosity; /* suppress msgs > this */
|
|||
/* Print a string 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...) \
|
||||
#define printMsgUsing(loggerParam, level, args...) \
|
||||
do { \
|
||||
auto __lvl = level; \
|
||||
if (__lvl <= nix::verbosity) { \
|
||||
logger->log(__lvl, fmt(args)); \
|
||||
loggerParam->log(__lvl, fmt(args)); \
|
||||
} \
|
||||
} while (0)
|
||||
#define printMsg(level, args...) printMsgUsing(logger, level, args)
|
||||
|
||||
#define printError(args...) printMsg(lvlError, args)
|
||||
#define notice(args...) printMsg(lvlNotice, args)
|
||||
|
|
@ -215,6 +216,6 @@ inline void warn(const std::string & fs, const Args & ... args)
|
|||
|
||||
void warnOnce(bool & haveWarned, const FormatOrString & fs);
|
||||
|
||||
void writeToStderr(const string & s);
|
||||
void writeToStderr(std::string_view s);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
ref<T>(const ref<T> & r)
|
||||
ref(const ref<T> & r)
|
||||
: p(r.p)
|
||||
{ }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
#if 0
|
||||
#include "logging.hh"
|
||||
#include "rust-ffi.hh"
|
||||
|
||||
extern "C" std::exception_ptr * make_error(rust::StringSlice s)
|
||||
{
|
||||
return new std::exception_ptr(std::make_exception_ptr(nix::Error(std::string(s.ptr, s.size))));
|
||||
}
|
||||
|
||||
extern "C" void destroy_error(std::exception_ptr * ex)
|
||||
{
|
||||
free(ex);
|
||||
}
|
||||
|
||||
namespace rust {
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const String & s)
|
||||
{
|
||||
str << (std::string_view) s;
|
||||
return str;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
#pragma once
|
||||
#if 0
|
||||
|
||||
#include "serialise.hh"
|
||||
|
||||
#include <string_view>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
|
||||
namespace rust {
|
||||
|
||||
typedef void (*DropFun)(void *);
|
||||
|
||||
/* A Rust value of N bytes. It can be moved but not copied. When it
|
||||
goes out of scope, the C++ destructor will run the drop
|
||||
function. */
|
||||
template<std::size_t N, DropFun drop>
|
||||
struct Value
|
||||
{
|
||||
protected:
|
||||
|
||||
std::array<char, N> raw;
|
||||
|
||||
~Value()
|
||||
{
|
||||
if (!isEvacuated()) {
|
||||
drop(this);
|
||||
evacuate();
|
||||
}
|
||||
}
|
||||
|
||||
// Must not be called directly.
|
||||
Value()
|
||||
{ }
|
||||
|
||||
Value(Value && other)
|
||||
: raw(other.raw)
|
||||
{
|
||||
other.evacuate();
|
||||
}
|
||||
|
||||
void operator =(Value && other)
|
||||
{
|
||||
if (!isEvacuated())
|
||||
drop(this);
|
||||
raw = other.raw;
|
||||
other.evacuate();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/* FIXME: optimize these (ideally in such a way that the compiler
|
||||
can elide most calls to evacuate() / isEvacuated(). */
|
||||
inline void evacuate()
|
||||
{
|
||||
for (auto & i : raw) i = 0;
|
||||
}
|
||||
|
||||
inline bool isEvacuated()
|
||||
{
|
||||
for (auto & i : raw)
|
||||
if (i != 0) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/* A Rust vector. */
|
||||
template<typename T, DropFun drop>
|
||||
struct Vec : Value<3 * sizeof(void *), drop>
|
||||
{
|
||||
inline size_t size() const
|
||||
{
|
||||
return ((const size_t *) &this->raw)[2];
|
||||
}
|
||||
|
||||
const T * data() const
|
||||
{
|
||||
return ((const T * *) &this->raw)[0];
|
||||
}
|
||||
};
|
||||
|
||||
/* A Rust slice. */
|
||||
template<typename T>
|
||||
struct Slice
|
||||
{
|
||||
const T * ptr;
|
||||
size_t size;
|
||||
|
||||
Slice(const T * ptr, size_t size) : ptr(ptr), size(size)
|
||||
{
|
||||
assert(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct StringSlice : Slice<char>
|
||||
{
|
||||
StringSlice(const std::string & s): Slice(s.data(), s.size()) {}
|
||||
explicit StringSlice(std::string_view s): Slice(s.data(), s.size()) {}
|
||||
StringSlice(const char * s): Slice(s, strlen(s)) {}
|
||||
|
||||
operator std::string_view() const
|
||||
{
|
||||
return std::string_view(ptr, size);
|
||||
}
|
||||
};
|
||||
|
||||
/* A Rust string. */
|
||||
struct String;
|
||||
|
||||
extern "C" {
|
||||
void ffi_String_new(StringSlice s, String * out);
|
||||
void ffi_String_drop(void * s);
|
||||
}
|
||||
|
||||
struct String : Vec<char, ffi_String_drop>
|
||||
{
|
||||
String() = delete;
|
||||
|
||||
String(std::string_view s)
|
||||
{
|
||||
ffi_String_new(StringSlice(s), this);
|
||||
}
|
||||
|
||||
String(const char * s)
|
||||
: String({s, std::strlen(s)})
|
||||
{
|
||||
}
|
||||
|
||||
operator std::string_view() const
|
||||
{
|
||||
return std::string_view(data(), size());
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const String & s);
|
||||
|
||||
/* C++ representation of Rust's Result<T, CppException>. */
|
||||
template<typename T>
|
||||
struct Result
|
||||
{
|
||||
enum { Ok = 0, Err = 1, Uninit = 2 } tag;
|
||||
|
||||
union {
|
||||
T data;
|
||||
std::exception_ptr * exc;
|
||||
};
|
||||
|
||||
Result() : tag(Uninit) { }; // FIXME: remove
|
||||
|
||||
Result(const Result &) = delete;
|
||||
|
||||
Result(Result && other)
|
||||
: tag(other.tag)
|
||||
{
|
||||
other.tag = Uninit;
|
||||
if (tag == Ok)
|
||||
data = std::move(other.data);
|
||||
else if (tag == Err)
|
||||
exc = other.exc;
|
||||
}
|
||||
|
||||
~Result()
|
||||
{
|
||||
if (tag == Ok)
|
||||
data.~T();
|
||||
else if (tag == Err)
|
||||
free(exc);
|
||||
else if (tag == Uninit)
|
||||
;
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Rethrow the wrapped exception or return the wrapped value. */
|
||||
T unwrap()
|
||||
{
|
||||
if (tag == Ok) {
|
||||
tag = Uninit;
|
||||
return std::move(data);
|
||||
}
|
||||
else if (tag == Err)
|
||||
std::rethrow_exception(*exc);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
@ -110,7 +110,7 @@ std::string Source::drain()
|
|||
{
|
||||
StringSink s;
|
||||
drainInto(s);
|
||||
return *s.s;
|
||||
return std::move(s.s);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -325,7 +325,7 @@ void writeString(std::string_view data, Sink & sink)
|
|||
}
|
||||
|
||||
|
||||
Sink & operator << (Sink & sink, const string & s)
|
||||
Sink & operator << (Sink & sink, std::string_view s)
|
||||
{
|
||||
writeString(s, sink);
|
||||
return sink;
|
||||
|
|
@ -391,7 +391,7 @@ size_t readString(char * buf, size_t max, Source & source)
|
|||
}
|
||||
|
||||
|
||||
string readString(Source & source, size_t max)
|
||||
std::string readString(Source & source, size_t max)
|
||||
{
|
||||
auto len = readNum<size_t>(source);
|
||||
if (len > max) throw SerialisationError("string is too long");
|
||||
|
|
@ -401,7 +401,7 @@ string readString(Source & source, size_t max)
|
|||
return res;
|
||||
}
|
||||
|
||||
Source & operator >> (Source & in, string & s)
|
||||
Source & operator >> (Source & in, std::string & s)
|
||||
{
|
||||
s = readString(in);
|
||||
return in;
|
||||
|
|
@ -450,11 +450,11 @@ Error readError(Source & source)
|
|||
void StringSink::operator () (std::string_view data)
|
||||
{
|
||||
static bool warned = false;
|
||||
if (!warned && s->size() > threshold) {
|
||||
if (!warned && s.size() > threshold) {
|
||||
warnLargeDump();
|
||||
warned = true;
|
||||
}
|
||||
s->append(data);
|
||||
s.append(data);
|
||||
}
|
||||
|
||||
size_t ChainSource::read(char * data, size_t len)
|
||||
|
|
|
|||
|
|
@ -154,12 +154,13 @@ private:
|
|||
/* A sink that writes data to a string. */
|
||||
struct StringSink : Sink
|
||||
{
|
||||
ref<std::string> s;
|
||||
StringSink() : s(make_ref<std::string>()) { };
|
||||
explicit StringSink(const size_t reservedSize) : s(make_ref<std::string>()) {
|
||||
s->reserve(reservedSize);
|
||||
std::string s;
|
||||
StringSink() { }
|
||||
explicit StringSink(const size_t reservedSize)
|
||||
{
|
||||
s.reserve(reservedSize);
|
||||
};
|
||||
StringSink(ref<std::string> s) : s(s) { };
|
||||
StringSink(std::string && s) : s(std::move(s)) { };
|
||||
void operator () (std::string_view data) override;
|
||||
};
|
||||
|
||||
|
|
@ -167,9 +168,9 @@ struct StringSink : Sink
|
|||
/* A source that reads data from a string. */
|
||||
struct StringSource : Source
|
||||
{
|
||||
const string & s;
|
||||
std::string_view s;
|
||||
size_t pos;
|
||||
StringSource(const string & _s) : s(_s), pos(0) { }
|
||||
StringSource(std::string_view s) : s(s), pos(0) { }
|
||||
size_t read(char * data, size_t len) override;
|
||||
};
|
||||
|
||||
|
|
@ -317,10 +318,10 @@ inline Sink & operator << (Sink & sink, uint64_t n)
|
|||
return sink;
|
||||
}
|
||||
|
||||
Sink & operator << (Sink & sink, const string & s);
|
||||
Sink & operator << (Sink & in, const Error & ex);
|
||||
Sink & operator << (Sink & sink, std::string_view s);
|
||||
Sink & operator << (Sink & sink, const Strings & s);
|
||||
Sink & operator << (Sink & sink, const StringSet & s);
|
||||
Sink & operator << (Sink & in, const Error & ex);
|
||||
|
||||
|
||||
MakeError(SerialisationError, Error);
|
||||
|
|
@ -363,10 +364,10 @@ inline uint64_t readLongLong(Source & source)
|
|||
|
||||
void readPadding(size_t len, Source & source);
|
||||
size_t readString(char * buf, size_t max, Source & source);
|
||||
string readString(Source & source, size_t max = std::numeric_limits<size_t>::max());
|
||||
std::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, std::string & s);
|
||||
|
||||
template<typename T>
|
||||
Source & operator >> (Source & in, T & n)
|
||||
|
|
|
|||
|
|
@ -93,9 +93,16 @@ static void extract_archive(TarArchive & archive, const Path & destDir)
|
|||
else
|
||||
archive.check(r);
|
||||
|
||||
archive_entry_set_pathname(entry,
|
||||
archive_entry_copy_pathname(entry,
|
||||
(destDir + "/" + name).c_str());
|
||||
|
||||
// Patch hardlink path
|
||||
const char *original_hardlink = archive_entry_hardlink(entry);
|
||||
if (original_hardlink) {
|
||||
archive_entry_copy_hardlink(entry,
|
||||
(destDir + "/" + original_hardlink).c_str());
|
||||
}
|
||||
|
||||
archive.check(archive_read_extract(archive.archive, entry, flags));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@ namespace nix {
|
|||
}
|
||||
|
||||
TEST(compress, noneMethodDoesNothingToTheInput) {
|
||||
ref<std::string> o = compress("none", "this-is-a-test");
|
||||
auto o = compress("none", "this-is-a-test");
|
||||
|
||||
ASSERT_EQ(*o, "this-is-a-test");
|
||||
ASSERT_EQ(o, "this-is-a-test");
|
||||
}
|
||||
|
||||
TEST(decompress, decompressNoneCompressed) {
|
||||
auto method = "none";
|
||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||
ref<std::string> o = decompress(method, str);
|
||||
auto o = decompress(method, str);
|
||||
|
||||
ASSERT_EQ(*o, str);
|
||||
ASSERT_EQ(o, str);
|
||||
}
|
||||
|
||||
TEST(decompress, decompressEmptyCompressed) {
|
||||
|
|
@ -30,33 +30,33 @@ namespace nix {
|
|||
// (Content-Encoding == "").
|
||||
auto method = "";
|
||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||
ref<std::string> o = decompress(method, str);
|
||||
auto o = decompress(method, str);
|
||||
|
||||
ASSERT_EQ(*o, str);
|
||||
ASSERT_EQ(o, str);
|
||||
}
|
||||
|
||||
TEST(decompress, decompressXzCompressed) {
|
||||
auto method = "xz";
|
||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||
ref<std::string> o = decompress(method, *compress(method, str));
|
||||
auto o = decompress(method, compress(method, str));
|
||||
|
||||
ASSERT_EQ(*o, str);
|
||||
ASSERT_EQ(o, str);
|
||||
}
|
||||
|
||||
TEST(decompress, decompressBzip2Compressed) {
|
||||
auto method = "bzip2";
|
||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||
ref<std::string> o = decompress(method, *compress(method, str));
|
||||
auto o = decompress(method, compress(method, str));
|
||||
|
||||
ASSERT_EQ(*o, str);
|
||||
ASSERT_EQ(o, str);
|
||||
}
|
||||
|
||||
TEST(decompress, decompressBrCompressed) {
|
||||
auto method = "br";
|
||||
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
|
||||
ref<std::string> o = decompress(method, *compress(method, str));
|
||||
auto o = decompress(method, compress(method, str));
|
||||
|
||||
ASSERT_EQ(*o, str);
|
||||
ASSERT_EQ(o, str);
|
||||
}
|
||||
|
||||
TEST(decompress, decompressInvalidInputThrowsCompressionError) {
|
||||
|
|
@ -77,7 +77,7 @@ namespace nix {
|
|||
(*sink)(inputString);
|
||||
sink->finish();
|
||||
|
||||
ASSERT_STREQ((*strSink.s).c_str(), inputString);
|
||||
ASSERT_STREQ(strSink.s.c_str(), inputString);
|
||||
}
|
||||
|
||||
TEST(makeCompressionSink, compressAndDecompress) {
|
||||
|
|
@ -90,7 +90,7 @@ namespace nix {
|
|||
sink->finish();
|
||||
decompressionSink->finish();
|
||||
|
||||
ASSERT_STREQ((*strSink.s).c_str(), inputString);
|
||||
ASSERT_STREQ(strSink.s.c_str(), inputString);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ namespace nix {
|
|||
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
||||
setting.assign("value");
|
||||
|
||||
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","value":"value"}})#");
|
||||
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","documentDefault":true,"value":"value"}})#");
|
||||
}
|
||||
|
||||
TEST(Config, setSettingAlias) {
|
||||
|
|
|
|||
68
src/libutil/tests/fmt.cc
Normal file
68
src/libutil/tests/fmt.cc
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include "fmt.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
/* ----------- tests for fmt.hh -------------------------------------------------*/
|
||||
|
||||
TEST(hiliteMatches, noHighlight) {
|
||||
ASSERT_STREQ(hiliteMatches("Hello, world!", std::vector<std::smatch>(), "(", ")").c_str(), "Hello, world!");
|
||||
}
|
||||
|
||||
TEST(hiliteMatches, simpleHighlight) {
|
||||
std::string str = "Hello, world!";
|
||||
std::regex re = std::regex("world");
|
||||
auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
|
||||
ASSERT_STREQ(
|
||||
hiliteMatches(str, matches, "(", ")").c_str(),
|
||||
"Hello, (world)!"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(hiliteMatches, multipleMatches) {
|
||||
std::string str = "Hello, world, world, world, world, world, world, Hello!";
|
||||
std::regex re = std::regex("world");
|
||||
auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
|
||||
ASSERT_STREQ(
|
||||
hiliteMatches(str, matches, "(", ")").c_str(),
|
||||
"Hello, (world), (world), (world), (world), (world), (world), Hello!"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(hiliteMatches, overlappingMatches) {
|
||||
std::string str = "world, Hello, world, Hello, world, Hello, world, Hello, world!";
|
||||
std::regex re = std::regex("Hello, world");
|
||||
std::regex re2 = std::regex("world, Hello");
|
||||
auto v = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
|
||||
for(auto it = std::sregex_iterator(str.begin(), str.end(), re2); it != std::sregex_iterator(); ++it) {
|
||||
v.push_back(*it);
|
||||
}
|
||||
ASSERT_STREQ(
|
||||
hiliteMatches(str, v, "(", ")").c_str(),
|
||||
"(world, Hello, world, Hello, world, Hello, world, Hello, world)!"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(hiliteMatches, complexOverlappingMatches) {
|
||||
std::string str = "legacyPackages.x86_64-linux.git-crypt";
|
||||
std::vector regexes = {
|
||||
std::regex("t-cry"),
|
||||
std::regex("ux\\.git-cry"),
|
||||
std::regex("git-c"),
|
||||
std::regex("pt"),
|
||||
};
|
||||
std::vector<std::smatch> matches;
|
||||
for(auto regex : regexes)
|
||||
{
|
||||
for(auto it = std::sregex_iterator(str.begin(), str.end(), regex); it != std::sregex_iterator(); ++it) {
|
||||
matches.push_back(*it);
|
||||
}
|
||||
}
|
||||
ASSERT_STREQ(
|
||||
hiliteMatches(str, matches, "(", ")").c_str(),
|
||||
"legacyPackages.x86_64-lin(ux.git-crypt)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
#include <limits.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* ----------- tests for util.hh ------------------------------------------------*/
|
||||
|
|
@ -282,6 +284,17 @@ namespace nix {
|
|||
ASSERT_EQ(decoded, s);
|
||||
}
|
||||
|
||||
TEST(base64Encode, encodeAndDecodeNonPrintable) {
|
||||
char s[256];
|
||||
std::iota(std::rbegin(s), std::rend(s), 0);
|
||||
|
||||
auto encoded = base64Encode(s);
|
||||
auto decoded = base64Decode(encoded);
|
||||
|
||||
EXPECT_EQ(decoded.length(), 255);
|
||||
ASSERT_EQ(decoded, s);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* base64Decode
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
|
@ -294,6 +307,10 @@ namespace nix {
|
|||
ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum");
|
||||
}
|
||||
|
||||
TEST(base64Decode, decodeThrowsOnInvalidChar) {
|
||||
ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* toLower
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ namespace nix {
|
|||
|
||||
/* ----------- tests for url.hh --------------------------------------------------*/
|
||||
|
||||
string print_map(std::map<string, string> m) {
|
||||
std::map<string, string>::iterator it;
|
||||
string s = "{ ";
|
||||
std::string print_map(std::map<std::string, std::string> m) {
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
std::string s = "{ ";
|
||||
for (it = m.begin(); it != m.end(); ++it) {
|
||||
s += "{ ";
|
||||
s += it->first;
|
||||
|
|
@ -262,21 +262,21 @@ namespace nix {
|
|||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(percentDecode, decodesUrlEncodedString) {
|
||||
string s = "==@==";
|
||||
string d = percentDecode("%3D%3D%40%3D%3D");
|
||||
std::string s = "==@==";
|
||||
std::string d = percentDecode("%3D%3D%40%3D%3D");
|
||||
ASSERT_EQ(d, s);
|
||||
}
|
||||
|
||||
TEST(percentDecode, multipleDecodesAreIdempotent) {
|
||||
string once = percentDecode("%3D%3D%40%3D%3D");
|
||||
string twice = percentDecode(once);
|
||||
std::string once = percentDecode("%3D%3D%40%3D%3D");
|
||||
std::string twice = percentDecode(once);
|
||||
|
||||
ASSERT_EQ(once, twice);
|
||||
}
|
||||
|
||||
TEST(percentDecode, trailingPercent) {
|
||||
string s = "==@==%";
|
||||
string d = percentDecode("%3D%3D%40%3D%3D%25");
|
||||
std::string s = "==@==%";
|
||||
std::string d = percentDecode("%3D%3D%40%3D%3D%25");
|
||||
|
||||
ASSERT_EQ(d, s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
#include "thread-pool.hh"
|
||||
#include "affinity.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
ThreadPool::ThreadPool(size_t _maxThreads)
|
||||
: maxThreads(_maxThreads)
|
||||
{
|
||||
restoreAffinity(); // FIXME
|
||||
|
||||
if (!maxThreads) {
|
||||
maxThreads = std::thread::hardware_concurrency();
|
||||
if (!maxThreads) maxThreads = 1;
|
||||
|
|
|
|||
|
|
@ -6,26 +6,23 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace nix {
|
||||
|
||||
using std::list;
|
||||
using std::set;
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
typedef list<string> Strings;
|
||||
typedef set<string> StringSet;
|
||||
typedef std::map<string, string> StringMap;
|
||||
typedef std::list<std::string> Strings;
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string, std::string> StringMap;
|
||||
typedef std::map<std::string, std::string> StringPairs;
|
||||
|
||||
/* Paths are just strings. */
|
||||
typedef std::string Path;
|
||||
typedef std::string_view PathView;
|
||||
typedef std::list<Path> Paths;
|
||||
typedef std::set<Path> PathSet;
|
||||
|
||||
typedef string Path;
|
||||
typedef list<Path> Paths;
|
||||
typedef set<Path> PathSet;
|
||||
|
||||
typedef vector<std::pair<string, string>> Headers;
|
||||
typedef std::vector<std::pair<std::string, std::string>> Headers;
|
||||
|
||||
/* Helper class to run code at startup. */
|
||||
template<typename T>
|
||||
|
|
@ -46,4 +43,63 @@ struct Explicit {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/* This wants to be a little bit like rust's Cow type.
|
||||
Some parts of the evaluator benefit greatly from being able to reuse
|
||||
existing allocations for strings, but have to be able to also use
|
||||
newly allocated storage for values.
|
||||
|
||||
We do not define implicit conversions, even with ref qualifiers,
|
||||
since those can easily become ambiguous to the reader and can degrade
|
||||
into copying behaviour we want to avoid. */
|
||||
class BackedStringView {
|
||||
private:
|
||||
std::variant<std::string, std::string_view> data;
|
||||
|
||||
/* Needed to introduce a temporary since operator-> must return
|
||||
a pointer. Without this we'd need to store the view object
|
||||
even when we already own a string. */
|
||||
class Ptr {
|
||||
private:
|
||||
std::string_view view;
|
||||
public:
|
||||
Ptr(std::string_view view): view(view) {}
|
||||
const std::string_view * operator->() const { return &view; }
|
||||
};
|
||||
|
||||
public:
|
||||
BackedStringView(std::string && s): data(std::move(s)) {}
|
||||
BackedStringView(std::string_view sv): data(sv) {}
|
||||
template<size_t N>
|
||||
BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
|
||||
|
||||
BackedStringView(const BackedStringView &) = delete;
|
||||
BackedStringView & operator=(const BackedStringView &) = delete;
|
||||
|
||||
/* We only want move operations defined since the sole purpose of
|
||||
this type is to avoid copies. */
|
||||
BackedStringView(BackedStringView && other) = default;
|
||||
BackedStringView & operator=(BackedStringView && other) = default;
|
||||
|
||||
bool isOwned() const
|
||||
{
|
||||
return std::holds_alternative<std::string>(data);
|
||||
}
|
||||
|
||||
std::string toOwned() &&
|
||||
{
|
||||
return isOwned()
|
||||
? std::move(std::get<std::string>(data))
|
||||
: std::string(std::get<std::string_view>(data));
|
||||
}
|
||||
|
||||
std::string_view operator*() const
|
||||
{
|
||||
return isOwned()
|
||||
? std::get<std::string>(data)
|
||||
: std::get<std::string_view>(data);
|
||||
}
|
||||
Ptr operator->() const { return Ptr(**this); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "util.hh"
|
||||
#include "affinity.hh"
|
||||
#include "sync.hh"
|
||||
#include "finally.hh"
|
||||
#include "serialise.hh"
|
||||
|
|
@ -82,7 +81,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
|
|||
}
|
||||
|
||||
|
||||
Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
|
||||
Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||
{
|
||||
if (path[0] != '/') {
|
||||
if (!dir) {
|
||||
|
|
@ -96,28 +95,28 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
|
|||
if (!getcwd(buf, sizeof(buf)))
|
||||
#endif
|
||||
throw SysError("cannot get cwd");
|
||||
dir = buf;
|
||||
path = concatStrings(buf, "/", path);
|
||||
#ifdef __GNU__
|
||||
free(buf);
|
||||
#endif
|
||||
}
|
||||
path = *dir + "/" + path;
|
||||
} else
|
||||
path = concatStrings(*dir, "/", path);
|
||||
}
|
||||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
||||
|
||||
Path canonPath(const Path & path, bool resolveSymlinks)
|
||||
Path canonPath(PathView path, bool resolveSymlinks)
|
||||
{
|
||||
assert(path != "");
|
||||
|
||||
string s;
|
||||
std::string s;
|
||||
s.reserve(256);
|
||||
|
||||
if (path[0] != '/')
|
||||
throw Error("not an absolute path: '%1%'", path);
|
||||
|
||||
string::const_iterator i = path.begin(), end = path.end();
|
||||
string temp;
|
||||
std::string temp;
|
||||
|
||||
/* Count the number of times we follow a symlink and stop at some
|
||||
arbitrary (but high) limit to prevent infinite loops. */
|
||||
|
|
@ -126,33 +125,37 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
|||
while (1) {
|
||||
|
||||
/* Skip slashes. */
|
||||
while (i != end && *i == '/') i++;
|
||||
if (i == end) break;
|
||||
while (!path.empty() && path[0] == '/') path.remove_prefix(1);
|
||||
if (path.empty()) break;
|
||||
|
||||
/* Ignore `.'. */
|
||||
if (*i == '.' && (i + 1 == end || i[1] == '/'))
|
||||
i++;
|
||||
if (path == "." || path.substr(0, 2) == "./")
|
||||
path.remove_prefix(1);
|
||||
|
||||
/* If `..', delete the last component. */
|
||||
else if (*i == '.' && i + 1 < end && i[1] == '.' &&
|
||||
(i + 2 == end || i[2] == '/'))
|
||||
else if (path == ".." || path.substr(0, 3) == "../")
|
||||
{
|
||||
if (!s.empty()) s.erase(s.rfind('/'));
|
||||
i += 2;
|
||||
path.remove_prefix(2);
|
||||
}
|
||||
|
||||
/* Normal component; copy it. */
|
||||
else {
|
||||
s += '/';
|
||||
while (i != end && *i != '/') s += *i++;
|
||||
if (const auto slash = path.find('/'); slash == std::string::npos) {
|
||||
s += path;
|
||||
path = {};
|
||||
} else {
|
||||
s += path.substr(0, slash);
|
||||
path = path.substr(slash);
|
||||
}
|
||||
|
||||
/* If s points to a symlink, resolve it and continue from there */
|
||||
if (resolveSymlinks && isLink(s)) {
|
||||
if (++followCount >= maxFollow)
|
||||
throw Error("infinite symlink recursion in path '%1%'", path);
|
||||
temp = readLink(s) + string(i, end);
|
||||
i = temp.begin();
|
||||
end = temp.end();
|
||||
temp = concatStrings(readLink(s), path);
|
||||
path = temp;
|
||||
if (!temp.empty() && temp[0] == '/') {
|
||||
s.clear(); /* restart for symlinks pointing to absolute path */
|
||||
} else {
|
||||
|
|
@ -165,14 +168,14 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
|||
}
|
||||
}
|
||||
|
||||
return s.empty() ? "/" : s;
|
||||
return s.empty() ? "/" : std::move(s);
|
||||
}
|
||||
|
||||
|
||||
Path dirOf(const Path & path)
|
||||
Path dirOf(const PathView path)
|
||||
{
|
||||
Path::size_type pos = path.rfind('/');
|
||||
if (pos == string::npos)
|
||||
if (pos == std::string::npos)
|
||||
return ".";
|
||||
return pos == 0 ? "/" : Path(path, 0, pos);
|
||||
}
|
||||
|
|
@ -188,7 +191,7 @@ std::string_view baseNameOf(std::string_view path)
|
|||
last -= 1;
|
||||
|
||||
auto pos = path.rfind('/', last);
|
||||
if (pos == string::npos)
|
||||
if (pos == std::string::npos)
|
||||
pos = 0;
|
||||
else
|
||||
pos += 1;
|
||||
|
|
@ -197,16 +200,16 @@ std::string_view baseNameOf(std::string_view path)
|
|||
}
|
||||
|
||||
|
||||
bool isInDir(const Path & path, const Path & dir)
|
||||
bool isInDir(std::string_view path, std::string_view dir)
|
||||
{
|
||||
return path[0] == '/'
|
||||
&& string(path, 0, dir.size()) == dir
|
||||
return path.substr(0, 1) == "/"
|
||||
&& path.substr(0, dir.size()) == dir
|
||||
&& path.size() >= dir.size() + 2
|
||||
&& path[dir.size()] == '/';
|
||||
}
|
||||
|
||||
|
||||
bool isDirOrInDir(const Path & path, const Path & dir)
|
||||
bool isDirOrInDir(std::string_view path, std::string_view dir)
|
||||
{
|
||||
return path == dir || isInDir(path, dir);
|
||||
}
|
||||
|
|
@ -246,7 +249,7 @@ Path readLink(const Path & path)
|
|||
else
|
||||
throw SysError("reading symbolic link '%1%'", path);
|
||||
else if (rlSize < bufSize)
|
||||
return string(buf.data(), rlSize);
|
||||
return std::string(buf.data(), rlSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +269,7 @@ DirEntries readDirectory(DIR *dir, const Path & path)
|
|||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) { /* sic */
|
||||
checkInterrupt();
|
||||
string name = dirent->d_name;
|
||||
std::string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
entries.emplace_back(name, dirent->d_ino,
|
||||
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
|
||||
|
|
@ -300,7 +303,7 @@ unsigned char getFileType(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
string readFile(int fd)
|
||||
std::string readFile(int fd)
|
||||
{
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1)
|
||||
|
|
@ -310,7 +313,7 @@ string readFile(int fd)
|
|||
}
|
||||
|
||||
|
||||
string readFile(const Path & path)
|
||||
std::string readFile(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (!fd)
|
||||
|
|
@ -363,9 +366,9 @@ void writeFile(const Path & path, Source & source, mode_t mode)
|
|||
}
|
||||
}
|
||||
|
||||
string readLine(int fd)
|
||||
std::string readLine(int fd)
|
||||
{
|
||||
string s;
|
||||
std::string s;
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
char ch;
|
||||
|
|
@ -384,7 +387,7 @@ string readLine(int fd)
|
|||
}
|
||||
|
||||
|
||||
void writeLine(int fd, string s)
|
||||
void writeLine(int fd, std::string s)
|
||||
{
|
||||
s += '\n';
|
||||
writeFull(fd, s);
|
||||
|
|
@ -395,7 +398,7 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
|
|||
{
|
||||
checkInterrupt();
|
||||
|
||||
string name(baseNameOf(path));
|
||||
std::string name(baseNameOf(path));
|
||||
|
||||
struct stat st;
|
||||
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
|
|
@ -512,6 +515,7 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
|||
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
|
||||
if (!fd)
|
||||
throw SysError("creating temporary file '%s'", tmpl);
|
||||
closeOnExec(fd.get());
|
||||
return {std::move(fd), tmpl};
|
||||
}
|
||||
|
||||
|
|
@ -562,8 +566,8 @@ Path getConfigDir()
|
|||
std::vector<Path> getConfigDirs()
|
||||
{
|
||||
Path configHome = getConfigDir();
|
||||
string configDirs = getEnv("XDG_CONFIG_DIRS").value_or("");
|
||||
std::vector<Path> result = tokenizeString<std::vector<string>>(configDirs, ":");
|
||||
auto configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg");
|
||||
std::vector<Path> result = tokenizeString<std::vector<std::string>>(configDirs, ":");
|
||||
result.insert(result.begin(), configHome);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -666,11 +670,13 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts)
|
|||
}
|
||||
|
||||
|
||||
string drainFD(int fd, bool block, const size_t reserveSize)
|
||||
std::string drainFD(int fd, bool block, const size_t reserveSize)
|
||||
{
|
||||
StringSink sink(reserveSize);
|
||||
// the parser needs two extra bytes to append terminating characters, other users will
|
||||
// not care very much about the extra memory.
|
||||
StringSink sink(reserveSize + 2);
|
||||
drainFD(fd, sink, block);
|
||||
return std::move(*sink.s);
|
||||
return std::move(sink.s);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -713,7 +719,7 @@ void drainFD(int fd, Sink & sink, bool block)
|
|||
|
||||
AutoDelete::AutoDelete() : del{false} {}
|
||||
|
||||
AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
|
||||
AutoDelete::AutoDelete(const std::string & p, bool recursive) : path(p)
|
||||
{
|
||||
del = true;
|
||||
this->recursive = recursive;
|
||||
|
|
@ -903,7 +909,7 @@ int Pid::wait()
|
|||
return status;
|
||||
}
|
||||
if (errno != EINTR)
|
||||
throw SysError("cannot get child exit status");
|
||||
throw SysError("cannot get exit status of PID %d", pid);
|
||||
checkInterrupt();
|
||||
}
|
||||
}
|
||||
|
|
@ -939,9 +945,6 @@ void killUser(uid_t uid)
|
|||
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;
|
||||
|
||||
Pid pid = startProcess([&]() {
|
||||
|
||||
if (setuid(uid) == -1)
|
||||
|
|
@ -964,7 +967,7 @@ void killUser(uid_t uid)
|
|||
}
|
||||
|
||||
_exit(0);
|
||||
}, options);
|
||||
});
|
||||
|
||||
int status = pid.wait();
|
||||
if (status != 0)
|
||||
|
|
@ -1006,7 +1009,6 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
|||
if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
||||
throw SysError("setting death signal");
|
||||
#endif
|
||||
restoreAffinity();
|
||||
fun();
|
||||
} catch (std::exception & e) {
|
||||
try {
|
||||
|
|
@ -1034,7 +1036,7 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss)
|
|||
return res;
|
||||
}
|
||||
|
||||
string runProgram(Path program, bool searchPath, const Strings & args,
|
||||
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||
const std::optional<std::string> & input)
|
||||
{
|
||||
auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input});
|
||||
|
|
@ -1059,7 +1061,7 @@ std::pair<int, std::string> runProgram(RunOptions && options)
|
|||
status = e.status;
|
||||
}
|
||||
|
||||
return {status, std::move(*sink.s)};
|
||||
return {status, std::move(sink.s)};
|
||||
}
|
||||
|
||||
void runProgram2(const RunOptions & options)
|
||||
|
|
@ -1085,8 +1087,7 @@ void runProgram2(const RunOptions & options)
|
|||
// 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;
|
||||
processOptions.allowVfork = !options.environment;
|
||||
|
||||
/* Fork. */
|
||||
Pid pid = startProcess([&]() {
|
||||
|
|
@ -1173,7 +1174,7 @@ void runProgram2(const RunOptions & options)
|
|||
}
|
||||
|
||||
|
||||
void closeMostFDs(const set<int> & exceptions)
|
||||
void closeMostFDs(const std::set<int> & exceptions)
|
||||
{
|
||||
#if __linux__
|
||||
try {
|
||||
|
|
@ -1209,7 +1210,7 @@ void closeOnExec(int fd)
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
bool _isInterrupted = false;
|
||||
std::atomic<bool> _isInterrupted = false;
|
||||
|
||||
static thread_local bool interruptThrown = false;
|
||||
thread_local std::function<bool()> interruptCheck;
|
||||
|
|
@ -1234,45 +1235,45 @@ void _interrupted()
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template<class C> C tokenizeString(std::string_view s, const string & separators)
|
||||
template<class C> C tokenizeString(std::string_view s, std::string_view 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);
|
||||
auto pos = s.find_first_not_of(separators, 0);
|
||||
while (pos != std::string::npos) {
|
||||
auto end = s.find_first_of(separators, pos + 1);
|
||||
if (end == std::string::npos) end = s.size();
|
||||
result.insert(result.end(), std::string(s, pos, end - pos));
|
||||
pos = s.find_first_not_of(separators, end);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template Strings tokenizeString(std::string_view s, const string & separators);
|
||||
template StringSet tokenizeString(std::string_view s, const string & separators);
|
||||
template vector<string> tokenizeString(std::string_view s, const string & separators);
|
||||
template Strings tokenizeString(std::string_view s, std::string_view separators);
|
||||
template StringSet tokenizeString(std::string_view s, std::string_view separators);
|
||||
template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
|
||||
|
||||
string chomp(std::string_view s)
|
||||
std::string chomp(std::string_view s)
|
||||
{
|
||||
size_t i = s.find_last_not_of(" \n\r\t");
|
||||
return i == string::npos ? "" : string(s, 0, i + 1);
|
||||
return i == std::string_view::npos ? "" : std::string(s, 0, i + 1);
|
||||
}
|
||||
|
||||
|
||||
string trim(const string & s, const string & whitespace)
|
||||
std::string trim(std::string_view s, std::string_view whitespace)
|
||||
{
|
||||
auto i = s.find_first_not_of(whitespace);
|
||||
if (i == string::npos) return "";
|
||||
if (i == std::string_view::npos) return "";
|
||||
auto j = s.find_last_not_of(whitespace);
|
||||
return string(s, i, j == string::npos ? j : j - i + 1);
|
||||
return std::string(s, i, j == std::string::npos ? j : j - i + 1);
|
||||
}
|
||||
|
||||
|
||||
string replaceStrings(std::string_view s,
|
||||
const std::string & from, const std::string & to)
|
||||
std::string replaceStrings(
|
||||
std::string res,
|
||||
std::string_view from,
|
||||
std::string_view to)
|
||||
{
|
||||
string res(s);
|
||||
if (from.empty()) return res;
|
||||
size_t pos = 0;
|
||||
while ((pos = res.find(from, pos)) != std::string::npos) {
|
||||
|
|
@ -1283,20 +1284,19 @@ string replaceStrings(std::string_view s,
|
|||
}
|
||||
|
||||
|
||||
std::string rewriteStrings(const std::string & _s, const StringMap & rewrites)
|
||||
std::string rewriteStrings(std::string s, const StringMap & rewrites)
|
||||
{
|
||||
auto s = _s;
|
||||
for (auto & i : rewrites) {
|
||||
if (i.first == i.second) continue;
|
||||
size_t j = 0;
|
||||
while ((j = s.find(i.first, j)) != string::npos)
|
||||
while ((j = s.find(i.first, j)) != std::string::npos)
|
||||
s.replace(j, i.first.size(), i.second);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
string statusToString(int status)
|
||||
std::string statusToString(int status)
|
||||
{
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
if (WIFEXITED(status))
|
||||
|
|
@ -1344,9 +1344,11 @@ std::string toLower(const std::string & s)
|
|||
}
|
||||
|
||||
|
||||
std::string shellEscape(const std::string & s)
|
||||
std::string shellEscape(const std::string_view s)
|
||||
{
|
||||
std::string r = "'";
|
||||
std::string r;
|
||||
r.reserve(s.size() + 2);
|
||||
r += "'";
|
||||
for (auto & i : s)
|
||||
if (i == '\'') r += "'\\''"; else r += i;
|
||||
r += '\'';
|
||||
|
|
@ -1356,11 +1358,15 @@ std::string shellEscape(const std::string & s)
|
|||
|
||||
void ignoreException()
|
||||
{
|
||||
/* Make sure no exceptions leave this function.
|
||||
printError() also throws when remote is closed. */
|
||||
try {
|
||||
throw;
|
||||
} catch (std::exception & e) {
|
||||
printError("error (ignored): %1%", e.what());
|
||||
}
|
||||
try {
|
||||
throw;
|
||||
} catch (std::exception & e) {
|
||||
printError("error (ignored): %1%", e.what());
|
||||
}
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
bool shouldANSI()
|
||||
|
|
@ -1440,12 +1446,11 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in
|
|||
}
|
||||
|
||||
|
||||
static char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
static std::array<char, 256> base64DecodeChars;
|
||||
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
string base64Encode(std::string_view s)
|
||||
std::string base64Encode(std::string_view s)
|
||||
{
|
||||
string res;
|
||||
std::string res;
|
||||
int data = 0, nbits = 0;
|
||||
|
||||
for (char c : s) {
|
||||
|
|
@ -1464,16 +1469,19 @@ string base64Encode(std::string_view s)
|
|||
}
|
||||
|
||||
|
||||
string base64Decode(std::string_view s)
|
||||
std::string base64Decode(std::string_view s)
|
||||
{
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [](){
|
||||
base64DecodeChars = { (char)-1 };
|
||||
constexpr char npos = -1;
|
||||
constexpr std::array<char, 256> base64DecodeChars = [&]() {
|
||||
std::array<char, 256> result{};
|
||||
for (auto& c : result)
|
||||
c = npos;
|
||||
for (int i = 0; i < 64; i++)
|
||||
base64DecodeChars[(int) base64Chars[i]] = i;
|
||||
});
|
||||
result[base64Chars[i]] = i;
|
||||
return result;
|
||||
}();
|
||||
|
||||
string res;
|
||||
std::string res;
|
||||
unsigned int d = 0, bits = 0;
|
||||
|
||||
for (char c : s) {
|
||||
|
|
@ -1481,7 +1489,7 @@ string base64Decode(std::string_view s)
|
|||
if (c == '\n') continue;
|
||||
|
||||
char digit = base64DecodeChars[(unsigned char) c];
|
||||
if (digit == -1)
|
||||
if (digit == npos)
|
||||
throw Error("invalid character in Base64 string: '%c'", c);
|
||||
|
||||
bits += 6;
|
||||
|
|
@ -1557,7 +1565,22 @@ std::pair<unsigned short, unsigned short> getWindowSize()
|
|||
}
|
||||
|
||||
|
||||
static Sync<std::list<std::function<void()>>> _interruptCallbacks;
|
||||
/* We keep track of interrupt callbacks using integer tokens, so we can iterate
|
||||
safely without having to lock the data structure while executing arbitrary
|
||||
functions.
|
||||
*/
|
||||
struct InterruptCallbacks {
|
||||
typedef int64_t Token;
|
||||
|
||||
/* We use unique tokens so that we can't accidentally delete the wrong
|
||||
handler because of an erroneous double delete. */
|
||||
Token nextToken = 0;
|
||||
|
||||
/* Used as a list, see InterruptCallbacks comment. */
|
||||
std::map<Token, std::function<void()>> callbacks;
|
||||
};
|
||||
|
||||
static Sync<InterruptCallbacks> _interruptCallbacks;
|
||||
|
||||
static void signalHandlerThread(sigset_t set)
|
||||
{
|
||||
|
|
@ -1579,8 +1602,19 @@ void triggerInterrupt()
|
|||
_isInterrupted = true;
|
||||
|
||||
{
|
||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||
for (auto & callback : *interruptCallbacks) {
|
||||
InterruptCallbacks::Token i = 0;
|
||||
while (true) {
|
||||
std::function<void()> callback;
|
||||
{
|
||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||
auto lb = interruptCallbacks->callbacks.lower_bound(i);
|
||||
if (lb == interruptCallbacks->callbacks.end())
|
||||
break;
|
||||
|
||||
callback = lb->second;
|
||||
i = lb->first + 1;
|
||||
}
|
||||
|
||||
try {
|
||||
callback();
|
||||
} catch (...) {
|
||||
|
|
@ -1634,11 +1668,47 @@ void setStackSize(size_t stackSize)
|
|||
#endif
|
||||
}
|
||||
|
||||
void restoreProcessContext()
|
||||
static AutoCloseFD fdSavedMountNamespace;
|
||||
|
||||
void saveMountNamespace()
|
||||
{
|
||||
#if __linux__
|
||||
static std::once_flag done;
|
||||
std::call_once(done, []() {
|
||||
AutoCloseFD fd = open("/proc/self/ns/mnt", O_RDONLY);
|
||||
if (!fd)
|
||||
throw SysError("saving parent mount namespace");
|
||||
fdSavedMountNamespace = std::move(fd);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void restoreMountNamespace()
|
||||
{
|
||||
#if __linux__
|
||||
try {
|
||||
if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1)
|
||||
throw SysError("restoring parent mount namespace");
|
||||
} catch (Error & e) {
|
||||
debug(e.msg());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void unshareFilesystem()
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (unshare(CLONE_FS) != 0 && errno != EPERM)
|
||||
throw SysError("unsharing filesystem state in download thread");
|
||||
#endif
|
||||
}
|
||||
|
||||
void restoreProcessContext(bool restoreMounts)
|
||||
{
|
||||
restoreSignals();
|
||||
|
||||
restoreAffinity();
|
||||
if (restoreMounts) {
|
||||
restoreMountNamespace();
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
if (savedStackSize) {
|
||||
|
|
@ -1654,27 +1724,28 @@ void restoreProcessContext()
|
|||
/* RAII helper to automatically deregister a callback. */
|
||||
struct InterruptCallbackImpl : InterruptCallback
|
||||
{
|
||||
std::list<std::function<void()>>::iterator it;
|
||||
InterruptCallbacks::Token token;
|
||||
~InterruptCallbackImpl() override
|
||||
{
|
||||
_interruptCallbacks.lock()->erase(it);
|
||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||
interruptCallbacks->callbacks.erase(token);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback)
|
||||
{
|
||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||
interruptCallbacks->push_back(callback);
|
||||
auto token = interruptCallbacks->nextToken++;
|
||||
interruptCallbacks->callbacks.emplace(token, callback);
|
||||
|
||||
auto res = std::make_unique<InterruptCallbackImpl>();
|
||||
res->it = interruptCallbacks->end();
|
||||
res->it--;
|
||||
res->token = token;
|
||||
|
||||
return std::unique_ptr<InterruptCallback>(res.release());
|
||||
}
|
||||
|
||||
|
||||
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
||||
AutoCloseFD createUnixDomainSocket()
|
||||
{
|
||||
AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM
|
||||
#ifdef SOCK_CLOEXEC
|
||||
|
|
@ -1683,19 +1754,16 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
|||
, 0);
|
||||
if (!fdSocket)
|
||||
throw SysError("cannot create Unix domain socket");
|
||||
|
||||
closeOnExec(fdSocket.get());
|
||||
return fdSocket;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
if (path.size() + 1 >= sizeof(addr.sun_path))
|
||||
throw Error("socket path '%1%' is too long", path);
|
||||
strcpy(addr.sun_path, path.c_str());
|
||||
|
||||
unlink(path.c_str());
|
||||
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
||||
{
|
||||
auto fdSocket = nix::createUnixDomainSocket();
|
||||
|
||||
if (bind(fdSocket.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||
throw SysError("cannot bind to socket '%1%'", path);
|
||||
bind(fdSocket.get(), path);
|
||||
|
||||
if (chmod(path.c_str(), mode) == -1)
|
||||
throw SysError("changing permissions on '%1%'", path);
|
||||
|
|
@ -1707,7 +1775,67 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
|||
}
|
||||
|
||||
|
||||
string showBytes(uint64_t bytes)
|
||||
void bind(int fd, const std::string & path)
|
||||
{
|
||||
unlink(path.c_str());
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||
Pid pid = startProcess([&]() {
|
||||
Path dir = dirOf(path);
|
||||
if (chdir(dir.c_str()) == -1)
|
||||
throw SysError("chdir to '%s' failed", dir);
|
||||
std::string base(baseNameOf(path));
|
||||
if (base.size() + 1 >= sizeof(addr.sun_path))
|
||||
throw Error("socket path '%s' is too long", base);
|
||||
memcpy(addr.sun_path, base.c_str(), base.size() + 1);
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||
throw SysError("cannot bind to socket '%s'", path);
|
||||
_exit(0);
|
||||
});
|
||||
int status = pid.wait();
|
||||
if (status != 0)
|
||||
throw Error("cannot bind to socket '%s'", path);
|
||||
} else {
|
||||
memcpy(addr.sun_path, path.c_str(), path.size() + 1);
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||
throw SysError("cannot bind to socket '%s'", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void connect(int fd, const std::string & path)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||
Pid pid = startProcess([&]() {
|
||||
Path dir = dirOf(path);
|
||||
if (chdir(dir.c_str()) == -1)
|
||||
throw SysError("chdir to '%s' failed", dir);
|
||||
std::string base(baseNameOf(path));
|
||||
if (base.size() + 1 >= sizeof(addr.sun_path))
|
||||
throw Error("socket path '%s' is too long", base);
|
||||
memcpy(addr.sun_path, base.c_str(), base.size() + 1);
|
||||
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||
throw SysError("cannot connect to socket at '%s'", path);
|
||||
_exit(0);
|
||||
});
|
||||
int status = pid.wait();
|
||||
if (status != 0)
|
||||
throw Error("cannot connect to socket at '%s'", path);
|
||||
} else {
|
||||
memcpy(addr.sun_path, path.c_str(), path.size() + 1);
|
||||
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||
throw SysError("cannot connect to socket at '%s'", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string showBytes(uint64_t bytes)
|
||||
{
|
||||
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
|
||||
}
|
||||
|
|
@ -1716,8 +1844,10 @@ string showBytes(uint64_t bytes)
|
|||
// FIXME: move to libstore/build
|
||||
void commonChildInit(Pipe & logPipe)
|
||||
{
|
||||
const static string pathNullDevice = "/dev/null";
|
||||
restoreProcessContext();
|
||||
logger = makeSimpleLogger();
|
||||
|
||||
const static std::string pathNullDevice = "/dev/null";
|
||||
restoreProcessContext(false);
|
||||
|
||||
/* Put the child in a separate session (and thus a separate
|
||||
process group) so that it has no controlling terminal (meaning
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
|
@ -46,30 +49,32 @@ void clearEnv();
|
|||
specified directory, or the current directory otherwise. The path
|
||||
is also canonicalised. */
|
||||
Path absPath(Path path,
|
||||
std::optional<Path> dir = {},
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false);
|
||||
|
||||
/* Canonicalise a path by removing all `.' or `..' components and
|
||||
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(PathView 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 `/'
|
||||
is returned.*/
|
||||
Path dirOf(const Path & path);
|
||||
Path dirOf(const PathView path);
|
||||
|
||||
/* Return the base name of the given canonical path, i.e., everything
|
||||
following the final `/' (trailing slashes are removed). */
|
||||
std::string_view baseNameOf(std::string_view path);
|
||||
|
||||
/* Check whether 'path' is a descendant of 'dir'. */
|
||||
bool isInDir(const Path & path, const Path & dir);
|
||||
/* Check whether 'path' is a descendant of 'dir'. Both paths must be
|
||||
canonicalized. */
|
||||
bool isInDir(std::string_view path, std::string_view dir);
|
||||
|
||||
/* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */
|
||||
bool isDirOrInDir(const Path & path, const Path & dir);
|
||||
/* Check whether 'path' is equal to 'dir' or a descendant of
|
||||
'dir'. Both paths must be canonicalized. */
|
||||
bool isDirOrInDir(std::string_view path, std::string_view dir);
|
||||
|
||||
/* Get status of `path'. */
|
||||
struct stat lstat(const Path & path);
|
||||
|
|
@ -87,22 +92,22 @@ bool isLink(const Path & path);
|
|||
removed. */
|
||||
struct DirEntry
|
||||
{
|
||||
string name;
|
||||
std::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) { }
|
||||
DirEntry(std::string name, ino_t ino, unsigned char type)
|
||||
: name(std::move(name)), ino(ino), type(type) { }
|
||||
};
|
||||
|
||||
typedef vector<DirEntry> DirEntries;
|
||||
typedef std::vector<DirEntry> DirEntries;
|
||||
|
||||
DirEntries readDirectory(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);
|
||||
std::string readFile(int fd);
|
||||
std::string readFile(const Path & path);
|
||||
void readFile(const Path & path, Sink & sink);
|
||||
|
||||
/* Write a string to a file. */
|
||||
|
|
@ -111,10 +116,10 @@ void writeFile(const Path & path, std::string_view s, 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);
|
||||
std::string readLine(int fd);
|
||||
|
||||
/* Write a line to a file descriptor. */
|
||||
void writeLine(int fd, string s);
|
||||
void writeLine(int fd, std::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
|
||||
|
|
@ -143,6 +148,9 @@ 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);
|
||||
inline Paths createDirs(PathView path) {
|
||||
return createDirs(Path(path));
|
||||
}
|
||||
|
||||
/* Create a symlink. */
|
||||
void createSymlink(const Path & target, const Path & link,
|
||||
|
|
@ -162,7 +170,7 @@ MakeError(EndOfFile, Error);
|
|||
|
||||
|
||||
/* Read a file descriptor until EOF occurs. */
|
||||
string drainFD(int fd, bool block = true, const size_t reserveSize=0);
|
||||
std::string drainFD(int fd, bool block = true, const size_t reserveSize=0);
|
||||
|
||||
void drainFD(int fd, Sink & sink, bool block = true);
|
||||
|
||||
|
|
@ -182,6 +190,7 @@ public:
|
|||
void cancel();
|
||||
void reset(const Path & p, bool recursive = true);
|
||||
operator Path() const { return path; }
|
||||
operator PathView() const { return path; }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -259,10 +268,10 @@ void killUser(uid_t uid);
|
|||
pid to the caller. */
|
||||
struct ProcessOptions
|
||||
{
|
||||
string errorPrefix = "error: ";
|
||||
std::string errorPrefix = "";
|
||||
bool dieWithParent = true;
|
||||
bool runExitHandlers = false;
|
||||
bool allowVfork = true;
|
||||
bool allowVfork = false;
|
||||
};
|
||||
|
||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||
|
|
@ -270,7 +279,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P
|
|||
|
||||
/* Run a program and return its stdout in a string (i.e., like the
|
||||
shell backtick operator). */
|
||||
string runProgram(Path program, bool searchPath = false,
|
||||
std::string runProgram(Path program, bool searchPath = false,
|
||||
const Strings & args = Strings(),
|
||||
const std::optional<std::string> & input = {});
|
||||
|
||||
|
|
@ -299,8 +308,21 @@ void setStackSize(size_t stackSize);
|
|||
|
||||
|
||||
/* Restore the original inherited Unix process context (such as signal
|
||||
masks, stack size, CPU affinity). */
|
||||
void restoreProcessContext();
|
||||
masks, stack size). */
|
||||
void restoreProcessContext(bool restoreMounts = true);
|
||||
|
||||
/* Save the current mount namespace. Ignored if called more than
|
||||
once. */
|
||||
void saveMountNamespace();
|
||||
|
||||
/* Restore the mount namespace saved by saveMountNamespace(). Ignored
|
||||
if saveMountNamespace() was never called. */
|
||||
void restoreMountNamespace();
|
||||
|
||||
/* Cause this thread to not share any FS attributes with the main
|
||||
thread, because this causes setns() in restoreMountNamespace() to
|
||||
fail. */
|
||||
void unshareFilesystem();
|
||||
|
||||
|
||||
class ExecError : public Error
|
||||
|
|
@ -321,7 +343,7 @@ 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 std::set<int> & exceptions);
|
||||
|
||||
/* Set the close-on-exec flag for the given file descriptor. */
|
||||
void closeOnExec(int fd);
|
||||
|
|
@ -329,7 +351,7 @@ void closeOnExec(int fd);
|
|||
|
||||
/* User interruption. */
|
||||
|
||||
extern bool _isInterrupted;
|
||||
extern std::atomic<bool> _isInterrupted;
|
||||
|
||||
extern thread_local std::function<bool()> interruptCheck;
|
||||
|
||||
|
|
@ -350,15 +372,19 @@ MakeError(FormatError, Error);
|
|||
|
||||
|
||||
/* String tokenizer. */
|
||||
template<class C> C tokenizeString(std::string_view s, const string & separators = " \t\n\r");
|
||||
template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
||||
|
||||
|
||||
/* Concatenate the given strings with a separator between the
|
||||
elements. */
|
||||
template<class C>
|
||||
string concatStringsSep(const string & sep, const C & ss)
|
||||
std::string concatStringsSep(const std::string_view sep, const C & ss)
|
||||
{
|
||||
string s;
|
||||
size_t size = 0;
|
||||
// need a cast to string_view since this is also called with Symbols
|
||||
for (const auto & s : ss) size += sep.size() + std::string_view(s).size();
|
||||
std::string s;
|
||||
s.reserve(size);
|
||||
for (auto & i : ss) {
|
||||
if (s.size() != 0) s += sep;
|
||||
s += i;
|
||||
|
|
@ -366,6 +392,14 @@ string concatStringsSep(const string & sep, const C & ss)
|
|||
return s;
|
||||
}
|
||||
|
||||
template<class ... Parts>
|
||||
auto concatStrings(Parts && ... parts)
|
||||
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string>
|
||||
{
|
||||
std::string_view views[sizeof...(parts)] = { parts... };
|
||||
return concatStringsSep({}, views);
|
||||
}
|
||||
|
||||
|
||||
/* Add quotes around a collection of strings. */
|
||||
template<class C> Strings quoteStrings(const C & c)
|
||||
|
|
@ -379,45 +413,47 @@ template<class C> Strings quoteStrings(const C & c)
|
|||
|
||||
/* Remove trailing whitespace from a string. FIXME: return
|
||||
std::string_view. */
|
||||
string chomp(std::string_view s);
|
||||
std::string chomp(std::string_view s);
|
||||
|
||||
|
||||
/* Remove whitespace from the start and end of a string. */
|
||||
string trim(const string & s, const string & whitespace = " \n\r\t");
|
||||
std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t");
|
||||
|
||||
|
||||
/* Replace all occurrences of a string inside another string. */
|
||||
string replaceStrings(std::string_view s,
|
||||
const std::string & from, const std::string & to);
|
||||
std::string replaceStrings(
|
||||
std::string s,
|
||||
std::string_view from,
|
||||
std::string_view to);
|
||||
|
||||
|
||||
std::string rewriteStrings(const std::string & s, const StringMap & rewrites);
|
||||
std::string rewriteStrings(std::string s, const StringMap & rewrites);
|
||||
|
||||
|
||||
/* Convert the exit status of a child as returned by wait() into an
|
||||
error string. */
|
||||
string statusToString(int status);
|
||||
std::string statusToString(int status);
|
||||
|
||||
bool statusOk(int status);
|
||||
|
||||
|
||||
/* Parse a string into an integer. */
|
||||
template<class N>
|
||||
std::optional<N> string2Int(const std::string & s)
|
||||
std::optional<N> string2Int(const std::string_view s)
|
||||
{
|
||||
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
||||
return std::nullopt;
|
||||
std::istringstream str(s);
|
||||
N n;
|
||||
str >> n;
|
||||
if (str && str.get() == EOF) return n;
|
||||
return std::nullopt;
|
||||
try {
|
||||
return boost::lexical_cast<N>(s.data(), s.size());
|
||||
} catch (const boost::bad_lexical_cast &) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
||||
'T' denoting a binary unit prefix. */
|
||||
template<class N>
|
||||
N string2IntWithUnitPrefix(std::string s)
|
||||
N string2IntWithUnitPrefix(std::string_view s)
|
||||
{
|
||||
N multiplier = 1;
|
||||
if (!s.empty()) {
|
||||
|
|
@ -428,7 +464,7 @@ N string2IntWithUnitPrefix(std::string s)
|
|||
else if (u == 'G') multiplier = 1ULL << 30;
|
||||
else if (u == 'T') multiplier = 1ULL << 40;
|
||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||
s.resize(s.size() - 1);
|
||||
s.remove_suffix(1);
|
||||
}
|
||||
}
|
||||
if (auto n = string2Int<N>(s))
|
||||
|
|
@ -438,13 +474,13 @@ N string2IntWithUnitPrefix(std::string s)
|
|||
|
||||
/* Parse a string into a float. */
|
||||
template<class N>
|
||||
std::optional<N> string2Float(const string & s)
|
||||
std::optional<N> string2Float(const std::string_view s)
|
||||
{
|
||||
std::istringstream str(s);
|
||||
N n;
|
||||
str >> n;
|
||||
if (str && str.get() == EOF) return n;
|
||||
return std::nullopt;
|
||||
try {
|
||||
return boost::lexical_cast<N>(s.data(), s.size());
|
||||
} catch (const boost::bad_lexical_cast &) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -461,7 +497,7 @@ 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_view s);
|
||||
|
||||
|
||||
/* Exception handling in destructors: print an error message, then
|
||||
|
|
@ -491,8 +527,8 @@ std::string filterANSIEscapes(const std::string & s,
|
|||
|
||||
|
||||
/* Base64 encoding/decoding. */
|
||||
string base64Encode(std::string_view s);
|
||||
string base64Decode(std::string_view s);
|
||||
std::string base64Encode(std::string_view s);
|
||||
std::string base64Decode(std::string_view s);
|
||||
|
||||
|
||||
/* Remove common leading whitespace from the lines in the string
|
||||
|
|
@ -511,6 +547,29 @@ std::optional<typename T::mapped_type> get(const T & map, const typename T::key_
|
|||
}
|
||||
|
||||
|
||||
/* Remove and return the first item from a container. */
|
||||
template <class T>
|
||||
std::optional<typename T::value_type> remove_begin(T & c)
|
||||
{
|
||||
auto i = c.begin();
|
||||
if (i == c.end()) return {};
|
||||
auto v = std::move(*i);
|
||||
c.erase(i);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/* Remove and return the first item from a container. */
|
||||
template <class T>
|
||||
std::optional<typename T::value_type> pop(T & c)
|
||||
{
|
||||
if (c.empty()) return {};
|
||||
auto v = std::move(c.front());
|
||||
c.pop();
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class Callback;
|
||||
|
||||
|
|
@ -571,9 +630,18 @@ extern PathFilter defaultPathFilter;
|
|||
/* Common initialisation performed in child processes. */
|
||||
void commonChildInit(Pipe & logPipe);
|
||||
|
||||
/* Create a Unix domain socket. */
|
||||
AutoCloseFD createUnixDomainSocket();
|
||||
|
||||
/* Create a Unix domain socket in listen mode. */
|
||||
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
|
||||
|
||||
/* Bind a Unix domain socket to a path. */
|
||||
void bind(int fd, const std::string & path);
|
||||
|
||||
/* Connect to a Unix domain socket. */
|
||||
void connect(int fd, const std::string & path);
|
||||
|
||||
|
||||
// A Rust/Python-like enumerate() iterator adapter.
|
||||
// Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17.
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@ void XMLWriter::close()
|
|||
void XMLWriter::indent_(size_t depth)
|
||||
{
|
||||
if (!indent) return;
|
||||
output << string(depth * 2, ' ');
|
||||
output << std::string(depth * 2, ' ');
|
||||
}
|
||||
|
||||
|
||||
void XMLWriter::openElement(const string & name,
|
||||
void XMLWriter::openElement(
|
||||
std::string_view name,
|
||||
const XMLAttrs & attrs)
|
||||
{
|
||||
assert(!closed);
|
||||
|
|
@ -44,7 +45,7 @@ void XMLWriter::openElement(const string & name,
|
|||
writeAttrs(attrs);
|
||||
output << ">";
|
||||
if (indent) output << std::endl;
|
||||
pendingElems.push_back(name);
|
||||
pendingElems.push_back(std::string(name));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -59,7 +60,8 @@ void XMLWriter::closeElement()
|
|||
}
|
||||
|
||||
|
||||
void XMLWriter::writeEmptyElement(const string & name,
|
||||
void XMLWriter::writeEmptyElement(
|
||||
std::string_view name,
|
||||
const XMLAttrs & attrs)
|
||||
{
|
||||
assert(!closed);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::list;
|
||||
|
||||
|
||||
typedef map<string, string> XMLAttrs;
|
||||
typedef std::map<std::string, std::string> XMLAttrs;
|
||||
|
||||
|
||||
class XMLWriter
|
||||
|
|
@ -25,7 +21,7 @@ private:
|
|||
bool indent;
|
||||
bool closed;
|
||||
|
||||
list<string> pendingElems;
|
||||
std::list<std::string> pendingElems;
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -34,11 +30,11 @@ public:
|
|||
|
||||
void close();
|
||||
|
||||
void openElement(const string & name,
|
||||
void openElement(std::string_view name,
|
||||
const XMLAttrs & attrs = XMLAttrs());
|
||||
void closeElement();
|
||||
|
||||
void writeEmptyElement(const string & name,
|
||||
void writeEmptyElement(std::string_view name,
|
||||
const XMLAttrs & attrs = XMLAttrs());
|
||||
|
||||
private:
|
||||
|
|
@ -53,7 +49,7 @@ class XMLOpenElement
|
|||
private:
|
||||
XMLWriter & writer;
|
||||
public:
|
||||
XMLOpenElement(XMLWriter & writer, const string & name,
|
||||
XMLOpenElement(XMLWriter & writer, std::string_view name,
|
||||
const XMLAttrs & attrs = XMLAttrs())
|
||||
: writer(writer)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue