1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-12 13:36:02 +01:00

Merge remote-tracking branch 'upstream/master' into upstream-merge

This commit is contained in:
Ben Burdette 2022-04-07 13:42:01 -06:00
commit 1a93ac8133
262 changed files with 5827 additions and 2852 deletions

View file

@ -37,7 +37,7 @@ 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; };
@ -64,11 +64,12 @@ static void dumpContents(const Path & path, off_t size,
}
static void dump(const Path & path, Sink & sink, PathFilter & filter)
static time_t dump(const Path & path, Sink & sink, PathFilter & filter)
{
checkInterrupt();
auto st = lstat(path);
time_t result = st.st_mtime;
sink << "(";
@ -84,12 +85,12 @@ 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);
}
@ -103,7 +104,10 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
for (auto & i : unhacked)
if (filter(path + "/" + i.first)) {
sink << "entry" << "(" << "name" << i.first << "node";
dump(path + "/" + i.second, sink, filter);
auto tmp_mtime = dump(path + "/" + i.second, sink, filter);
if (tmp_mtime > result) {
result = tmp_mtime;
}
sink << ")";
}
}
@ -114,23 +118,30 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
else throw Error("file '%1%' has an unsupported type", path);
sink << ")";
return result;
}
time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter)
{
sink << narVersionMagic1;
return dump(path, sink, filter);
}
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
{
sink << narVersionMagic1;
dump(path, sink, filter);
dumpPathAndGetMtime(path, sink, 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);
}
@ -171,7 +182,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;
}
@ -180,7 +191,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");
@ -201,7 +212,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;
@ -232,7 +243,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");
@ -246,7 +257,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");
@ -269,7 +280,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);
}
@ -281,7 +292,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) {
@ -345,7 +356,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);

View file

@ -48,7 +48,11 @@ namespace nix {
void dumpPath(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
void dumpString(const std::string & s, Sink & sink);
/* Same as `void dumpPath()`, but returns the last modified date of the path */
time_t dumpPathAndGetMtime(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
void dumpString(std::string_view s, Sink & sink);
/* FIXME: fix this API, it sucks. */
struct ParseSink
@ -60,7 +64,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 +86,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;
}

View file

@ -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;
@ -328,8 +328,13 @@ MultiCommand::MultiCommand(const Commands & commands_)
completions->add(name);
}
auto i = commands.find(s);
if (i == commands.end())
throw UsageError("'%s' is not a recognised command", s);
if (i == commands.end()) {
std::set<std::string> commandNames;
for (auto & [name, _] : commands)
commandNames.insert(name);
auto suggestions = Suggestions::bestMatches(commandNames, s);
throw UsageError(suggestions, "'%s' is not a recognised command", s);
}
command = {s, i->second()};
command->second->parent = this;
}}

View file

@ -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,

View file

@ -81,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)
@ -120,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
@ -132,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 &) { }
}

View file

@ -9,15 +9,14 @@ namespace nix {
const std::string nativeSystem = SYSTEM;
BaseError & BaseError::addTrace(std::optional<ErrPos> e, hintformat hint)
void BaseError::addTrace(std::optional<ErrPos> e, hintformat hint)
{
err.traces.push_front(Trace { .pos = e, .hint = hint });
return *this;
}
// 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_;
@ -32,14 +31,14 @@ const string & BaseError::calcWhat() const
}
}
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 +67,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 +99,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 +131,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)
{
@ -282,6 +281,13 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
}
}
auto suggestions = einfo.suggestions.trim();
if (! suggestions.suggestions.empty()){
oss << "Did you mean " <<
suggestions.trim() <<
"?" << std::endl;
}
// traces
if (showTrace && !einfo.traces.empty()) {
for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter) {

View file

@ -1,5 +1,6 @@
#pragma once
#include "suggestions.hh"
#include "ref.hh"
#include "types.hh"
#include "fmt.hh"
@ -53,6 +54,7 @@ typedef enum {
lvlVomit
} Verbosity;
/* adjust Pos::origin bit width when adding stuff here */
typedef enum {
foFile,
foStdin,
@ -61,16 +63,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 +82,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 +96,7 @@ struct ErrPos {
}
template <class P>
ErrPos(const P &p)
ErrPos(const P & p)
{
*this = p;
}
@ -102,7 +104,7 @@ struct ErrPos {
std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos);
void printCodeLines(std::ostream & out,
const string & prefix,
const std::string & prefix,
const ErrPos & errPos,
const LinesOfCode & loc);
@ -116,15 +118,17 @@ 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;
Suggestions suggestions;
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. */
@ -133,8 +137,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
@ -150,6 +154,11 @@ public:
: err { .level = lvlError, .msg = hintfmt(fs, args...) }
{ }
template<typename... Args>
BaseError(const Suggestions & sug, const Args & ... args)
: err { .level = lvlError, .msg = hintfmt(args...), .suggestions = sug }
{ }
BaseError(hintformat hint)
: err { .level = lvlError, .msg = hint }
{ }
@ -171,16 +180,16 @@ 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)
void addTrace(std::optional<ErrPos> e, const std::string & fs, const Args & ... args)
{
return addTrace(e, hintfmt(fs, args...));
addTrace(e, hintfmt(fs, args...));
}
BaseError & addTrace(std::optional<ErrPos> e, hintformat hint);
void addTrace(std::optional<ErrPos> e, hintformat hint);
bool hasTrace() const { return !err.traces.empty(); }
};

View file

@ -7,10 +7,12 @@ namespace nix {
std::map<ExperimentalFeature, std::string> stringifiedXpFeatures = {
{ Xp::CaDerivations, "ca-derivations" },
{ Xp::ImpureDerivations, "impure-derivations" },
{ Xp::Flakes, "flakes" },
{ Xp::NixCommand, "nix-command" },
{ Xp::RecursiveNix, "recursive-nix" },
{ Xp::NoUrlLiterals, "no-url-literals" },
{ Xp::FetchClosure, "fetch-closure" },
};
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name)

View file

@ -16,10 +16,12 @@ namespace nix {
enum struct ExperimentalFeature
{
CaDerivations,
ImpureDerivations,
Flakes,
NixCommand,
RecursiveNix,
NoUrlLiterals
NoUrlLiterals,
FetchClosure,
};
/**

View file

@ -1,14 +1,13 @@
#pragma once
#include <functional>
/* A trivial class to run a function at the end of a scope. */
template<typename Fn>
class Finally
{
private:
std::function<void()> fun;
Fn fun;
public:
Finally(std::function<void()> fun) : fun(fun) { }
Finally(Fn fun) : fun(std::move(fun)) { }
~Finally() { fun(); }
};

View file

@ -10,7 +10,6 @@ namespace nix {
/* Inherit some names from other namespaces for convenience. */
using std::string;
using boost::format;
@ -21,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) { };
@ -102,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 ^

View file

@ -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.
@ -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";

View file

@ -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 };
@ -110,7 +110,7 @@ public:
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;

View file

@ -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);
@ -266,51 +266,63 @@ static Logger::Fields getFields(nlohmann::json & json)
return fields;
}
bool handleJSONLogMessage(const std::string & msg,
const Activity & act, std::map<ActivityId, Activity> & activities, bool trusted)
std::optional<nlohmann::json> parseJSONMessage(const std::string & msg)
{
if (!hasPrefix(msg, "@nix ")) return false;
if (!hasPrefix(msg, "@nix ")) return std::nullopt;
try {
auto json = nlohmann::json::parse(std::string(msg, 5));
std::string action = json["action"];
if (action == "start") {
auto type = (ActivityType) json["type"];
if (trusted || type == actFileTransfer)
activities.emplace(std::piecewise_construct,
std::forward_as_tuple(json["id"]),
std::forward_as_tuple(*logger, (Verbosity) json["level"], type,
json["text"], getFields(json["fields"]), act.id));
}
else if (action == "stop")
activities.erase((ActivityId) json["id"]);
else if (action == "result") {
auto i = activities.find((ActivityId) json["id"]);
if (i != activities.end())
i->second.result((ResultType) json["type"], getFields(json["fields"]));
}
else if (action == "setPhase") {
std::string phase = json["phase"];
act.result(resSetPhase, phase);
}
else if (action == "msg") {
std::string msg = json["msg"];
logger->log((Verbosity) json["level"], msg);
}
return nlohmann::json::parse(std::string(msg, 5));
} catch (std::exception & e) {
printError("bad JSON log message from builder: %s", e.what());
}
return std::nullopt;
}
bool handleJSONLogMessage(nlohmann::json & json,
const Activity & act, std::map<ActivityId, Activity> & activities,
bool trusted)
{
std::string action = json["action"];
if (action == "start") {
auto type = (ActivityType) json["type"];
if (trusted || type == actFileTransfer)
activities.emplace(std::piecewise_construct,
std::forward_as_tuple(json["id"]),
std::forward_as_tuple(*logger, (Verbosity) json["level"], type,
json["text"], getFields(json["fields"]), act.id));
}
else if (action == "stop")
activities.erase((ActivityId) json["id"]);
else if (action == "result") {
auto i = activities.find((ActivityId) json["id"]);
if (i != activities.end())
i->second.result((ResultType) json["type"], getFields(json["fields"]));
}
else if (action == "setPhase") {
std::string phase = json["phase"];
act.result(resSetPhase, phase);
}
else if (action == "msg") {
std::string msg = json["msg"];
logger->log((Verbosity) json["level"], msg);
}
return true;
}
bool handleJSONLogMessage(const std::string & msg,
const Activity & act, std::map<ActivityId, Activity> & activities, bool trusted)
{
auto json = parseJSONMessage(msg);
if (!json) return false;
return handleJSONLogMessage(*json, act, activities, trusted);
}
Activity::~Activity()
{
try {

View file

@ -4,6 +4,8 @@
#include "error.hh"
#include "config.hh"
#include <nlohmann/json_fwd.hpp>
namespace nix {
typedef enum {
@ -166,6 +168,12 @@ Logger * makeSimpleLogger(bool printBuildLogs = true);
Logger * makeJSONLogger(Logger & prevLogger);
std::optional<nlohmann::json> parseJSONMessage(const std::string & msg);
bool handleJSONLogMessage(nlohmann::json & json,
const Activity & act, std::map<ActivityId, Activity> & activities,
bool trusted);
bool handleJSONLogMessage(const std::string & msg,
const Activity & act, std::map<ActivityId, Activity> & activities,
bool trusted);
@ -216,6 +224,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);
}

View file

@ -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;

View file

@ -364,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)

114
src/libutil/suggestions.cc Normal file
View file

@ -0,0 +1,114 @@
#include "suggestions.hh"
#include "ansicolor.hh"
#include "util.hh"
#include <algorithm>
namespace nix {
int levenshteinDistance(std::string_view first, std::string_view second)
{
// Implementation borrowed from
// https://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows
int m = first.size();
int n = second.size();
auto v0 = std::vector<int>(n+1);
auto v1 = std::vector<int>(n+1);
for (auto i = 0; i <= n; i++)
v0[i] = i;
for (auto i = 0; i < m; i++) {
v1[0] = i+1;
for (auto j = 0; j < n; j++) {
auto deletionCost = v0[j+1] + 1;
auto insertionCost = v1[j] + 1;
auto substitutionCost = first[i] == second[j] ? v0[j] : v0[j] + 1;
v1[j+1] = std::min({deletionCost, insertionCost, substitutionCost});
}
std::swap(v0, v1);
}
return v0[n];
}
Suggestions Suggestions::bestMatches (
std::set<std::string> allMatches,
std::string query)
{
std::set<Suggestion> res;
for (const auto & possibleMatch : allMatches) {
res.insert(Suggestion {
.distance = levenshteinDistance(query, possibleMatch),
.suggestion = possibleMatch,
});
}
return Suggestions { res };
}
Suggestions Suggestions::trim(int limit, int maxDistance) const
{
std::set<Suggestion> res;
int count = 0;
for (auto & elt : suggestions) {
if (count >= limit || elt.distance > maxDistance)
break;
count++;
res.insert(elt);
}
return Suggestions{res};
}
std::string Suggestion::to_string() const
{
return ANSI_WARNING + filterANSIEscapes(suggestion) + ANSI_NORMAL;
}
std::string Suggestions::to_string() const
{
switch (suggestions.size()) {
case 0:
return "";
case 1:
return suggestions.begin()->to_string();
default: {
std::string res = "one of ";
auto iter = suggestions.begin();
res += iter->to_string(); // Iter cant be end() because the container isnt null
iter++;
auto last = suggestions.end(); last--;
for ( ; iter != suggestions.end() ; iter++) {
res += (iter == last) ? " or " : ", ";
res += iter->to_string();
}
return res;
}
}
}
Suggestions & Suggestions::operator+=(const Suggestions & other)
{
suggestions.insert(
other.suggestions.begin(),
other.suggestions.end()
);
return *this;
}
std::ostream & operator<<(std::ostream & str, const Suggestion & suggestion)
{
return str << suggestion.to_string();
}
std::ostream & operator<<(std::ostream & str, const Suggestions & suggestions)
{
return str << suggestions.to_string();
}
}

102
src/libutil/suggestions.hh Normal file
View file

@ -0,0 +1,102 @@
#pragma once
#include "comparator.hh"
#include "types.hh"
#include <set>
namespace nix {
int levenshteinDistance(std::string_view first, std::string_view second);
/**
* A potential suggestion for the cli interface.
*/
class Suggestion {
public:
int distance; // The smaller the better
std::string suggestion;
std::string to_string() const;
GENERATE_CMP(Suggestion, me->distance, me->suggestion)
};
class Suggestions {
public:
std::set<Suggestion> suggestions;
std::string to_string() const;
Suggestions trim(
int limit = 5,
int maxDistance = 2
) const;
static Suggestions bestMatches (
std::set<std::string> allMatches,
std::string query
);
Suggestions& operator+=(const Suggestions & other);
};
std::ostream & operator<<(std::ostream & str, const Suggestion &);
std::ostream & operator<<(std::ostream & str, const Suggestions &);
// Either a value of type `T`, or some suggestions
template<typename T>
class OrSuggestions {
public:
using Raw = std::variant<T, Suggestions>;
Raw raw;
T* operator ->()
{
return &**this;
}
T& operator *()
{
return std::get<T>(raw);
}
operator bool() const noexcept
{
return std::holds_alternative<T>(raw);
}
OrSuggestions(T t)
: raw(t)
{
}
OrSuggestions()
: raw(Suggestions{})
{
}
static OrSuggestions<T> failed(const Suggestions & s)
{
auto res = OrSuggestions<T>();
res.raw = s;
return res;
}
static OrSuggestions<T> failed()
{
return OrSuggestions<T>::failed(Suggestions{});
}
const Suggestions & getSuggestions()
{
static Suggestions noSuggestions;
if (const auto & suggestions = std::get_if<Suggestions>(&raw))
return *suggestions;
else
return noSuggestions;
}
};
}

View file

@ -39,30 +39,32 @@ void TarArchive::check(int err, const std::string & reason)
throw Error(reason, archive_error_string(this->archive));
}
TarArchive::TarArchive(Source & source, bool raw)
: source(&source), buffer(4096)
TarArchive::TarArchive(Source & source, bool raw) : buffer(4096)
{
init();
if (!raw)
this->archive = archive_read_new();
this->source = &source;
if (!raw) {
archive_read_support_filter_all(archive);
archive_read_support_format_all(archive);
else
} else {
archive_read_support_filter_all(archive);
archive_read_support_format_raw(archive);
archive_read_support_format_empty(archive);
}
check(archive_read_open(archive, (void *)this, callback_open, callback_read, callback_close), "Failed to open archive (%s)");
}
TarArchive::TarArchive(const Path & path)
{
init();
this->archive = archive_read_new();
archive_read_support_filter_all(archive);
archive_read_support_format_all(archive);
check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s");
}
void TarArchive::init()
{
archive = archive_read_new();
archive_read_support_filter_all(archive);
}
void TarArchive::close()
{
check(archive_read_close(this->archive), "Failed to close archive (%s)");

View file

@ -17,13 +17,10 @@ struct TarArchive {
// disable copy constructor
TarArchive(const TarArchive &) = delete;
void init();
void close();
~TarArchive();
};
void unpackTarfile(Source & source, const Path & destDir);
void unpackTarfile(const Path & tarFile, const Path & destDir);

View file

@ -359,7 +359,7 @@ namespace nix {
// constructing without access violation.
ErrPos ep(invalid);
// assignment without access violation.
ep = invalid;

View file

@ -0,0 +1,43 @@
#include "suggestions.hh"
#include <gtest/gtest.h>
namespace nix {
struct LevenshteinDistanceParam {
std::string s1, s2;
int distance;
};
class LevenshteinDistanceTest :
public testing::TestWithParam<LevenshteinDistanceParam> {
};
TEST_P(LevenshteinDistanceTest, CorrectlyComputed) {
auto params = GetParam();
ASSERT_EQ(levenshteinDistance(params.s1, params.s2), params.distance);
ASSERT_EQ(levenshteinDistance(params.s2, params.s1), params.distance);
}
INSTANTIATE_TEST_SUITE_P(LevenshteinDistance, LevenshteinDistanceTest,
testing::Values(
LevenshteinDistanceParam{"foo", "foo", 0},
LevenshteinDistanceParam{"foo", "", 3},
LevenshteinDistanceParam{"", "", 0},
LevenshteinDistanceParam{"foo", "fo", 1},
LevenshteinDistanceParam{"foo", "oo", 1},
LevenshteinDistanceParam{"foo", "fao", 1},
LevenshteinDistanceParam{"foo", "abc", 3}
)
);
TEST(Suggestions, Trim) {
auto suggestions = Suggestions::bestMatches({"foooo", "bar", "fo", "gao"}, "foo");
auto onlyOne = suggestions.trim(1);
ASSERT_EQ(onlyOne.suggestions.size(), 1);
ASSERT_TRUE(onlyOne.suggestions.begin()->suggestion == "fo");
auto closest = suggestions.trim(999, 2);
ASSERT_EQ(closest.suggestions.size(), 3);
}
}

View file

@ -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);
}

View file

@ -11,23 +11,18 @@
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 string Path;
typedef std::string Path;
typedef std::string_view PathView;
typedef list<Path> Paths;
typedef set<Path> PathSet;
typedef std::list<Path> Paths;
typedef std::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>

View file

@ -71,13 +71,11 @@ void clearEnv()
unsetenv(name.first.c_str());
}
void replaceEnv(std::map<std::string, std::string> newEnv)
void replaceEnv(const std::map<std::string, std::string> & newEnv)
{
clearEnv();
for (auto newEnvVar : newEnv)
{
for (auto & newEnvVar : newEnv)
setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1);
}
}
@ -110,13 +108,13 @@ 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 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. */
@ -142,7 +140,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
/* Normal component; copy it. */
else {
s += '/';
if (const auto slash = path.find('/'); slash == string::npos) {
if (const auto slash = path.find('/'); slash == std::string::npos) {
s += path;
path = {};
} else {
@ -175,7 +173,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
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);
}
@ -191,7 +189,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;
@ -249,7 +247,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);
}
}
@ -269,7 +267,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
@ -303,7 +301,7 @@ unsigned char getFileType(const Path & path)
}
string readFile(int fd)
std::string readFile(int fd)
{
struct stat st;
if (fstat(fd, &st) == -1)
@ -313,7 +311,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)
@ -366,9 +364,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;
@ -387,7 +385,7 @@ string readLine(int fd)
}
void writeLine(int fd, string s)
void writeLine(int fd, std::string s)
{
s += '\n';
writeFull(fd, s);
@ -398,7 +396,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) {
@ -406,8 +404,29 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
throw SysError("getting status of '%1%'", path);
}
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
bytesFreed += st.st_size;
if (!S_ISDIR(st.st_mode)) {
/* We are about to delete a file. Will it likely free space? */
switch (st.st_nlink) {
/* Yes: last link. */
case 1:
bytesFreed += st.st_size;
break;
/* Maybe: yes, if 'auto-optimise-store' or manual optimisation
was performed. Instead of checking for real let's assume
it's an optimised file and space will be freed.
In worst case we will double count on freed space for files
with exactly two hardlinks for unoptimised packages.
*/
case 2:
bytesFreed += st.st_size;
break;
/* No: 3+ links. */
default:
break;
}
}
if (S_ISDIR(st.st_mode)) {
/* Make the directory accessible. */
@ -566,8 +585,8 @@ Path getConfigDir()
std::vector<Path> getConfigDirs()
{
Path configHome = getConfigDir();
string configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg");
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;
}
@ -670,7 +689,7 @@ 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)
{
// the parser needs two extra bytes to append terminating characters, other users will
// not care very much about the extra memory.
@ -682,7 +701,14 @@ string drainFD(int fd, bool block, const size_t reserveSize)
void drainFD(int fd, Sink & sink, bool block)
{
int saved;
// silence GCC maybe-uninitialized warning in finally
int saved = 0;
if (!block) {
saved = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1)
throw SysError("making file descriptor non-blocking");
}
Finally finally([&]() {
if (!block) {
@ -691,12 +717,6 @@ void drainFD(int fd, Sink & sink, bool block)
}
});
if (!block) {
saved = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1)
throw SysError("making file descriptor non-blocking");
}
std::vector<unsigned char> buf(64 * 1024);
while (1) {
checkInterrupt();
@ -719,7 +739,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;
@ -1036,7 +1056,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});
@ -1174,7 +1194,7 @@ void runProgram2(const RunOptions & options)
}
void closeMostFDs(const set<int> & exceptions)
void closeMostFDs(const std::set<int> & exceptions)
{
#if __linux__
try {
@ -1238,11 +1258,11 @@ void _interrupted()
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();
result.insert(result.end(), string(s, pos, end - pos));
auto pos = s.find_first_not_of(separators, 0);
while (pos != std::string_view::npos) {
auto end = s.find_first_of(separators, pos + 1);
if (end == std::string_view::npos) end = s.size();
result.insert(result.end(), std::string(s, pos, end - pos));
pos = s.find_first_not_of(separators, end);
}
return result;
@ -1250,29 +1270,30 @@ template<class C> C tokenizeString(std::string_view s, std::string_view separato
template Strings tokenizeString(std::string_view s, std::string_view separators);
template StringSet tokenizeString(std::string_view s, std::string_view separators);
template vector<string> 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 == s.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 == s.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 +1304,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))
@ -1358,11 +1378,15 @@ std::string shellEscape(const std::string_view 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()
@ -1408,7 +1432,7 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in
}
}
else if (*i == '\r')
else if (*i == '\r' || *i == '\a')
// do nothing for now
i++;
@ -1444,9 +1468,10 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string base64Encode(std::string_view s)
std::string base64Encode(std::string_view s)
{
string res;
std::string res;
res.reserve((s.size() + 2) / 3 * 4);
int data = 0, nbits = 0;
for (char c : s) {
@ -1465,7 +1490,7 @@ string base64Encode(std::string_view s)
}
string base64Decode(std::string_view s)
std::string base64Decode(std::string_view s)
{
constexpr char npos = -1;
constexpr std::array<char, 256> base64DecodeChars = [&]() {
@ -1477,7 +1502,10 @@ string base64Decode(std::string_view s)
return result;
}();
string res;
std::string res;
// Some sequences are missing the padding consisting of up to two '='.
// vvv
res.reserve((s.size() + 2) / 4 * 3);
unsigned int d = 0, bits = 0;
for (char c : s) {
@ -1561,7 +1589,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)
{
@ -1583,8 +1626,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 (...) {
@ -1638,7 +1692,9 @@ void setStackSize(size_t stackSize)
#endif
}
#if __linux__
static AutoCloseFD fdSavedMountNamespace;
#endif
void saveMountNamespace()
{
@ -1657,8 +1713,13 @@ void restoreMountNamespace()
{
#if __linux__
try {
auto savedCwd = absPath(".");
if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1)
throw SysError("restoring parent mount namespace");
if (chdir(savedCwd.c_str()) == -1) {
throw SysError("restoring cwd");
}
} catch (Error & e) {
debug(e.msg());
}
@ -1694,21 +1755,22 @@ void restoreProcessContext(bool restoreMounts)
/* 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());
}
@ -1804,7 +1866,7 @@ void connect(int fd, const std::string & path)
}
string showBytes(uint64_t bytes)
std::string showBytes(uint64_t bytes)
{
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
}
@ -1815,7 +1877,7 @@ void commonChildInit(Pipe & logPipe)
{
logger = makeSimpleLogger();
const static string pathNullDevice = "/dev/null";
const static std::string pathNullDevice = "/dev/null";
restoreProcessContext(false);
/* Put the child in a separate session (and thus a separate

View file

@ -92,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. */
@ -116,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
@ -170,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);
@ -268,7 +268,7 @@ void killUser(uid_t uid);
pid to the caller. */
struct ProcessOptions
{
string errorPrefix = "";
std::string errorPrefix = "";
bool dieWithParent = true;
bool runExitHandlers = false;
bool allowVfork = false;
@ -279,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 = {});
@ -343,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);
@ -378,12 +378,12 @@ template<class C> C tokenizeString(std::string_view s, std::string_view separato
/* Concatenate the given strings with a separator between the
elements. */
template<class C>
string concatStringsSep(const std::string_view sep, const C & ss)
std::string concatStringsSep(const std::string_view sep, const C & ss)
{
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();
string s;
std::string s;
s.reserve(size);
for (auto & i : ss) {
if (s.size() != 0) s += sep;
@ -394,7 +394,7 @@ string concatStringsSep(const std::string_view sep, const C & ss)
template<class ... Parts>
auto concatStrings(Parts && ... parts)
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), string>
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string>
{
std::string_view views[sizeof...(parts)] = { parts... };
return concatStringsSep({}, views);
@ -413,24 +413,26 @@ 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);
@ -525,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

View file

@ -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);

View file

@ -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)
{