diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/config.cc | 231 | ||||
-rw-r--r-- | src/libutil/config.hh | 192 | ||||
-rw-r--r-- | src/libutil/hash.cc | 2 | ||||
-rw-r--r-- | src/libutil/json.cc | 43 | ||||
-rw-r--r-- | src/libutil/json.hh | 11 | ||||
-rw-r--r-- | src/libutil/lazy.hh | 48 | ||||
-rw-r--r-- | src/libutil/logging.cc | 22 | ||||
-rw-r--r-- | src/libutil/logging.hh | 99 | ||||
-rw-r--r-- | src/libutil/pool.hh | 16 | ||||
-rw-r--r-- | src/libutil/types.hh | 19 | ||||
-rw-r--r-- | src/libutil/util.cc | 64 | ||||
-rw-r--r-- | src/libutil/util.hh | 15 |
12 files changed, 666 insertions, 96 deletions
diff --git a/src/libutil/config.cc b/src/libutil/config.cc new file mode 100644 index 000000000..f7a46bfee --- /dev/null +++ b/src/libutil/config.cc @@ -0,0 +1,231 @@ +#include "config.hh" +#include "args.hh" +#include "json.hh" + +namespace nix { + +void Config::set(const std::string & name, const std::string & value) +{ + auto i = _settings.find(name); + if (i == _settings.end()) + throw UsageError("unknown setting '%s'", name); + i->second.setting->set(value); + i->second.setting->overriden = true; +} + +void Config::addSetting(AbstractSetting * setting) +{ + _settings.emplace(setting->name, Config::SettingData(false, setting)); + for (auto & alias : setting->aliases) + _settings.emplace(alias, Config::SettingData(true, setting)); + + bool set = false; + + auto i = initials.find(setting->name); + if (i != initials.end()) { + setting->set(i->second); + setting->overriden = true; + initials.erase(i); + set = true; + } + + for (auto & alias : setting->aliases) { + auto i = initials.find(alias); + if (i != initials.end()) { + if (set) + warn("setting '%s' is set, but it's an alias of '%s' which is also set", + alias, setting->name); + else { + setting->set(i->second); + setting->overriden = true; + initials.erase(i); + set = true; + } + } + } +} + +void Config::warnUnknownSettings() +{ + for (auto & i : initials) + warn("unknown setting '%s'", i.first); +} + +StringMap Config::getSettings(bool overridenOnly) +{ + StringMap res; + for (auto & opt : _settings) + if (!opt.second.isAlias && (!overridenOnly || opt.second.setting->overriden)) + res.emplace(opt.first, opt.second.setting->to_string()); + return res; +} + +void Config::applyConfigFile(const Path & path, bool fatal) +{ + try { + string contents = readFile(path); + + unsigned int pos = 0; + + while (pos < contents.size()) { + string line; + while (pos < contents.size() && contents[pos] != '\n') + line += contents[pos++]; + pos++; + + string::size_type hash = line.find('#'); + if (hash != string::npos) + line = string(line, 0, hash); + + vector<string> tokens = tokenizeString<vector<string> >(line); + if (tokens.empty()) continue; + + if (tokens.size() < 2 || tokens[1] != "=") + throw UsageError("illegal configuration line ‘%1%’ in ‘%2%’", line, path); + + string name = tokens[0]; + + vector<string>::iterator i = tokens.begin(); + advance(i, 2); + + try { + set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow + } catch (UsageError & e) { + if (fatal) throw; + warn("in configuration file '%s': %s", path, e.what()); + } + }; + } catch (SysError &) { } +} + +void Config::resetOverriden() +{ + for (auto & s : _settings) + s.second.setting->overriden = false; +} + +void Config::toJSON(JSONObject & out) +{ + for (auto & s : _settings) + if (!s.second.isAlias) { + JSONObject out2(out.object(s.first)); + out2.attr("description", s.second.setting->description); + JSONPlaceholder out3(out2.placeholder("value")); + s.second.setting->toJSON(out3); + } +} + +AbstractSetting::AbstractSetting( + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases) + : name(name), description(description), aliases(aliases) +{ +} + +void AbstractSetting::toJSON(JSONPlaceholder & out) +{ + out.write(to_string()); +} + +template<typename T> +void BaseSetting<T>::toJSON(JSONPlaceholder & out) +{ + out.write(value); +} + +template<> void BaseSetting<std::string>::set(const std::string & str) +{ + value = str; +} + +template<> std::string BaseSetting<std::string>::to_string() +{ + return value; +} + +template<typename T> +void BaseSetting<T>::set(const std::string & str) +{ + static_assert(std::is_integral<T>::value, "Integer required."); + if (!string2Int(str, value)) + throw UsageError("setting '%s' has invalid value '%s'", name, str); +} + +template<typename T> +std::string BaseSetting<T>::to_string() +{ + static_assert(std::is_integral<T>::value, "Integer required."); + return std::to_string(value); +} + +template<> void BaseSetting<bool>::set(const std::string & str) +{ + if (str == "true" || str == "yes" || str == "1") + value = true; + else if (str == "false" || str == "no" || str == "0") + value = false; + else + throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); +} + +template<> std::string BaseSetting<bool>::to_string() +{ + return value ? "true" : "false"; +} + +template<> void BaseSetting<Strings>::set(const std::string & str) +{ + value = tokenizeString<Strings>(str); +} + +template<> std::string BaseSetting<Strings>::to_string() +{ + return concatStringsSep(" ", value); +} + +template<> void BaseSetting<Strings>::toJSON(JSONPlaceholder & out) +{ + JSONList list(out.list()); + for (auto & s : value) + list.elem(s); +} + +template<> void BaseSetting<StringSet>::set(const std::string & str) +{ + value = tokenizeString<StringSet>(str); +} + +template<> std::string BaseSetting<StringSet>::to_string() +{ + return concatStringsSep(" ", value); +} + +template<> void BaseSetting<StringSet>::toJSON(JSONPlaceholder & out) +{ + JSONList list(out.list()); + for (auto & s : value) + list.elem(s); +} + +template class BaseSetting<int>; +template class BaseSetting<unsigned int>; +template class BaseSetting<long>; +template class BaseSetting<unsigned long>; +template class BaseSetting<long long>; +template class BaseSetting<unsigned long long>; +template class BaseSetting<bool>; +template class BaseSetting<std::string>; + +void PathSetting::set(const std::string & str) +{ + if (str == "") { + if (allowEmpty) + value = ""; + else + throw UsageError("setting '%s' cannot be empty", name); + } else + value = canonPath(str); +} + +} diff --git a/src/libutil/config.hh b/src/libutil/config.hh new file mode 100644 index 000000000..77620d47d --- /dev/null +++ b/src/libutil/config.hh @@ -0,0 +1,192 @@ +#include <map> +#include <set> + +#include "types.hh" + +#pragma once + +namespace nix { + +class Args; +class AbstractSetting; +class JSONPlaceholder; +class JSONObject; + +/* A class to simplify providing configuration settings. The typical + use is to inherit Config and add Setting<T> members: + + class MyClass : private Config + { + Setting<int> foo{this, 123, "foo", "the number of foos to use"}; + Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"}; + + MyClass() : Config(readConfigFile("/etc/my-app.conf")) + { + std::cout << foo << "\n"; // will print 123 unless overriden + } + }; +*/ + +class Config +{ + friend class AbstractSetting; + + struct SettingData + { + bool isAlias; + AbstractSetting * setting; + SettingData(bool isAlias, AbstractSetting * setting) + : isAlias(isAlias), setting(setting) + { } + }; + + std::map<std::string, SettingData> _settings; + + StringMap initials; + +public: + + Config(const StringMap & initials) + : initials(initials) + { } + + void set(const std::string & name, const std::string & value); + + void addSetting(AbstractSetting * setting); + + void warnUnknownSettings(); + + StringMap getSettings(bool overridenOnly = false); + + void applyConfigFile(const Path & path, bool fatal = false); + + void resetOverriden(); + + void toJSON(JSONObject & out); +}; + +class AbstractSetting +{ + friend class Config; + +public: + + const std::string name; + const std::string description; + const std::set<std::string> aliases; + + int created = 123; + + bool overriden = false; + +protected: + + AbstractSetting( + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases); + + virtual ~AbstractSetting() + { + // Check against a gcc miscompilation causing our constructor + // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431). + assert(created == 123); + } + + virtual void set(const std::string & value) = 0; + + virtual std::string to_string() = 0; + + virtual void toJSON(JSONPlaceholder & out); + + bool isOverriden() { return overriden; } +}; + +/* A setting of type T. */ +template<typename T> +class BaseSetting : public AbstractSetting +{ +protected: + + T value; + +public: + + BaseSetting(const T & def, + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases = {}) + : AbstractSetting(name, description, aliases) + , value(def) + { } + + operator const T &() const { return value; } + operator T &() { return value; } + const T & get() const { return value; } + bool operator ==(const T & v2) const { return value == v2; } + bool operator !=(const T & v2) const { return value != v2; } + void operator =(const T & v) { assign(v); } + virtual void assign(const T & v) { value = v; } + + void set(const std::string & str) override; + + std::string to_string() override; + + void toJSON(JSONPlaceholder & out) override; +}; + +template<typename T> +std::ostream & operator <<(std::ostream & str, const BaseSetting<T> & opt) +{ + str << (const T &) opt; + return str; +} + +template<typename T> +bool operator ==(const T & v1, const BaseSetting<T> & v2) { return v1 == (const T &) v2; } + +template<typename T> +class Setting : public BaseSetting<T> +{ +public: + Setting(Config * options, + const T & def, + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases = {}) + : BaseSetting<T>(def, name, description, aliases) + { + options->addSetting(this); + } + + void operator =(const T & v) { this->assign(v); } +}; + +/* A special setting for Paths. These are automatically canonicalised + (e.g. "/foo//bar/" becomes "/foo/bar"). */ +class PathSetting : public BaseSetting<Path> +{ + bool allowEmpty; + +public: + + PathSetting(Config * options, + bool allowEmpty, + const Path & def, + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases = {}) + : BaseSetting<Path>(def, name, description, aliases) + , allowEmpty(allowEmpty) + { + options->addSetting(this); + } + + void set(const std::string & str) override; + + Path operator +(const char * p) const { return value + p; } + + void operator =(const Path & v) { this->assign(v); } +}; + +} diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 9f4afd93c..fa1bb5d97 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -224,7 +224,7 @@ static void start(HashType ht, Ctx & ctx) static void update(HashType ht, Ctx & ctx, - const unsigned char * bytes, unsigned int len) + const unsigned char * bytes, size_t len) { if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len); else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len); diff --git a/src/libutil/json.cc b/src/libutil/json.cc index 6023d1d4f..b8b8ef9c8 100644 --- a/src/libutil/json.cc +++ b/src/libutil/json.cc @@ -19,49 +19,32 @@ void toJSON(std::ostream & str, const char * start, const char * end) str << '"'; } -void toJSON(std::ostream & str, const std::string & s) -{ - toJSON(str, s.c_str(), s.c_str() + s.size()); -} - void toJSON(std::ostream & str, const char * s) { if (!s) str << "null"; else toJSON(str, s, s + strlen(s)); } -void toJSON(std::ostream & str, unsigned long long n) -{ - str << n; -} - -void toJSON(std::ostream & str, unsigned long n) -{ - str << n; -} - -void toJSON(std::ostream & str, long n) -{ - str << n; -} +template<> void toJSON<int>(std::ostream & str, const int & n) { str << n; } +template<> void toJSON<unsigned int>(std::ostream & str, const unsigned int & n) { str << n; } +template<> void toJSON<long>(std::ostream & str, const long & n) { str << n; } +template<> void toJSON<unsigned long>(std::ostream & str, const unsigned long & n) { str << n; } +template<> void toJSON<long long>(std::ostream & str, const long long & n) { str << n; } +template<> void toJSON<unsigned long long>(std::ostream & str, const unsigned long long & n) { str << n; } +template<> void toJSON<float>(std::ostream & str, const float & n) { str << n; } -void toJSON(std::ostream & str, unsigned int n) +template<> void toJSON<std::string>(std::ostream & str, const std::string & s) { - str << n; -} - -void toJSON(std::ostream & str, int n) -{ - str << n; + toJSON(str, s.c_str(), s.c_str() + s.size()); } -void toJSON(std::ostream & str, double f) +template<> void toJSON<bool>(std::ostream & str, const bool & b) { - str << f; + str << (b ? "true" : "false"); } -void toJSON(std::ostream & str, bool b) +template<> void toJSON<std::nullptr_t>(std::ostream & str, const std::nullptr_t & b) { - str << (b ? "true" : "false"); + str << "null"; } JSONWriter::JSONWriter(std::ostream & str, bool indent) diff --git a/src/libutil/json.hh b/src/libutil/json.hh index 03eecb732..595e9bbe3 100644 --- a/src/libutil/json.hh +++ b/src/libutil/json.hh @@ -7,15 +7,10 @@ namespace nix { void toJSON(std::ostream & str, const char * start, const char * end); -void toJSON(std::ostream & str, const std::string & s); void toJSON(std::ostream & str, const char * s); -void toJSON(std::ostream & str, unsigned long long n); -void toJSON(std::ostream & str, unsigned long n); -void toJSON(std::ostream & str, long n); -void toJSON(std::ostream & str, unsigned int n); -void toJSON(std::ostream & str, int n); -void toJSON(std::ostream & str, double f); -void toJSON(std::ostream & str, bool b); + +template<typename T> +void toJSON(std::ostream & str, const T & n); class JSONWriter { diff --git a/src/libutil/lazy.hh b/src/libutil/lazy.hh new file mode 100644 index 000000000..d073e486c --- /dev/null +++ b/src/libutil/lazy.hh @@ -0,0 +1,48 @@ +#include <exception> +#include <functional> +#include <mutex> + +namespace nix { + +/* A helper class for lazily-initialized variables. + + Lazy<T> var([]() { return value; }); + + declares a variable of type T that is initialized to 'value' (in a + thread-safe way) on first use, that is, when var() is first + called. If the initialiser code throws an exception, then all + subsequent calls to var() will rethrow that exception. */ +template<typename T> +class Lazy +{ + + typedef std::function<T()> Init; + + Init init; + + std::once_flag done; + + T value; + + std::exception_ptr ex; + +public: + + Lazy(Init init) : init(init) + { } + + const T & operator () () + { + std::call_once(done, [&]() { + try { + value = init(); + } catch (...) { + ex = std::current_exception(); + } + }); + if (ex) std::rethrow_exception(ex); + return value; + } +}; + +} diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index d9e8d22d7..2d0acca24 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -1,9 +1,16 @@ #include "logging.hh" #include "util.hh" +#include <atomic> + namespace nix { -Logger * logger = 0; +Logger * logger = makeDefaultLogger(); + +void Logger::warn(const std::string & msg) +{ + log(lvlInfo, ANSI_RED "warning:" ANSI_NORMAL " " + msg); +} class SimpleLogger : public Logger { @@ -37,12 +44,7 @@ public: writeToStderr(prefix + (tty ? fs.s : filterANSIEscapes(fs.s)) + "\n"); } - void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override - { - log(lvl, fs); - } - - void stopActivity(Activity & activity) override + void event(const Event & ev) override { } }; @@ -52,7 +54,7 @@ Verbosity verbosity = lvlInfo; void warnOnce(bool & haveWarned, const FormatOrString & fs) { if (!haveWarned) { - printError(format("warning: %1%") % fs.s); + warn(fs.s); haveWarned = true; } } @@ -74,4 +76,8 @@ Logger * makeDefaultLogger() return new SimpleLogger(); } +std::atomic<uint64_t> Activity::nextId{(uint64_t) getpid() << 32}; + +Activity::Activity() : id(nextId++) { }; + } diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 3f8366479..ddfc336fe 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -13,7 +13,64 @@ typedef enum { lvlVomit } Verbosity; -class Activity; +class Activity +{ + static std::atomic<uint64_t> nextId; +public: + typedef uint64_t t; + const t id; + Activity(); + Activity(const Activity & act) : id(act.id) { }; + Activity(uint64_t id) : id(id) { }; +}; + +typedef enum { + evBuildCreated = 0, + evBuildStarted = 1, + evBuildOutput = 2, + evBuildFinished = 3, + evDownloadCreated = 4, + evDownloadDestroyed = 5, + evDownloadProgress = 6, + evDownloadSucceeded = 7, + evSubstitutionCreated = 8, + evSubstitutionStarted = 9, + evSubstitutionFinished = 10, +} EventType; + +struct Event +{ + struct Field + { + // FIXME: use std::variant. + enum { tInt, tString } type; + uint64_t i = 0; + std::string s; + Field(const std::string & s) : type(tString), s(s) { } + Field(const char * s) : type(tString), s(s) { } + Field(const uint64_t & i) : type(tInt), i(i) { } + Field(const Activity & act) : type(tInt), i(act.id) { } + }; + + typedef std::vector<Field> Fields; + + EventType type; + Fields fields; + + std::string getS(size_t n) const + { + assert(n < fields.size()); + assert(fields[n].type == Field::tString); + return fields[n].s; + } + + uint64_t getI(size_t n) const + { + assert(n < fields.size()); + assert(fields[n].type == Field::tInt); + return fields[n].i; + } +}; class Logger { @@ -30,34 +87,18 @@ public: log(lvlInfo, fs); } - virtual void setExpected(const std::string & label, uint64_t value = 1) { } - virtual void setProgress(const std::string & label, uint64_t value = 1) { } - virtual void incExpected(const std::string & label, uint64_t value = 1) { } - virtual void incProgress(const std::string & label, uint64_t value = 1) { } + virtual void warn(const std::string & msg); -private: - - virtual void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) = 0; - - virtual void stopActivity(Activity & activity) = 0; - -}; - -class Activity -{ -public: - Logger & logger; - - Activity(Logger & logger, Verbosity lvl, const FormatOrString & fs) - : logger(logger) + template<typename... Args> + void event(EventType type, const Args & ... args) { - logger.startActivity(*this, lvl, fs); + Event ev; + ev.type = type; + nop{(ev.fields.emplace_back(Event::Field(args)), 1)...}; + event(ev); } - ~Activity() - { - logger.stopActivity(*this); - } + virtual void event(const Event & ev) = 0; }; extern Logger * logger; @@ -82,6 +123,14 @@ extern Verbosity verbosity; /* suppress msgs > this */ #define debug(args...) printMsg(lvlDebug, args) #define vomit(args...) printMsg(lvlVomit, args) +template<typename... Args> +inline void warn(const std::string & fs, Args... args) +{ + boost::format f(fs); + nop{boost::io::detail::feed(f, args)...}; + logger->warn(f.str()); +} + void warnOnce(bool & haveWarned, const FormatOrString & fs); void writeToStderr(const string & s); diff --git a/src/libutil/pool.hh b/src/libutil/pool.hh index 20df21948..703309002 100644 --- a/src/libutil/pool.hh +++ b/src/libutil/pool.hh @@ -68,6 +68,22 @@ public: state_->max = max; } + void incCapacity() + { + auto state_(state.lock()); + state_->max++; + /* we could wakeup here, but this is only used when we're + * about to nest Pool usages, and we want to save the slot for + * the nested use if we can + */ + } + + void decCapacity() + { + auto state_(state.lock()); + state_->max--; + } + ~Pool() { auto state_(state.lock()); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 97d79af9b..9f32d31ad 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -7,6 +7,7 @@ #include <list> #include <set> #include <memory> +#include <map> #include <boost/format.hpp> @@ -31,6 +32,11 @@ using std::vector; using boost::format; +/* A variadic template that does nothing. Useful to call a function + for all variadic arguments but ignoring the result. */ +struct nop { template<typename... T> nop(T...) {} }; + + struct FormatOrString { string s; @@ -45,16 +51,6 @@ struct FormatOrString ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion takes place). */ -inline void formatHelper(boost::format & f) -{ -} - -template<typename T, typename... Args> -inline void formatHelper(boost::format & f, T x, Args... args) -{ - formatHelper(f % x, args...); -} - inline std::string fmt(const std::string & s) { return s; @@ -74,7 +70,7 @@ template<typename... Args> inline std::string fmt(const std::string & fs, Args... args) { boost::format f(fs); - formatHelper(f, args...); + nop{boost::io::detail::feed(f, args)...}; return f.str(); } @@ -141,6 +137,7 @@ private: typedef list<string> Strings; typedef set<string> StringSet; +typedef std::map<std::string, std::string> StringMap; /* Paths are just strings. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index a640a64c7..16f4b232e 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1,3 +1,4 @@ +#include "lazy.hh" #include "util.hh" #include "affinity.hh" #include "sync.hh" @@ -13,10 +14,12 @@ #include <thread> #include <future> -#include <sys/wait.h> -#include <unistd.h> #include <fcntl.h> #include <limits.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> #ifdef __APPLE__ #include <sys/syscall.h> @@ -96,6 +99,8 @@ Path absPath(Path path, Path dir) Path canonPath(const Path & path, bool resolveSymlinks) { + assert(path != ""); + string s; if (path[0] != '/') @@ -367,7 +372,7 @@ void deletePath(const Path & path) void deletePath(const Path & path, unsigned long long & bytesFreed) { - Activity act(*logger, lvlDebug, format("recursively deleting path ‘%1%’") % path); + //Activity act(*logger, lvlDebug, format("recursively deleting path ‘%1%’") % path); bytesFreed = 0; _deletePath(path, bytesFreed); } @@ -415,18 +420,50 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, } +static Lazy<Path> getHome2([]() { + Path homeDir = getEnv("HOME"); + if (homeDir.empty()) { + char buf[16384]; + struct passwd pwbuf; + struct passwd * pw; + if (getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pw) != 0 + || !pw || !pw->pw_dir || !pw->pw_dir[0]) + throw Error("cannot determine user's home directory"); + homeDir = pw->pw_dir; + } + return homeDir; +}); + +Path getHome() { return getHome2(); } + + Path getCacheDir() { Path cacheDir = getEnv("XDG_CACHE_HOME"); - if (cacheDir.empty()) { - Path homeDir = getEnv("HOME"); - if (homeDir.empty()) throw Error("$XDG_CACHE_HOME and $HOME are not set"); - cacheDir = homeDir + "/.cache"; - } + if (cacheDir.empty()) + cacheDir = getHome() + "/.cache"; return cacheDir; } +Path getConfigDir() +{ + Path configDir = getEnv("XDG_CONFIG_HOME"); + if (configDir.empty()) + configDir = getHome() + "/.config"; + return configDir; +} + + +Path getDataDir() +{ + Path dataDir = getEnv("XDG_DATA_HOME"); + if (dataDir.empty()) + dataDir = getHome() + "/.local/share"; + return dataDir; +} + + Paths createDirs(const Path & path) { Paths created; @@ -932,7 +969,12 @@ void closeOnExec(int fd) bool _isInterrupted = false; -thread_local bool interruptThrown = false; +static thread_local bool interruptThrown = false; + +void setInterruptThrown() +{ + interruptThrown = true; +} void _interrupted() { @@ -1047,9 +1089,9 @@ bool statusOk(int status) } -bool hasPrefix(const string & s, const string & suffix) +bool hasPrefix(const string & s, const string & prefix) { - return s.compare(0, suffix.size(), suffix) == 0; + return s.compare(0, prefix.size(), prefix) == 0; } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 0e6941e4a..7ea32e8d9 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -110,9 +110,18 @@ void deletePath(const Path & path, unsigned long long & bytesFreed); Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755); -/* Return the path to $XDG_CACHE_HOME/.cache. */ +/* Return $HOME or the user's home directory from /etc/passwd. */ +Path getHome(); + +/* Return $XDG_CACHE_HOME or $HOME/.cache. */ Path getCacheDir(); +/* Return $XDG_CONFIG_HOME or $HOME/.config. */ +Path getConfigDir(); + +/* Return $XDG_DATA_HOME or $HOME/.local/share. */ +Path getDataDir(); + /* Create a directory and all its parents, if necessary. Returns the list of created directories, in order of creation. */ Paths createDirs(const Path & path); @@ -264,7 +273,7 @@ void closeOnExec(int fd); extern bool _isInterrupted; -extern thread_local bool interruptThrown; +void setInterruptThrown(); void _interrupted(); @@ -355,6 +364,8 @@ void ignoreException(); #define ANSI_NORMAL "\e[0m" #define ANSI_BOLD "\e[1m" #define ANSI_RED "\e[31;1m" +#define ANSI_GREEN "\e[32;1m" +#define ANSI_BLUE "\e[34;1m" /* Filter out ANSI escape codes from the given string. If ‘nixOnly’ is |