nix-super/src/libutil/util.cc

320 lines
8.3 KiB
C++
Raw Normal View History

#include "util.hh"
#include "fmt.hh"
2021-09-08 13:20:08 +03:00
#include <array>
#include <cctype>
#include <iostream>
#include <grp.h>
#include <regex>
namespace nix {
void initLibUtil() {
// Check that exception handling works. Exception handling has been observed
// not to work on darwin when the linker flags aren't quite right.
// In this case we don't want to expose the user to some unrelated uncaught
// exception, but rather tell them exactly that exception handling is
// broken.
// When exception handling fails, the message tends to be printed by the
// C++ runtime, followed by an abort.
// For example on macOS we might see an error such as
// libc++abi: terminating with uncaught exception of type nix::SysError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
bool caught = false;
try {
throwExceptionSelfCheck();
} catch (const nix::Error & _e) {
caught = true;
}
// This is not actually the main point of this check, but let's make sure anyway:
assert(caught);
}
//////////////////////////////////////////////////////////////////////
2015-06-09 11:50:55 +03:00
std::vector<char *> stringsToCharPtrs(const Strings & ss)
2014-12-12 16:01:16 +02:00
{
2015-06-09 11:50:55 +03:00
std::vector<char *> res;
for (auto & s : ss) res.push_back((char *) s.c_str());
2014-12-12 16:01:16 +02:00
res.push_back(0);
return res;
}
2004-06-20 16:37:51 +03:00
2004-06-22 12:51:44 +03:00
//////////////////////////////////////////////////////////////////////
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
2005-09-22 18:43:22 +03:00
{
2012-09-19 22:43:23 +03:00
C result;
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));
2005-09-22 18:43:22 +03:00
pos = s.find_first_not_of(separators, end);
}
return result;
}
template Strings tokenizeString(std::string_view s, std::string_view separators);
template StringSet tokenizeString(std::string_view s, std::string_view separators);
template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
2012-09-19 22:43:23 +03:00
2005-09-22 18:43:22 +03:00
std::string chomp(std::string_view s)
{
size_t i = s.find_last_not_of(" \n\r\t");
return i == std::string_view::npos ? "" : std::string(s, 0, i + 1);
}
std::string trim(std::string_view s, std::string_view whitespace)
{
auto i = s.find_first_not_of(whitespace);
if (i == s.npos) return "";
auto j = s.find_last_not_of(whitespace);
return std::string(s, i, j == s.npos ? j : j - i + 1);
}
std::string replaceStrings(
std::string res,
std::string_view from,
std::string_view to)
{
2020-11-10 15:59:03 +02:00
if (from.empty()) return res;
size_t pos = 0;
while ((pos = res.find(from, pos)) != std::string::npos) {
res.replace(pos, from.size(), to);
pos += to.size();
}
return res;
}
std::string rewriteStrings(std::string s, const StringMap & rewrites)
Allow content-addressable paths to have references This adds a command 'nix make-content-addressable' that rewrites the specified store paths into content-addressable paths. The advantage of such paths is that 1) they can be imported without signatures; 2) they can enable deduplication in cases where derivation changes do not cause output changes (apart from store path hashes). For example, $ nix make-content-addressable -r nixpkgs.cowsay rewrote '/nix/store/g1g31ah55xdia1jdqabv1imf6mcw0nb1-glibc-2.25-49' to '/nix/store/48jfj7bg78a8n4f2nhg269rgw1936vj4-glibc-2.25-49' ... rewrote '/nix/store/qbi6rzpk0bxjw8lw6azn2mc7ynnn455q-cowsay-3.03+dfsg1-16' to '/nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16' We can then copy the resulting closure to another store without signatures: $ nix copy --trusted-public-keys '' ---to ~/my-nix /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 In order to support self-references in content-addressable paths, these paths are hashed "modulo" self-references, meaning that self-references are zeroed out during hashing. Somewhat annoyingly, this means that the NAR hash stored in the Nix database is no longer necessarily equal to the output of "nix hash-path"; for content-addressable paths, you need to pass the --modulo flag: $ nix path-info --json /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 | jq -r .[].narHash sha256:0ri611gdilz2c9rsibqhsipbfs9vwcqvs811a52i2bnkhv7w9mgw $ nix hash-path --type sha256 --base32 /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 1ggznh07khq0hz6id09pqws3a8q9pn03ya3c03nwck1kwq8rclzs $ nix hash-path --type sha256 --base32 /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 --modulo iq6g2x4q62xp7y7493bibx0qn5w7xz67 0ri611gdilz2c9rsibqhsipbfs9vwcqvs811a52i2bnkhv7w9mgw
2018-03-30 01:56:13 +03:00
{
for (auto & i : rewrites) {
if (i.first == i.second) continue;
size_t j = 0;
while ((j = s.find(i.first, j)) != std::string::npos)
Allow content-addressable paths to have references This adds a command 'nix make-content-addressable' that rewrites the specified store paths into content-addressable paths. The advantage of such paths is that 1) they can be imported without signatures; 2) they can enable deduplication in cases where derivation changes do not cause output changes (apart from store path hashes). For example, $ nix make-content-addressable -r nixpkgs.cowsay rewrote '/nix/store/g1g31ah55xdia1jdqabv1imf6mcw0nb1-glibc-2.25-49' to '/nix/store/48jfj7bg78a8n4f2nhg269rgw1936vj4-glibc-2.25-49' ... rewrote '/nix/store/qbi6rzpk0bxjw8lw6azn2mc7ynnn455q-cowsay-3.03+dfsg1-16' to '/nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16' We can then copy the resulting closure to another store without signatures: $ nix copy --trusted-public-keys '' ---to ~/my-nix /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 In order to support self-references in content-addressable paths, these paths are hashed "modulo" self-references, meaning that self-references are zeroed out during hashing. Somewhat annoyingly, this means that the NAR hash stored in the Nix database is no longer necessarily equal to the output of "nix hash-path"; for content-addressable paths, you need to pass the --modulo flag: $ nix path-info --json /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 | jq -r .[].narHash sha256:0ri611gdilz2c9rsibqhsipbfs9vwcqvs811a52i2bnkhv7w9mgw $ nix hash-path --type sha256 --base32 /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 1ggznh07khq0hz6id09pqws3a8q9pn03ya3c03nwck1kwq8rclzs $ nix hash-path --type sha256 --base32 /nix/store/iq6g2x4q62xp7y7493bibx0qn5w7xz67-cowsay-3.03+dfsg1-16 --modulo iq6g2x4q62xp7y7493bibx0qn5w7xz67 0ri611gdilz2c9rsibqhsipbfs9vwcqvs811a52i2bnkhv7w9mgw
2018-03-30 01:56:13 +03:00
s.replace(j, i.first.size(), i.second);
}
return s;
}
bool hasPrefix(std::string_view s, std::string_view prefix)
{
2017-05-01 18:28:19 +03:00
return s.compare(0, prefix.size(), prefix) == 0;
}
bool hasSuffix(std::string_view s, std::string_view suffix)
{
return s.size() >= suffix.size()
&& s.substr(s.size() - suffix.size()) == suffix;
}
2016-09-14 15:42:15 +03:00
std::string toLower(const std::string & s)
{
std::string r(s);
for (auto & c : r)
c = std::tolower(c);
return r;
}
std::string shellEscape(const std::string_view s)
{
std::string r;
r.reserve(s.size() + 2);
r += "'";
for (auto & i : s)
if (i == '\'') r += "'\\''"; else r += i;
r += '\'';
return r;
}
/* Recreate the effect of the perl shellwords function, breaking up a
* string into arguments like a shell word, including escapes
*/
std::vector<std::string> shellwords2(const std::string & s)
{
std::regex whitespace("^(\\s+).*");
auto begin = s.cbegin();
std::vector<std::string> res;
std::string cur;
enum state {
sBegin,
sQuote
};
state st = sBegin;
auto it = begin;
for (; it != s.cend(); ++it) {
if (st == sBegin) {
std::smatch match;
if (regex_search(it, s.cend(), match, whitespace)) {
cur.append(begin, it);
res.push_back(cur);
cur.clear();
it = match[1].second;
begin = it;
}
}
switch (*it) {
case '"':
cur.append(begin, it);
begin = it + 1;
st = st == sBegin ? sQuote : sBegin;
break;
case '\\':
/* perl shellwords mostly just treats the next char as part of the string with no special processing */
cur.append(begin, it);
begin = ++it;
break;
}
}
cur.append(begin, it);
if (!cur.empty()) res.push_back(cur);
return res;
}
void ignoreException(Verbosity lvl)
{
/* Make sure no exceptions leave this function.
printError() also throws when remote is closed. */
try {
try {
throw;
} catch (std::exception & e) {
printMsg(lvl, "error (ignored): %1%", e.what());
}
} catch (...) { }
}
2014-08-20 17:01:16 +03:00
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2015-02-09 16:09:39 +02:00
std::string base64Encode(std::string_view s)
2015-02-09 16:09:39 +02:00
{
std::string res;
res.reserve((s.size() + 2) / 3 * 4);
2015-02-09 16:09:39 +02:00
int data = 0, nbits = 0;
for (char c : s) {
data = data << 8 | (unsigned char) c;
nbits += 8;
while (nbits >= 6) {
nbits -= 6;
res.push_back(base64Chars[data >> nbits & 0x3f]);
}
}
if (nbits) res.push_back(base64Chars[data << (6 - nbits) & 0x3f]);
while (res.size() % 4) res.push_back('=');
return res;
}
std::string base64Decode(std::string_view s)
2015-02-09 16:09:39 +02:00
{
constexpr char npos = -1;
constexpr std::array<char, 256> base64DecodeChars = [&]() {
std::array<char, 256> result{};
for (auto& c : result)
c = npos;
2015-02-09 16:09:39 +02:00
for (int i = 0; i < 64; i++)
result[base64Chars[i]] = i;
return result;
}();
2015-02-09 16:09:39 +02:00
std::string res;
// Some sequences are missing the padding consisting of up to two '='.
// vvv
res.reserve((s.size() + 2) / 4 * 3);
2015-02-09 16:09:39 +02:00
unsigned int d = 0, bits = 0;
for (char c : s) {
if (c == '=') break;
if (c == '\n') continue;
char digit = base64DecodeChars[(unsigned char) c];
if (digit == npos)
2020-07-02 00:32:06 +03:00
throw Error("invalid character in Base64 string: '%c'", c);
2015-02-09 16:09:39 +02:00
bits += 6;
d = d << 6 | digit;
if (bits >= 8) {
res.push_back(d >> (bits - 8) & 0xff);
bits -= 8;
}
}
return res;
}
std::string stripIndentation(std::string_view s)
{
size_t minIndent = 10000;
size_t curIndent = 0;
bool atStartOfLine = true;
for (auto & c : s) {
if (atStartOfLine && c == ' ')
curIndent++;
else if (c == '\n') {
if (atStartOfLine)
minIndent = std::max(minIndent, curIndent);
curIndent = 0;
atStartOfLine = true;
} else {
if (atStartOfLine) {
minIndent = std::min(minIndent, curIndent);
atStartOfLine = false;
}
}
}
std::string res;
size_t pos = 0;
while (pos < s.size()) {
auto eol = s.find('\n', pos);
if (eol == s.npos) eol = s.size();
if (eol - pos > minIndent)
res.append(s.substr(pos + minIndent, eol - pos - minIndent));
res.push_back('\n');
pos = eol + 1;
}
return res;
}
std::pair<std::string_view, std::string_view> getLine(std::string_view s)
{
auto newline = s.find('\n');
if (newline == s.npos) {
return {s, ""};
} else {
auto line = s.substr(0, newline);
if (!line.empty() && line[line.size() - 1] == '\r')
line = line.substr(0, line.size() - 1);
return {line, s.substr(newline + 1)};
}
}
std::string showBytes(uint64_t bytes)
2020-10-06 11:40:49 +03:00
{
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
}
}