diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/archive.cc | 24 | ||||
-rw-r--r-- | src/libutil/archive.hh | 10 | ||||
-rw-r--r-- | src/libutil/args.cc | 5 | ||||
-rw-r--r-- | src/libutil/args.hh | 5 | ||||
-rw-r--r-- | src/libutil/compression.cc | 113 | ||||
-rw-r--r-- | src/libutil/config.cc | 76 | ||||
-rw-r--r-- | src/libutil/config.hh | 11 | ||||
-rw-r--r-- | src/libutil/error.hh | 1 | ||||
-rw-r--r-- | src/libutil/hash.cc | 20 | ||||
-rw-r--r-- | src/libutil/hash.hh | 2 | ||||
-rw-r--r-- | src/libutil/logging.cc | 2 | ||||
-rw-r--r-- | src/libutil/logging.hh | 15 | ||||
-rw-r--r-- | src/libutil/serialise.cc | 113 | ||||
-rw-r--r-- | src/libutil/serialise.hh | 108 | ||||
-rw-r--r-- | src/libutil/tarfile.cc | 2 | ||||
-rw-r--r-- | src/libutil/tests/config.cc | 4 | ||||
-rw-r--r-- | src/libutil/tests/tests.cc | 11 | ||||
-rw-r--r-- | src/libutil/types.hh | 13 | ||||
-rw-r--r-- | src/libutil/url-parts.hh | 3 | ||||
-rw-r--r-- | src/libutil/util.cc | 92 | ||||
-rw-r--r-- | src/libutil/util.hh | 11 |
21 files changed, 398 insertions, 243 deletions
diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index f1479329f..ed0eb2fb5 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -27,7 +27,7 @@ struct ArchiveSettings : Config #endif "use-case-hack", "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; - Setting<bool> preallocateContents{this, true, "preallocate-contents", + Setting<bool> preallocateContents{this, false, "preallocate-contents", "Whether to preallocate files when writing objects with known size."}; }; @@ -50,14 +50,14 @@ static void dumpContents(const Path & path, size_t size, AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); if (!fd) throw SysError("opening file '%1%'", path); - std::vector<unsigned char> buf(65536); + std::vector<char> buf(65536); size_t left = size; while (left > 0) { auto n = std::min(left, buf.size()); readFull(fd.get(), buf.data(), n); left -= n; - sink(buf.data(), n); + sink({buf.data(), n}); } writePadding(size, sink); @@ -155,14 +155,14 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path) sink.preallocateContents(size); uint64_t left = size; - std::vector<unsigned char> buf(65536); + std::vector<char> buf(65536); while (left) { checkInterrupt(); auto n = buf.size(); if ((uint64_t)n > left) n = left; source(buf.data(), n); - sink.receiveContents(buf.data(), n); + sink.receiveContents({buf.data(), n}); left -= n; } @@ -300,21 +300,21 @@ struct RestoreSink : ParseSink Path dstPath; AutoCloseFD fd; - void createDirectory(const Path & path) + void createDirectory(const Path & path) override { Path p = dstPath + path; if (mkdir(p.c_str(), 0777) == -1) throw SysError("creating directory '%1%'", p); }; - void createRegularFile(const Path & path) + void createRegularFile(const Path & path) override { Path p = dstPath + path; fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); if (!fd) throw SysError("creating file '%1%'", p); } - void isExecutable() + void isExecutable() override { struct stat st; if (fstat(fd.get(), &st) == -1) @@ -323,7 +323,7 @@ struct RestoreSink : ParseSink throw SysError("fchmod"); } - void preallocateContents(uint64_t len) + void preallocateContents(uint64_t len) override { if (!archiveSettings.preallocateContents) return; @@ -341,12 +341,12 @@ struct RestoreSink : ParseSink #endif } - void receiveContents(unsigned char * data, size_t len) + void receiveContents(std::string_view data) override { - writeFull(fd.get(), data, len); + writeFull(fd.get(), data); } - void createSymlink(const Path & path, const string & target) + void createSymlink(const Path & path, const string & target) override { Path p = dstPath + path; nix::createSymlink(target, p); diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index 5665732d2..9e9e11b1a 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -58,7 +58,7 @@ struct ParseSink virtual void createRegularFile(const Path & path) { }; virtual void isExecutable() { }; virtual void preallocateContents(uint64_t size) { }; - virtual void receiveContents(unsigned char * data, size_t len) { }; + virtual void receiveContents(std::string_view data) { }; virtual void createSymlink(const Path & path, const string & target) { }; }; @@ -72,17 +72,17 @@ struct RetrieveRegularNARSink : ParseSink RetrieveRegularNARSink(Sink & sink) : sink(sink) { } - void createDirectory(const Path & path) + void createDirectory(const Path & path) override { regular = false; } - void receiveContents(unsigned char * data, size_t len) + void receiveContents(std::string_view data) override { - sink(data, len); + sink(data); } - void createSymlink(const Path & path, const string & target) + void createSymlink(const Path & path, const string & target) override { regular = false; } diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 8bd9c8aeb..61f9503ec 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -86,6 +86,7 @@ void Args::parseCmdline(const Strings & _cmdline) throw UsageError("unrecognised flag '%1%'", arg); } else { + pos = rewriteArgs(cmdline, pos); pendingArgs.push_back(*pos++); if (processArgs(pendingArgs, false)) pendingArgs.clear(); @@ -390,10 +391,6 @@ MultiCommand::MultiCommand(const Commands & commands) .optional = true, .handler = {[=](std::string s) { assert(!command); - if (auto alias = get(deprecatedAliases, s)) { - warn("'%s' is a deprecated alias for '%s'", s, *alias); - s = *alias; - } if (auto prefix = needsCompletion(s)) { for (auto & [name, command] : commands) if (hasPrefix(name, *prefix)) diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 26f1bc11b..8069fd70f 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -115,6 +115,9 @@ protected: virtual bool processArgs(const Strings & args, bool finish); + virtual Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos) + { return pos; } + std::set<std::string> hiddenCategories; public: @@ -257,8 +260,6 @@ public: std::map<Command::Category, std::string> categories; - std::map<std::string, std::string> deprecatedAliases; - // Selected command, if any. std::optional<std::pair<std::string, ref<Command>>> command; diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index a117ddc72..986ba2976 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -22,18 +22,17 @@ struct ChunkedCompressionSink : CompressionSink { uint8_t outbuf[32 * 1024]; - void write(const unsigned char * data, size_t len) override + void write(std::string_view data) override { const size_t CHUNK_SIZE = sizeof(outbuf) << 2; - while (len) { - size_t n = std::min(CHUNK_SIZE, len); - writeInternal(data, n); - data += n; - len -= n; + while (!data.empty()) { + size_t n = std::min(CHUNK_SIZE, data.size()); + writeInternal(data); + data.remove_prefix(n); } } - virtual void writeInternal(const unsigned char * data, size_t len) = 0; + virtual void writeInternal(std::string_view data) = 0; }; struct NoneSink : CompressionSink @@ -41,7 +40,7 @@ struct NoneSink : CompressionSink Sink & nextSink; NoneSink(Sink & nextSink) : nextSink(nextSink) { } void finish() override { flush(); } - void write(const unsigned char * data, size_t len) override { nextSink(data, len); } + void write(std::string_view data) override { nextSink(data); } }; struct GzipDecompressionSink : CompressionSink @@ -75,28 +74,28 @@ struct GzipDecompressionSink : CompressionSink void finish() override { CompressionSink::flush(); - write(nullptr, 0); + write({}); } - void write(const unsigned char * data, size_t len) override + void write(std::string_view data) override { - assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); + assert(data.size() <= std::numeric_limits<decltype(strm.avail_in)>::max()); - strm.next_in = (Bytef *) data; - strm.avail_in = len; + strm.next_in = (Bytef *) data.data(); + strm.avail_in = data.size(); - while (!finished && (!data || strm.avail_in)) { + while (!finished && (!data.data() || strm.avail_in)) { checkInterrupt(); int ret = inflate(&strm,Z_SYNC_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) throw CompressionError("error while decompressing gzip file: %d (%d, %d)", - zError(ret), len, strm.avail_in); + zError(ret), data.size(), strm.avail_in); finished = ret == Z_STREAM_END; if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + nextSink({(char *) outbuf, sizeof(outbuf) - strm.avail_out}); strm.next_out = (Bytef *) outbuf; strm.avail_out = sizeof(outbuf); } @@ -130,25 +129,25 @@ struct XzDecompressionSink : CompressionSink void finish() override { CompressionSink::flush(); - write(nullptr, 0); + write({}); } - void write(const unsigned char * data, size_t len) override + void write(std::string_view data) override { - strm.next_in = data; - strm.avail_in = len; + strm.next_in = (const unsigned char *) data.data(); + strm.avail_in = data.size(); - while (!finished && (!data || strm.avail_in)) { + while (!finished && (!data.data() || strm.avail_in)) { checkInterrupt(); - lzma_ret ret = lzma_code(&strm, data ? LZMA_RUN : LZMA_FINISH); + lzma_ret ret = lzma_code(&strm, data.data() ? LZMA_RUN : LZMA_FINISH); if (ret != LZMA_OK && ret != LZMA_STREAM_END) throw CompressionError("error %d while decompressing xz file", ret); finished = ret == LZMA_STREAM_END; if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + nextSink({(char *) outbuf, sizeof(outbuf) - strm.avail_out}); strm.next_out = outbuf; strm.avail_out = sizeof(outbuf); } @@ -181,15 +180,15 @@ struct BzipDecompressionSink : ChunkedCompressionSink void finish() override { flush(); - write(nullptr, 0); + write({}); } - void writeInternal(const unsigned char * data, size_t len) override + void writeInternal(std::string_view data) override { - assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); + assert(data.size() <= std::numeric_limits<decltype(strm.avail_in)>::max()); - strm.next_in = (char *) data; - strm.avail_in = len; + strm.next_in = (char *) data.data(); + strm.avail_in = data.size(); while (strm.avail_in) { checkInterrupt(); @@ -201,7 +200,7 @@ struct BzipDecompressionSink : ChunkedCompressionSink finished = ret == BZ_STREAM_END; if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + nextSink({(char *) outbuf, sizeof(outbuf) - strm.avail_out}); strm.next_out = (char *) outbuf; strm.avail_out = sizeof(outbuf); } @@ -230,17 +229,17 @@ struct BrotliDecompressionSink : ChunkedCompressionSink void finish() override { flush(); - writeInternal(nullptr, 0); + writeInternal({}); } - void writeInternal(const unsigned char * data, size_t len) override + void writeInternal(std::string_view data) override { - const uint8_t * next_in = data; - size_t avail_in = len; + auto next_in = (const uint8_t *) data.data(); + size_t avail_in = data.size(); uint8_t * next_out = outbuf; size_t avail_out = sizeof(outbuf); - while (!finished && (!data || avail_in)) { + while (!finished && (!data.data() || avail_in)) { checkInterrupt(); if (!BrotliDecoderDecompressStream(state, @@ -250,7 +249,7 @@ struct BrotliDecompressionSink : ChunkedCompressionSink throw CompressionError("error while decompressing brotli file"); if (avail_out < sizeof(outbuf) || avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - avail_out); + nextSink({(char *) outbuf, sizeof(outbuf) - avail_out}); next_out = outbuf; avail_out = sizeof(outbuf); } @@ -338,25 +337,25 @@ struct XzCompressionSink : CompressionSink void finish() override { CompressionSink::flush(); - write(nullptr, 0); + write({}); } - void write(const unsigned char * data, size_t len) override + void write(std::string_view data) override { - strm.next_in = data; - strm.avail_in = len; + strm.next_in = (const unsigned char *) data.data(); + strm.avail_in = data.size(); - while (!finished && (!data || strm.avail_in)) { + while (!finished && (!data.data() || strm.avail_in)) { checkInterrupt(); - lzma_ret ret = lzma_code(&strm, data ? LZMA_RUN : LZMA_FINISH); + lzma_ret ret = lzma_code(&strm, data.data() ? LZMA_RUN : LZMA_FINISH); if (ret != LZMA_OK && ret != LZMA_STREAM_END) throw CompressionError("error %d while compressing xz file", ret); finished = ret == LZMA_STREAM_END; if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + nextSink({(const char *) outbuf, sizeof(outbuf) - strm.avail_out}); strm.next_out = outbuf; strm.avail_out = sizeof(outbuf); } @@ -389,27 +388,27 @@ struct BzipCompressionSink : ChunkedCompressionSink void finish() override { flush(); - writeInternal(nullptr, 0); + writeInternal({}); } - void writeInternal(const unsigned char * data, size_t len) override + void writeInternal(std::string_view data) override { - assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max()); + assert(data.size() <= std::numeric_limits<decltype(strm.avail_in)>::max()); - strm.next_in = (char *) data; - strm.avail_in = len; + strm.next_in = (char *) data.data(); + strm.avail_in = data.size(); - while (!finished && (!data || strm.avail_in)) { + while (!finished && (!data.data() || strm.avail_in)) { checkInterrupt(); - int ret = BZ2_bzCompress(&strm, data ? BZ_RUN : BZ_FINISH); + int ret = BZ2_bzCompress(&strm, data.data() ? BZ_RUN : BZ_FINISH); if (ret != BZ_RUN_OK && ret != BZ_FINISH_OK && ret != BZ_STREAM_END) throw CompressionError("error %d while compressing bzip2 file", ret); finished = ret == BZ_STREAM_END; if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + nextSink({(const char *) outbuf, sizeof(outbuf) - strm.avail_out}); strm.next_out = (char *) outbuf; strm.avail_out = sizeof(outbuf); } @@ -439,28 +438,28 @@ struct BrotliCompressionSink : ChunkedCompressionSink void finish() override { flush(); - writeInternal(nullptr, 0); + writeInternal({}); } - void writeInternal(const unsigned char * data, size_t len) override + void writeInternal(std::string_view data) override { - const uint8_t * next_in = data; - size_t avail_in = len; + auto next_in = (const uint8_t *) data.data(); + size_t avail_in = data.size(); uint8_t * next_out = outbuf; size_t avail_out = sizeof(outbuf); - while (!finished && (!data || avail_in)) { + while (!finished && (!data.data() || avail_in)) { checkInterrupt(); if (!BrotliEncoderCompressStream(state, - data ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, + data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, &avail_in, &next_in, &avail_out, &next_out, nullptr)) throw CompressionError("error while compressing brotli compression"); if (avail_out < sizeof(outbuf) || avail_in == 0) { - nextSink(outbuf, sizeof(outbuf) - avail_out); + nextSink({(const char *) outbuf, sizeof(outbuf) - avail_out}); next_out = outbuf; avail_out = sizeof(outbuf); } diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 521733025..7af3e7883 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -8,9 +8,18 @@ namespace nix { bool Config::set(const std::string & name, const std::string & value) { + bool append = false; auto i = _settings.find(name); - if (i == _settings.end()) return false; - i->second.setting->set(value); + if (i == _settings.end()) { + if (hasPrefix(name, "extra-")) { + i = _settings.find(std::string(name, 6)); + if (i == _settings.end() || !i->second.setting->isAppendable()) + return false; + append = true; + } else + return false; + } + i->second.setting->set(value, append); i->second.setting->overriden = true; return true; } @@ -181,18 +190,33 @@ void AbstractSetting::convertToArg(Args & args, const std::string & category) } template<typename T> +bool BaseSetting<T>::isAppendable() +{ + return false; +} + +template<typename T> void BaseSetting<T>::convertToArg(Args & args, const std::string & category) { args.addFlag({ .longName = name, - .description = description, + .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, .handler = {[=](std::string s) { overriden = true; set(s); }}, }); + + if (isAppendable()) + args.addFlag({ + .longName = "extra-" + name, + .description = fmt("Append to the `%s` setting.", name), + .category = category, + .labels = {"value"}, + .handler = {[=](std::string s) { overriden = true; set(s, true); }}, + }); } -template<> void BaseSetting<std::string>::set(const std::string & str) +template<> void BaseSetting<std::string>::set(const std::string & str, bool append) { value = str; } @@ -203,7 +227,7 @@ template<> std::string BaseSetting<std::string>::to_string() const } template<typename T> -void BaseSetting<T>::set(const std::string & str) +void BaseSetting<T>::set(const std::string & str, bool append) { static_assert(std::is_integral<T>::value, "Integer required."); if (!string2Int(str, value)) @@ -217,7 +241,7 @@ std::string BaseSetting<T>::to_string() const return std::to_string(value); } -template<> void BaseSetting<bool>::set(const std::string & str) +template<> void BaseSetting<bool>::set(const std::string & str, bool append) { if (str == "true" || str == "yes" || str == "1") value = true; @@ -236,21 +260,28 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & { args.addFlag({ .longName = name, - .description = description, + .description = fmt("Enable the `%s` setting.", name), .category = category, .handler = {[=]() { override(true); }} }); args.addFlag({ .longName = "no-" + name, - .description = description, + .description = fmt("Disable the `%s` setting.", name), .category = category, .handler = {[=]() { override(false); }} }); } -template<> void BaseSetting<Strings>::set(const std::string & str) +template<> void BaseSetting<Strings>::set(const std::string & str, bool append) { - value = tokenizeString<Strings>(str); + auto ss = tokenizeString<Strings>(str); + if (!append) value.clear(); + for (auto & s : ss) value.push_back(std::move(s)); +} + +template<> bool BaseSetting<Strings>::isAppendable() +{ + return true; } template<> std::string BaseSetting<Strings>::to_string() const @@ -258,9 +289,16 @@ template<> std::string BaseSetting<Strings>::to_string() const return concatStringsSep(" ", value); } -template<> void BaseSetting<StringSet>::set(const std::string & str) +template<> void BaseSetting<StringSet>::set(const std::string & str, bool append) { - value = tokenizeString<StringSet>(str); + if (!append) value.clear(); + for (auto & s : tokenizeString<StringSet>(str)) + value.insert(s); +} + +template<> bool BaseSetting<StringSet>::isAppendable() +{ + return true; } template<> std::string BaseSetting<StringSet>::to_string() const @@ -268,11 +306,10 @@ template<> std::string BaseSetting<StringSet>::to_string() const return concatStringsSep(" ", value); } -template<> void BaseSetting<StringMap>::set(const std::string & str) +template<> void BaseSetting<StringMap>::set(const std::string & str, bool append) { - auto kvpairs = tokenizeString<Strings>(str); - for (auto & s : kvpairs) - { + if (!append) value.clear(); + for (auto & s : tokenizeString<Strings>(str)) { auto eq = s.find_first_of('='); if (std::string::npos != eq) value.emplace(std::string(s, 0, eq), std::string(s, eq + 1)); @@ -280,6 +317,11 @@ template<> void BaseSetting<StringMap>::set(const std::string & str) } } +template<> bool BaseSetting<StringMap>::isAppendable() +{ + return true; +} + template<> std::string BaseSetting<StringMap>::to_string() const { Strings kvstrs; @@ -300,7 +342,7 @@ template class BaseSetting<Strings>; template class BaseSetting<StringSet>; template class BaseSetting<StringMap>; -void PathSetting::set(const std::string & str) +void PathSetting::set(const std::string & str, bool append) { if (str == "") { if (allowEmpty) diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 1f5f4e7b9..71e31656d 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -202,7 +202,10 @@ protected: assert(created == 123); } - virtual void set(const std::string & value) = 0; + virtual void set(const std::string & value, bool append = false) = 0; + + virtual bool isAppendable() + { return false; } virtual std::string to_string() const = 0; @@ -243,7 +246,9 @@ public: void operator =(const T & v) { assign(v); } virtual void assign(const T & v) { value = v; } - void set(const std::string & str) override; + void set(const std::string & str, bool append = false) override; + + bool isAppendable() override; virtual void override(const T & v) { @@ -305,7 +310,7 @@ public: options->addSetting(this); } - void set(const std::string & str) override; + void set(const std::string & str, bool append = false) override; Path operator +(const char * p) const { return value + p; } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index d1b6d82bb..aa4fadfcc 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -45,6 +45,7 @@ namespace nix { typedef enum { lvlError = 0, lvlWarn, + lvlNotice, lvlInfo, lvlTalkative, lvlChatty, diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 4a94f0dfd..4df8b4ecb 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -192,7 +192,7 @@ Hash Hash::parseAny(std::string_view original, std::optional<HashType> optType) // Either the string or user must provide the type, if they both do they // must agree. if (!optParsedType && !optType) - throw BadHash("hash '%s' does not include a type, nor is the type otherwise known from context.", rest); + throw BadHash("hash '%s' does not include a type, nor is the type otherwise known from context", rest); else if (optParsedType && optType && *optParsedType != *optType) throw BadHash("hash '%s' should have type '%s'", original, printHashType(*optType)); @@ -291,12 +291,12 @@ static void start(HashType ht, Ctx & ctx) static void update(HashType ht, Ctx & ctx, - const unsigned char * bytes, size_t len) + std::string_view data) { - if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len); - else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len); - else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len); - else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len); + if (ht == htMD5) MD5_Update(&ctx.md5, data.data(), data.size()); + else if (ht == htSHA1) SHA1_Update(&ctx.sha1, data.data(), data.size()); + else if (ht == htSHA256) SHA256_Update(&ctx.sha256, data.data(), data.size()); + else if (ht == htSHA512) SHA512_Update(&ctx.sha512, data.data(), data.size()); } @@ -314,7 +314,7 @@ Hash hashString(HashType ht, std::string_view s) Ctx ctx; Hash hash(ht); start(ht, ctx); - update(ht, ctx, (const unsigned char *) s.data(), s.length()); + update(ht, ctx, s); finish(ht, ctx, hash.hash); return hash; } @@ -341,10 +341,10 @@ HashSink::~HashSink() delete ctx; } -void HashSink::write(const unsigned char * data, size_t len) +void HashSink::write(std::string_view data) { - bytes += len; - update(ht, *ctx, data, len); + bytes += data.size(); + update(ht, *ctx, data); } HashResult HashSink::finish() diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index 6d6eb70ca..1b626dd85 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -156,7 +156,7 @@ public: HashSink(HashType ht); HashSink(const HashSink & h); ~HashSink(); - void write(const unsigned char * data, size_t len) override; + void write(std::string_view data) override; HashResult finish() override; HashResult currentHash(); }; diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 8a6752e22..6fd0dacef 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -306,7 +306,7 @@ bool handleJSONLogMessage(const std::string & msg, } catch (std::exception & e) { logError({ - .name = "Json log message", + .name = "JSON log message", .hint = hintfmt("bad log message from builder: %s", e.what()) }); } diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 63cb2b268..96ad69790 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -100,12 +100,15 @@ public: virtual void writeToStdout(std::string_view s); template<typename... Args> - inline void stdout(const std::string & fs, const Args & ... args) + inline void cout(const std::string & fs, const Args & ... args) { boost::format f(fs); formatHelper(f, args...); writeToStdout(f.str()); } + + virtual std::optional<char> ask(std::string_view s) + { return {}; } }; ActivityId getCurActivity(); @@ -175,8 +178,8 @@ extern Verbosity verbosity; /* suppress msgs > this */ lightweight status messages. */ #define logErrorInfo(level, errorInfo...) \ do { \ - if (level <= nix::verbosity) { \ - logger->logEI(level, errorInfo); \ + if ((level) <= nix::verbosity) { \ + logger->logEI((level), errorInfo); \ } \ } while (0) @@ -188,12 +191,14 @@ extern Verbosity verbosity; /* suppress msgs > this */ arguments are evaluated lazily. */ #define printMsg(level, args...) \ do { \ - if (level <= nix::verbosity) { \ - logger->log(level, fmt(args)); \ + auto __lvl = level; \ + if (__lvl <= nix::verbosity) { \ + logger->log(__lvl, fmt(args)); \ } \ } while (0) #define printError(args...) printMsg(lvlError, args) +#define notice(args...) printMsg(lvlNotice, args) #define printInfo(args...) printMsg(lvlInfo, args) #define printTalkative(args...) printMsg(lvlTalkative, args) #define debug(args...) printMsg(lvlDebug, args) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 5c9f6f901..87c1099a1 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -11,23 +11,23 @@ namespace nix { -void BufferedSink::operator () (const unsigned char * data, size_t len) +void BufferedSink::operator () (std::string_view data) { - if (!buffer) buffer = decltype(buffer)(new unsigned char[bufSize]); + if (!buffer) buffer = decltype(buffer)(new char[bufSize]); - while (len) { + while (!data.empty()) { /* Optimisation: bypass the buffer if the data exceeds the buffer size. */ - if (bufPos + len >= bufSize) { + if (bufPos + data.size() >= bufSize) { flush(); - write(data, len); + write(data); break; } /* Otherwise, copy the bytes to the buffer. Flush the buffer when it's full. */ - size_t n = bufPos + len > bufSize ? bufSize - bufPos : len; - memcpy(buffer.get() + bufPos, data, n); - data += n; bufPos += n; len -= n; + size_t n = bufPos + data.size() > bufSize ? bufSize - bufPos : data.size(); + memcpy(buffer.get() + bufPos, data.data(), n); + data.remove_prefix(n); bufPos += n; if (bufPos == bufSize) flush(); } } @@ -38,7 +38,7 @@ void BufferedSink::flush() if (bufPos == 0) return; size_t n = bufPos; bufPos = 0; // don't trigger the assert() in ~BufferedSink() - write(buffer.get(), n); + write({buffer.get(), n}); } @@ -59,9 +59,9 @@ static void warnLargeDump() } -void FdSink::write(const unsigned char * data, size_t len) +void FdSink::write(std::string_view data) { - written += len; + written += data.size(); static bool warned = false; if (warn && !warned) { if (written > threshold) { @@ -70,7 +70,7 @@ void FdSink::write(const unsigned char * data, size_t len) } } try { - writeFull(fd, data, len); + writeFull(fd, data); } catch (SysError & e) { _good = false; throw; @@ -84,7 +84,7 @@ bool FdSink::good() } -void Source::operator () (unsigned char * data, size_t len) +void Source::operator () (char * data, size_t len) { while (len) { size_t n = read(data, len); @@ -96,12 +96,12 @@ void Source::operator () (unsigned char * data, size_t len) void Source::drainInto(Sink & sink) { std::string s; - std::vector<unsigned char> buf(8192); + std::vector<char> buf(8192); while (true) { size_t n; try { n = read(buf.data(), buf.size()); - sink(buf.data(), n); + sink({buf.data(), n}); } catch (EndOfFile &) { break; } @@ -117,9 +117,9 @@ std::string Source::drain() } -size_t BufferedSource::read(unsigned char * data, size_t len) +size_t BufferedSource::read(char * data, size_t len) { - if (!buffer) buffer = decltype(buffer)(new unsigned char[bufSize]); + if (!buffer) buffer = decltype(buffer)(new char[bufSize]); if (!bufPosIn) bufPosIn = readUnbuffered(buffer.get(), bufSize); @@ -138,12 +138,12 @@ bool BufferedSource::hasData() } -size_t FdSource::readUnbuffered(unsigned char * data, size_t len) +size_t FdSource::readUnbuffered(char * data, size_t len) { ssize_t n; do { checkInterrupt(); - n = ::read(fd, (char *) data, len); + n = ::read(fd, data, len); } while (n == -1 && errno == EINTR); if (n == -1) { _good = false; throw SysError("reading from file"); } if (n == 0) { _good = false; throw EndOfFile("unexpected end-of-file"); } @@ -158,10 +158,10 @@ bool FdSource::good() } -size_t StringSource::read(unsigned char * data, size_t len) +size_t StringSource::read(char * data, size_t len) { if (pos == s.size()) throw EndOfFile("end of string reached"); - size_t n = s.copy((char *) data, len, pos); + size_t n = s.copy(data, len, pos); pos += n; return n; } @@ -171,6 +171,39 @@ size_t StringSource::read(unsigned char * data, size_t len) #error Coroutines are broken in this version of Boost! #endif +/* A concrete datatype allow virtual dispatch of stack allocation methods. */ +struct VirtualStackAllocator { + StackAllocator *allocator = StackAllocator::defaultAllocator; + + boost::context::stack_context allocate() { + return allocator->allocate(); + } + + void deallocate(boost::context::stack_context sctx) { + allocator->deallocate(sctx); + } +}; + + +/* This class reifies the default boost coroutine stack allocation strategy with + a virtual interface. */ +class DefaultStackAllocator : public StackAllocator { + boost::coroutines2::default_stack stack; + + boost::context::stack_context allocate() { + return stack.allocate(); + } + + void deallocate(boost::context::stack_context sctx) { + stack.deallocate(sctx); + } +}; + +static DefaultStackAllocator defaultAllocatorSingleton; + +StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton; + + std::unique_ptr<Source> sinkToSource( std::function<void(Sink &)> fun, std::function<void()> eof) @@ -192,13 +225,13 @@ std::unique_ptr<Source> sinkToSource( std::string cur; size_t pos = 0; - size_t read(unsigned char * data, size_t len) override + size_t read(char * data, size_t len) override { if (!coro) - coro = coro_t::pull_type([&](coro_t::push_type & yield) { - LambdaSink sink([&](const unsigned char * data, size_t len) { - if (len) yield(std::string((const char *) data, len)); - }); + coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) { + LambdaSink sink([&](std::string_view data) { + if (!data.empty()) yield(std::string(data)); + }); fun(sink); }); @@ -211,7 +244,7 @@ std::unique_ptr<Source> sinkToSource( } auto n = std::min(cur.size() - pos, len); - memcpy(data, (unsigned char *) cur.data() + pos, n); + memcpy(data, cur.data() + pos, n); pos += n; return n; @@ -225,24 +258,24 @@ std::unique_ptr<Source> sinkToSource( void writePadding(size_t len, Sink & sink) { if (len % 8) { - unsigned char zero[8]; + char zero[8]; memset(zero, 0, sizeof(zero)); - sink(zero, 8 - (len % 8)); + sink({zero, 8 - (len % 8)}); } } -void writeString(const unsigned char * buf, size_t len, Sink & sink) +void writeString(std::string_view data, Sink & sink) { - sink << len; - sink(buf, len); - writePadding(len, sink); + sink << data.size(); + sink(data); + writePadding(data.size(), sink); } Sink & operator << (Sink & sink, const string & s) { - writeString((const unsigned char *) s.data(), s.size(), sink); + writeString(s, sink); return sink; } @@ -288,7 +321,7 @@ Sink & operator << (Sink & sink, const Error & ex) void readPadding(size_t len, Source & source) { if (len % 8) { - unsigned char zero[8]; + char zero[8]; size_t n = 8 - (len % 8); source(zero, n); for (unsigned int i = 0; i < n; i++) @@ -297,7 +330,7 @@ void readPadding(size_t len, Source & source) } -size_t readString(unsigned char * buf, size_t max, Source & source) +size_t readString(char * buf, size_t max, Source & source) { auto len = readNum<size_t>(source); if (len > max) throw SerialisationError("string is too long"); @@ -312,7 +345,7 @@ string readString(Source & source, size_t max) auto len = readNum<size_t>(source); if (len > max) throw SerialisationError("string is too long"); std::string res(len, 0); - source((unsigned char*) res.data(), len); + source(res.data(), len); readPadding(len, source); return res; } @@ -361,17 +394,17 @@ Error readError(Source & source) } -void StringSink::operator () (const unsigned char * data, size_t len) +void StringSink::operator () (std::string_view data) { static bool warned = false; if (!warned && s->size() > threshold) { warnLargeDump(); warned = true; } - s->append((const char *) data, len); + s->append(data); } -size_t ChainSource::read(unsigned char * data, size_t len) +size_t ChainSource::read(char * data, size_t len) { if (useSecond) { return source2.read(data, len); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index d7fe0b81e..5bbbc7ce3 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -5,6 +5,7 @@ #include "types.hh" #include "util.hh" +namespace boost::context { struct stack_context; } namespace nix { @@ -13,19 +14,14 @@ namespace nix { struct Sink { virtual ~Sink() { } - virtual void operator () (const unsigned char * data, size_t len) = 0; + virtual void operator () (std::string_view data) = 0; virtual bool good() { return true; } - - void operator () (const std::string & s) - { - (*this)((const unsigned char *) s.data(), s.size()); - } }; /* Just throws away data. */ struct NullSink : Sink { - void operator () (const unsigned char * data, size_t len) override + void operator () (std::string_view data) override { } }; @@ -34,21 +30,16 @@ struct NullSink : Sink struct BufferedSink : virtual Sink { size_t bufSize, bufPos; - std::unique_ptr<unsigned char[]> buffer; + std::unique_ptr<char[]> buffer; BufferedSink(size_t bufSize = 32 * 1024) : bufSize(bufSize), bufPos(0), buffer(nullptr) { } - void operator () (const unsigned char * data, size_t len) override; - - void operator () (const std::string & s) - { - Sink::operator()(s); - } + void operator () (std::string_view data) override; void flush(); - virtual void write(const unsigned char * data, size_t len) = 0; + virtual void write(std::string_view data) = 0; }; @@ -60,12 +51,12 @@ struct Source /* Store exactly ‘len’ bytes in the buffer pointed to by ‘data’. It blocks until all the requested data is available, or throws an error if it is not going to be available. */ - void operator () (unsigned char * data, size_t len); + void operator () (char * data, size_t len); /* Store up to ‘len’ in the buffer pointed to by ‘data’, and return the number of bytes stored. It blocks until at least one byte is available. */ - virtual size_t read(unsigned char * data, size_t len) = 0; + virtual size_t read(char * data, size_t len) = 0; virtual bool good() { return true; } @@ -80,18 +71,18 @@ struct Source struct BufferedSource : Source { size_t bufSize, bufPosIn, bufPosOut; - std::unique_ptr<unsigned char[]> buffer; + std::unique_ptr<char[]> buffer; BufferedSource(size_t bufSize = 32 * 1024) : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) { } - size_t read(unsigned char * data, size_t len) override; + size_t read(char * data, size_t len) override; bool hasData(); protected: /* Underlying read call, to be overridden. */ - virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0; + virtual size_t readUnbuffered(char * data, size_t len) = 0; }; @@ -118,7 +109,7 @@ struct FdSink : BufferedSink ~FdSink(); - void write(const unsigned char * data, size_t len) override; + void write(std::string_view data) override; bool good() override; @@ -147,7 +138,7 @@ struct FdSource : BufferedSource bool good() override; protected: - size_t readUnbuffered(unsigned char * data, size_t len) override; + size_t readUnbuffered(char * data, size_t len) override; private: bool _good = true; }; @@ -162,7 +153,7 @@ struct StringSink : Sink s->reserve(reservedSize); }; StringSink(ref<std::string> s) : s(s) { }; - void operator () (const unsigned char * data, size_t len) override; + void operator () (std::string_view data) override; }; @@ -172,7 +163,7 @@ struct StringSource : Source const string & s; size_t pos; StringSource(const string & _s) : s(_s), pos(0) { } - size_t read(unsigned char * data, size_t len) override; + size_t read(char * data, size_t len) override; }; @@ -181,10 +172,10 @@ struct TeeSink : Sink { Sink & sink1, & sink2; TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) { } - virtual void operator () (const unsigned char * data, size_t len) + virtual void operator () (std::string_view data) { - sink1(data, len); - sink2(data, len); + sink1(data); + sink2(data); } }; @@ -196,10 +187,10 @@ struct TeeSource : Source Sink & sink; TeeSource(Source & orig, Sink & sink) : orig(orig), sink(sink) { } - size_t read(unsigned char * data, size_t len) + size_t read(char * data, size_t len) { size_t n = orig.read(data, len); - sink(data, n); + sink({data, n}); return n; } }; @@ -211,7 +202,7 @@ struct SizedSource : Source size_t remain; SizedSource(Source & orig, size_t size) : orig(orig), remain(size) { } - size_t read(unsigned char * data, size_t len) + size_t read(char * data, size_t len) { if (this->remain <= 0) { throw EndOfFile("sized: unexpected end-of-file"); @@ -225,7 +216,7 @@ struct SizedSource : Source /* Consume the original source until no remain data is left to consume. */ size_t drainAll() { - std::vector<unsigned char> buf(8192); + std::vector<char> buf(8192); size_t sum = 0; while (this->remain > 0) { size_t n = read(buf.data(), buf.size()); @@ -240,24 +231,24 @@ struct LengthSink : Sink { uint64_t length = 0; - virtual void operator () (const unsigned char * _, size_t len) + void operator () (std::string_view data) override { - length += len; + length += data.size(); } }; /* Convert a function into a sink. */ struct LambdaSink : Sink { - typedef std::function<void(const unsigned char *, size_t)> lambda_t; + typedef std::function<void(std::string_view data)> lambda_t; lambda_t lambda; LambdaSink(const lambda_t & lambda) : lambda(lambda) { } - virtual void operator () (const unsigned char * data, size_t len) + void operator () (std::string_view data) override { - lambda(data, len); + lambda(data); } }; @@ -265,13 +256,13 @@ struct LambdaSink : Sink /* Convert a function into a source. */ struct LambdaSource : Source { - typedef std::function<size_t(unsigned char *, size_t)> lambda_t; + typedef std::function<size_t(char *, size_t)> lambda_t; lambda_t lambda; LambdaSource(const lambda_t & lambda) : lambda(lambda) { } - size_t read(unsigned char * data, size_t len) override + size_t read(char * data, size_t len) override { return lambda(data, len); } @@ -287,7 +278,7 @@ struct ChainSource : Source : source1(s1), source2(s2) { } - size_t read(unsigned char * data, size_t len) override; + size_t read(char * data, size_t len) override; }; @@ -301,7 +292,7 @@ std::unique_ptr<Source> sinkToSource( void writePadding(size_t len, Sink & sink); -void writeString(const unsigned char * buf, size_t len, Sink & sink); +void writeString(std::string_view s, Sink & sink); inline Sink & operator << (Sink & sink, uint64_t n) { @@ -314,7 +305,7 @@ inline Sink & operator << (Sink & sink, uint64_t n) buf[5] = (n >> 40) & 0xff; buf[6] = (n >> 48) & 0xff; buf[7] = (unsigned char) (n >> 56) & 0xff; - sink(buf, sizeof(buf)); + sink({(char *) buf, sizeof(buf)}); return sink; } @@ -331,7 +322,7 @@ template<typename T> T readNum(Source & source) { unsigned char buf[8]; - source(buf, sizeof(buf)); + source((char *) buf, sizeof(buf)); uint64_t n = ((uint64_t) buf[0]) | @@ -363,7 +354,7 @@ inline uint64_t readLongLong(Source & source) void readPadding(size_t len, Source & source); -size_t readString(unsigned char * buf, size_t max, 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()); template<class T> T readStrings(Source & source); @@ -395,9 +386,9 @@ struct StreamToSourceAdapter : Source : istream(istream) { } - size_t read(unsigned char * data, size_t len) override + size_t read(char * data, size_t len) override { - if (!istream->read((char *) data, len)) { + if (!istream->read(data, len)) { if (istream->eof()) { if (istream->gcount() == 0) throw EndOfFile("end of file"); @@ -420,7 +411,7 @@ struct FramedSource : Source { Source & from; bool eof = false; - std::vector<unsigned char> pending; + std::vector<char> pending; size_t pos = 0; FramedSource(Source & from) : from(from) @@ -432,13 +423,13 @@ struct FramedSource : Source while (true) { auto n = readInt(from); if (!n) break; - std::vector<unsigned char> data(n); + std::vector<char> data(n); from(data.data(), n); } } } - size_t read(unsigned char * data, size_t len) override + size_t read(char * data, size_t len) override { if (eof) throw EndOfFile("reached end of FramedSource"); @@ -448,7 +439,7 @@ struct FramedSource : Source eof = true; return 0; } - pending = std::vector<unsigned char>(len); + pending = std::vector<char>(len); pos = 0; from(pending.data(), len); } @@ -483,7 +474,7 @@ struct FramedSink : nix::BufferedSink } } - void write(const unsigned char * data, size_t len) override + void write(std::string_view data) override { /* Don't send more data if the remote has encountered an error. */ @@ -492,10 +483,23 @@ struct FramedSink : nix::BufferedSink ex = nullptr; std::rethrow_exception(ex2); } - to << len; - to(data, len); + to << data.size(); + to(data); }; }; +/* Stack allocation strategy for sinkToSource. + Mutable to avoid a boehm gc dependency in libutil. + + boost::context doesn't provide a virtual class, so we define our own. + */ +struct StackAllocator { + virtual boost::context::stack_context allocate() = 0; + virtual void deallocate(boost::context::stack_context sctx) = 0; + + /* The stack allocator to use in sinkToSource and potentially elsewhere. + It is reassigned by the initGC() method in libexpr. */ + static StackAllocator *defaultAllocator; +}; } diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index c4d8a4f91..2da169ba7 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -66,7 +66,7 @@ private: *buffer = self->buffer.data(); try { - return self->source->read(self->buffer.data(), 4096); + return self->source->read((char *) self->buffer.data(), 4096); } catch (EndOfFile &) { return 0; } catch (std::exception & err) { diff --git a/src/libutil/tests/config.cc b/src/libutil/tests/config.cc index c7777a21f..c305af9f5 100644 --- a/src/libutil/tests/config.cc +++ b/src/libutil/tests/config.cc @@ -80,8 +80,8 @@ namespace nix { class TestSetting : public AbstractSetting { public: TestSetting() : AbstractSetting("test", "test", {}) {} - void set(const std::string & value) {} - std::string to_string() const { return {}; } + void set(const std::string & value, bool append) override {} + std::string to_string() const override { return {}; } }; Config config; diff --git a/src/libutil/tests/tests.cc b/src/libutil/tests/tests.cc index 8e77ccbe1..35a5d27bb 100644 --- a/src/libutil/tests/tests.cc +++ b/src/libutil/tests/tests.cc @@ -1,6 +1,7 @@ #include "util.hh" #include "types.hh" +#include <limits.h> #include <gtest/gtest.h> namespace nix { @@ -586,4 +587,14 @@ namespace nix { ASSERT_EQ(filterANSIEscapes(s, true), "foo bar baz" ); } + + TEST(filterANSIEscapes, utf8) { + ASSERT_EQ(filterANSIEscapes("foobar", true, 5), "fooba"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 6), "fóóbär"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 5), "fóóbä"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 3), "fóó"); + ASSERT_EQ(filterANSIEscapes("f€€bär", true, 4), "f€€b"); + ASSERT_EQ(filterANSIEscapes("f𐍈𐍈bär", true, 4), "f𐍈𐍈b"); + } + } diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 55d02bcf9..9c85fef62 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -4,6 +4,7 @@ #include <list> #include <set> +#include <string> #include <map> #include <vector> @@ -33,4 +34,16 @@ struct OnStartup OnStartup(T && t) { t(); } }; +/* Wrap bools to prevent string literals (i.e. 'char *') from being + cast to a bool in Attr. */ +template<typename T> +struct Explicit { + T t; + + bool operator ==(const Explicit<T> & other) const + { + return t == other.t; + } +}; + } diff --git a/src/libutil/url-parts.hh b/src/libutil/url-parts.hh index 68be15cb0..5d21b8d1a 100644 --- a/src/libutil/url-parts.hh +++ b/src/libutil/url-parts.hh @@ -8,7 +8,8 @@ namespace nix { // URI stuff. const static std::string pctEncoded = "(?:%[0-9a-fA-F][0-9a-fA-F])"; const static std::string schemeRegex = "(?:[a-z][a-z0-9+.-]*)"; -const static std::string ipv6AddressRegex = "(?:\\[[0-9a-fA-F:]+\\])"; +const static std::string ipv6AddressSegmentRegex = "[0-9a-fA-F:]+"; +const static std::string ipv6AddressRegex = "(?:\\[" + ipv6AddressSegmentRegex + "\\]|" + ipv6AddressSegmentRegex + ")"; const static std::string unreservedRegex = "(?:[a-zA-Z0-9-._~])"; const static std::string subdelimsRegex = "(?:[!$&'\"()*+,;=])"; const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + ")*)"; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9804e9a51..e6b6d287d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -320,7 +320,7 @@ void readFile(const Path & path, Sink & sink) } -void writeFile(const Path & path, const string & s, mode_t mode) +void writeFile(const Path & path, std::string_view s, mode_t mode) { AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); if (!fd) @@ -340,13 +340,13 @@ void writeFile(const Path & path, Source & source, mode_t mode) if (!fd) throw SysError("opening file '%1%'", path); - std::vector<unsigned char> buf(64 * 1024); + std::vector<char> buf(64 * 1024); try { while (true) { try { auto n = source.read(buf.data(), buf.size()); - writeFull(fd.get(), (unsigned char *) buf.data(), n); + writeFull(fd.get(), {buf.data(), n}); } catch (EndOfFile &) { break; } } } catch (Error & e) { @@ -632,11 +632,11 @@ void replaceSymlink(const Path & target, const Path & link, } -void readFull(int fd, unsigned char * buf, size_t count) +void readFull(int fd, char * buf, size_t count) { while (count) { checkInterrupt(); - ssize_t res = read(fd, (char *) buf, count); + ssize_t res = read(fd, buf, count); if (res == -1) { if (errno == EINTR) continue; throw SysError("reading from file"); @@ -648,27 +648,19 @@ void readFull(int fd, unsigned char * buf, size_t count) } -void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts) +void writeFull(int fd, std::string_view s, bool allowInterrupts) { - while (count) { + while (!s.empty()) { if (allowInterrupts) checkInterrupt(); - ssize_t res = write(fd, (char *) buf, count); + ssize_t res = write(fd, s.data(), s.size()); if (res == -1 && errno != EINTR) throw SysError("writing to file"); - if (res > 0) { - count -= res; - buf += res; - } + if (res > 0) + s.remove_prefix(res); } } -void writeFull(int fd, const string & s, bool allowInterrupts) -{ - writeFull(fd, (const unsigned char *) s.data(), s.size(), allowInterrupts); -} - - string drainFD(int fd, bool block, const size_t reserveSize) { StringSink sink(reserveSize); @@ -705,7 +697,7 @@ void drainFD(int fd, Sink & sink, bool block) throw SysError("reading from file"); } else if (rd == 0) break; - else sink(buf.data(), rd); + else sink({(char *) buf.data(), (size_t) rd}); } } @@ -1145,7 +1137,7 @@ void runProgram2(const RunOptions & options) in.readSide = -1; writerThread = std::thread([&]() { try { - std::vector<unsigned char> buf(8 * 1024); + std::vector<char> buf(8 * 1024); while (true) { size_t n; try { @@ -1153,7 +1145,7 @@ void runProgram2(const RunOptions & options) } catch (EndOfFile &) { break; } - writeFull(in.writeSide.get(), buf.data(), n); + writeFull(in.writeSide.get(), {buf.data(), n}); } promise.set_value(); } catch (...) { @@ -1273,11 +1265,11 @@ string trim(const string & s, const string & whitespace) } -string replaceStrings(const std::string & s, +string replaceStrings(std::string_view s, const std::string & from, const std::string & to) { - if (from.empty()) return s; - string res = s; + string res(s); + if (from.empty()) return res; size_t pos = 0; while ((pos = res.find(from, pos)) != std::string::npos) { res.replace(pos, from.size(), to); @@ -1409,7 +1401,28 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in i++; else { - t += *i++; w++; + w++; + // Copy one UTF-8 character. + if ((*i & 0xe0) == 0xc0) { + t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++; + } else if ((*i & 0xf0) == 0xe0) { + t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) { + t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++; + } + } else if ((*i & 0xf8) == 0xf0) { + t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) { + t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) { + t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++; + } + } + } else + t += *i++; } } @@ -1660,4 +1673,33 @@ string showBytes(uint64_t bytes) } +void commonChildInit(Pipe & logPipe) +{ + const static string pathNullDevice = "/dev/null"; + restoreSignals(); + + /* Put the child in a separate session (and thus a separate + process group) so that it has no controlling terminal (meaning + that e.g. ssh cannot open /dev/tty) and it doesn't receive + terminal signals. */ + if (setsid() == -1) + throw SysError("creating a new session"); + + /* Dup the write side of the logger pipe into stderr. */ + if (dup2(logPipe.writeSide.get(), STDERR_FILENO) == -1) + throw SysError("cannot pipe standard error into log file"); + + /* Dup stderr to stdout. */ + if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) + throw SysError("cannot dup stderr into stdout"); + + /* Reroute stdin to /dev/null. */ + int fdDevNull = open(pathNullDevice.c_str(), O_RDWR); + if (fdDevNull == -1) + throw SysError("cannot open '%1%'", pathNullDevice); + if (dup2(fdDevNull, STDIN_FILENO) == -1) + throw SysError("cannot dup null device into stdin"); + close(fdDevNull); +} + } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 129d59a97..0f82bed78 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -106,7 +106,7 @@ string readFile(const Path & path); void readFile(const Path & path, Sink & sink); /* Write a string to a file. */ -void writeFile(const Path & path, const string & s, mode_t mode = 0666); +void writeFile(const Path & path, std::string_view s, mode_t mode = 0666); void writeFile(const Path & path, Source & source, mode_t mode = 0666); @@ -155,9 +155,8 @@ void replaceSymlink(const Path & target, const Path & link, /* Wrappers arount read()/write() that read/write exactly the requested number of bytes. */ -void readFull(int fd, unsigned char * buf, size_t count); -void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts = true); -void writeFull(int fd, const string & s, bool allowInterrupts = true); +void readFull(int fd, char * buf, size_t count); +void writeFull(int fd, std::string_view s, bool allowInterrupts = true); MakeError(EndOfFile, Error); @@ -383,7 +382,7 @@ string trim(const string & s, const string & whitespace = " \n\r\t"); /* Replace all occurrences of a string inside another string. */ -string replaceStrings(const std::string & s, +string replaceStrings(std::string_view s, const std::string & from, const std::string & to); @@ -536,6 +535,8 @@ typedef std::function<bool(const Path & path)> PathFilter; extern PathFilter defaultPathFilter; +/* Common initialisation performed in child processes. */ +void commonChildInit(Pipe & logPipe); /* Create a Unix domain socket in listen mode. */ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); |