util.{hh,cc}: Split out strings.{hh,cc}
Change-Id: I4f642d1046d56b5db26f1b0296ee16a0e02d444a
This commit is contained in:
parent
5b5a75979a
commit
b910551120
25 changed files with 517 additions and 489 deletions
|
@ -1,7 +1,7 @@
|
||||||
#include "crypto.hh"
|
#include "crypto.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "machines.hh"
|
#include "machines.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "names.hh"
|
#include "names.hh"
|
||||||
#include "util.hh"
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "util.hh"
|
|
||||||
#include "regex-combinators.hh"
|
#include "regex-combinators.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
#include "path-regex.hh"
|
#include "path-regex.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#include "environment-variables.hh"
|
#include "environment-variables.hh"
|
||||||
#include "ssh.hh"
|
#include "ssh.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "util.hh"
|
#include "logging.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
|
#include "logging.hh"
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
|
||||||
#include "cgroup.hh"
|
#include "cgroup.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "abstract-setting-to-json.hh"
|
#include "abstract-setting-to-json.hh"
|
||||||
#include "experimental-features.hh"
|
#include "experimental-features.hh"
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#include "config-impl.hh"
|
#include "config-impl.hh"
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "logging.hh"
|
#include "logging.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
# include <mach-o/dyld.h>
|
# include <mach-o/dyld.h>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "experimental-features.hh"
|
#include "experimental-features.hh"
|
||||||
// Required for instances of to_json and from_json for ExperimentalFeature
|
// Required for instances of to_json and from_json for ExperimentalFeature
|
||||||
#include "experimental-features-json.hh"
|
#include "experimental-features-json.hh"
|
||||||
|
#include "strings.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
|
@ -31,6 +31,7 @@ libutil_sources = files(
|
||||||
'shlex.cc',
|
'shlex.cc',
|
||||||
'signals.cc',
|
'signals.cc',
|
||||||
'source-path.cc',
|
'source-path.cc',
|
||||||
|
'strings.cc',
|
||||||
'suggestions.cc',
|
'suggestions.cc',
|
||||||
'tarfile.cc',
|
'tarfile.cc',
|
||||||
'terminal.cc',
|
'terminal.cc',
|
||||||
|
@ -96,6 +97,7 @@ libutil_headers = files(
|
||||||
'signals.hh',
|
'signals.hh',
|
||||||
'source-path.hh',
|
'source-path.hh',
|
||||||
'split.hh',
|
'split.hh',
|
||||||
|
'strings.hh',
|
||||||
'suggestions.hh',
|
'suggestions.hh',
|
||||||
'sync.hh',
|
'sync.hh',
|
||||||
'tarfile.hh',
|
'tarfile.hh',
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "logging.hh"
|
#include "logging.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "namespaces.hh"
|
#include "namespaces.hh"
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
namespace nix::regex {
|
namespace nix::regex {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "strings.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "file-descriptor.hh"
|
#include "file-descriptor.hh"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "shlex.hh"
|
#include "shlex.hh"
|
||||||
#include "util.hh"
|
#include "strings.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
232
src/libutil/strings.cc
Normal file
232
src/libutil/strings.cc
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::vector<char *> stringsToCharPtrs(const Strings & ss)
|
||||||
|
{
|
||||||
|
std::vector<char *> res;
|
||||||
|
for (auto & s : ss) res.push_back((char *) s.c_str());
|
||||||
|
res.push_back(0);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rewriter::Rewriter(std::map<std::string, std::string> rewrites)
|
||||||
|
: rewrites(std::move(rewrites))
|
||||||
|
{
|
||||||
|
for (const auto & [k, v] : this->rewrites) {
|
||||||
|
assert(!k.empty());
|
||||||
|
initials.push_back(k[0]);
|
||||||
|
}
|
||||||
|
std::ranges::sort(initials);
|
||||||
|
auto [firstDupe, end] = std::ranges::unique(initials);
|
||||||
|
initials.erase(firstDupe, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Rewriter::operator()(std::string s)
|
||||||
|
{
|
||||||
|
size_t j = 0;
|
||||||
|
while ((j = s.find_first_of(initials, j)) != std::string::npos) {
|
||||||
|
size_t skip = 1;
|
||||||
|
for (auto & [from, to] : rewrites) {
|
||||||
|
if (s.compare(j, from.size(), from) == 0) {
|
||||||
|
s.replace(j, from.size(), to);
|
||||||
|
skip = to.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += skip;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
std::string base64Encode(std::string_view s)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res.reserve((s.size() + 2) / 3 * 4);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
constexpr char npos = -1;
|
||||||
|
constexpr std::array<char, 256> base64DecodeChars = [&]() {
|
||||||
|
std::array<char, 256> result{};
|
||||||
|
for (auto& c : result)
|
||||||
|
c = npos;
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
result[base64Chars[i]] = i;
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (c == '=') break;
|
||||||
|
if (c == '\n') continue;
|
||||||
|
|
||||||
|
char digit = base64DecodeChars[(unsigned char) c];
|
||||||
|
if (digit == npos)
|
||||||
|
throw Error("invalid character in Base64 string: '%c'", c);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
256
src/libutil/strings.hh
Normal file
256
src/libutil/strings.hh
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "error.hh"
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree formatting.
|
||||||
|
*/
|
||||||
|
constexpr char treeConn[] = "├───";
|
||||||
|
constexpr char treeLast[] = "└───";
|
||||||
|
constexpr char treeLine[] = "│ ";
|
||||||
|
constexpr char treeNull[] = " ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a list of strings to a null-terminated vector of `char
|
||||||
|
* *`s. The result must not be accessed beyond the lifetime of the
|
||||||
|
* list of strings.
|
||||||
|
*/
|
||||||
|
std::vector<char *> stringsToCharPtrs(const Strings & ss);
|
||||||
|
|
||||||
|
|
||||||
|
MakeError(FormatError, Error);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String tokenizer.
|
||||||
|
*/
|
||||||
|
template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate the given strings with a separator between the
|
||||||
|
* elements.
|
||||||
|
*/
|
||||||
|
template<class C>
|
||||||
|
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();
|
||||||
|
std::string s;
|
||||||
|
s.reserve(size);
|
||||||
|
for (auto & i : ss) {
|
||||||
|
if (s.size() != 0) s += sep;
|
||||||
|
s += i;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ... Parts>
|
||||||
|
auto concatStrings(Parts && ... parts)
|
||||||
|
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string>
|
||||||
|
{
|
||||||
|
std::string_view views[sizeof...(parts)] = { parts... };
|
||||||
|
return concatStringsSep({}, views);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add quotes around a collection of strings.
|
||||||
|
*/
|
||||||
|
template<class C> Strings quoteStrings(const C & c)
|
||||||
|
{
|
||||||
|
Strings res;
|
||||||
|
for (auto & s : c)
|
||||||
|
res.push_back("'" + s + "'");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove trailing whitespace from a string.
|
||||||
|
*
|
||||||
|
* \todo return std::string_view.
|
||||||
|
*/
|
||||||
|
std::string chomp(std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove whitespace from the start and end of a string.
|
||||||
|
*/
|
||||||
|
std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace all occurrences of a string inside another string.
|
||||||
|
*/
|
||||||
|
std::string replaceStrings(
|
||||||
|
std::string s,
|
||||||
|
std::string_view from,
|
||||||
|
std::string_view to);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrites a string given a map of replacements, applying the replacements in
|
||||||
|
* sorted order, only once, considering only the strings appearing in the input
|
||||||
|
* string in performing replacement.
|
||||||
|
*
|
||||||
|
* - Replacements are not performed on intermediate strings. That is, for an input
|
||||||
|
* `"abb"` with replacements `{"ab" -> "ba"}`, the result is `"bab"`.
|
||||||
|
* - Transitive replacements are not performed. For example, for the input `"abcde"`
|
||||||
|
* with replacements `{"a" -> "b", "b" -> "c", "e" -> "b"}`, the result is
|
||||||
|
* `"bccdb"`.
|
||||||
|
*/
|
||||||
|
class Rewriter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string initials;
|
||||||
|
std::map<std::string, std::string> rewrites;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Rewriter(std::map<std::string, std::string> rewrites);
|
||||||
|
|
||||||
|
std::string operator()(std::string s);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string rewriteStrings(std::string s, const StringMap & rewrites)
|
||||||
|
{
|
||||||
|
return Rewriter(rewrites)(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a string into an integer.
|
||||||
|
*/
|
||||||
|
template<class N>
|
||||||
|
std::optional<N> string2Int(const std::string_view s)
|
||||||
|
{
|
||||||
|
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
||||||
|
return std::nullopt;
|
||||||
|
try {
|
||||||
|
return boost::lexical_cast<N>(s.data(), s.size());
|
||||||
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
||||||
|
* 'T' denoting a binary unit prefix.
|
||||||
|
*/
|
||||||
|
template<class N>
|
||||||
|
N string2IntWithUnitPrefix(std::string_view s)
|
||||||
|
{
|
||||||
|
N multiplier = 1;
|
||||||
|
if (!s.empty()) {
|
||||||
|
char u = std::toupper(*s.rbegin());
|
||||||
|
if (std::isalpha(u)) {
|
||||||
|
if (u == 'K') multiplier = 1ULL << 10;
|
||||||
|
else if (u == 'M') multiplier = 1ULL << 20;
|
||||||
|
else if (u == 'G') multiplier = 1ULL << 30;
|
||||||
|
else if (u == 'T') multiplier = 1ULL << 40;
|
||||||
|
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||||
|
s.remove_suffix(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto n = string2Int<N>(s))
|
||||||
|
return *n * multiplier;
|
||||||
|
throw UsageError("'%s' is not an integer", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a string into a float.
|
||||||
|
*/
|
||||||
|
template<class N>
|
||||||
|
std::optional<N> string2Float(const std::string_view s)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return boost::lexical_cast<N>(s.data(), s.size());
|
||||||
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a little-endian integer to host order.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
T readLittleEndian(unsigned char * p)
|
||||||
|
{
|
||||||
|
T x = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(x); ++i, ++p) {
|
||||||
|
x |= ((T) *p) << (i * 8);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string to lower case.
|
||||||
|
*/
|
||||||
|
std::string toLower(const std::string & s);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape a string as a shell word.
|
||||||
|
*/
|
||||||
|
std::string shellEscape(const std::string_view s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 encoding/decoding.
|
||||||
|
*/
|
||||||
|
std::string base64Encode(std::string_view s);
|
||||||
|
std::string base64Decode(std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove common leading whitespace from the lines in the string
|
||||||
|
* 's'. For example, if every line is indented by at least 3 spaces,
|
||||||
|
* then we remove 3 spaces from the start of every line.
|
||||||
|
*/
|
||||||
|
std::string stripIndentation(std::string_view s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the prefix of 's' up to and excluding the next line break (LF
|
||||||
|
* optionally preceded by CR), and the remainder following the line
|
||||||
|
* break.
|
||||||
|
*/
|
||||||
|
std::pair<std::string_view, std::string_view> getLine(std::string_view s);
|
||||||
|
|
||||||
|
std::string showBytes(uint64_t bytes);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide an addition operator between strings and string_views
|
||||||
|
* inexplicably omitted from the standard library.
|
||||||
|
*/
|
||||||
|
inline std::string operator + (const std::string & s1, std::string_view s2)
|
||||||
|
{
|
||||||
|
auto s = s1;
|
||||||
|
s.append(s2);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string operator + (std::string && s, std::string_view s2)
|
||||||
|
{
|
||||||
|
s.append(s2);
|
||||||
|
return std::move(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string operator + (std::string_view s1, const char * s2)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
s.reserve(s1.size() + strlen(s2));
|
||||||
|
s.append(s1);
|
||||||
|
s.append(s2);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
#include "unix-domain-socket.hh"
|
#include "unix-domain-socket.hh"
|
||||||
#include "util.hh"
|
#include "strings.hh"
|
||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "url.hh"
|
#include "url.hh"
|
||||||
#include "url-parts.hh"
|
#include "url-parts.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "split.hh"
|
#include "split.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
|
#include "strings.hh"
|
||||||
#include "current-process.hh"
|
#include "current-process.hh"
|
||||||
|
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
|
@ -164,116 +165,6 @@ Path createNixStateDir()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<char *> stringsToCharPtrs(const Strings & ss)
|
|
||||||
{
|
|
||||||
std::vector<char *> res;
|
|
||||||
for (auto & s : ss) res.push_back((char *) s.c_str());
|
|
||||||
res.push_back(0);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Rewriter::Rewriter(std::map<std::string, std::string> rewrites)
|
|
||||||
: rewrites(std::move(rewrites))
|
|
||||||
{
|
|
||||||
for (const auto & [k, v] : this->rewrites) {
|
|
||||||
assert(!k.empty());
|
|
||||||
initials.push_back(k[0]);
|
|
||||||
}
|
|
||||||
std::ranges::sort(initials);
|
|
||||||
auto [firstDupe, end] = std::ranges::unique(initials);
|
|
||||||
initials.erase(firstDupe, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Rewriter::operator()(std::string s)
|
|
||||||
{
|
|
||||||
size_t j = 0;
|
|
||||||
while ((j = s.find_first_of(initials, j)) != std::string::npos) {
|
|
||||||
size_t skip = 1;
|
|
||||||
for (auto & [from, to] : rewrites) {
|
|
||||||
if (s.compare(j, from.size(), from) == 0) {
|
|
||||||
s.replace(j, from.size(), to);
|
|
||||||
skip = to.size();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j += skip;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ignoreException(Verbosity lvl)
|
void ignoreException(Verbosity lvl)
|
||||||
|
@ -289,120 +180,6 @@ void ignoreException(Verbosity lvl)
|
||||||
} catch (...) { }
|
} catch (...) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
|
|
||||||
std::string base64Encode(std::string_view s)
|
|
||||||
{
|
|
||||||
std::string res;
|
|
||||||
res.reserve((s.size() + 2) / 3 * 4);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
constexpr char npos = -1;
|
|
||||||
constexpr std::array<char, 256> base64DecodeChars = [&]() {
|
|
||||||
std::array<char, 256> result{};
|
|
||||||
for (auto& c : result)
|
|
||||||
c = npos;
|
|
||||||
for (int i = 0; i < 64; i++)
|
|
||||||
result[base64Chars[i]] = i;
|
|
||||||
return result;
|
|
||||||
}();
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (c == '=') break;
|
|
||||||
if (c == '\n') continue;
|
|
||||||
|
|
||||||
char digit = base64DecodeChars[(unsigned char) c];
|
|
||||||
if (digit == npos)
|
|
||||||
throw Error("invalid character in Base64 string: '%c'", c);
|
|
||||||
|
|
||||||
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)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -460,9 +237,4 @@ void unshareFilesystem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string showBytes(uint64_t bytes)
|
|
||||||
{
|
|
||||||
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,193 +106,6 @@ void restoreMountNamespace();
|
||||||
void unshareFilesystem();
|
void unshareFilesystem();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a list of strings to a null-terminated vector of `char
|
|
||||||
* *`s. The result must not be accessed beyond the lifetime of the
|
|
||||||
* list of strings.
|
|
||||||
*/
|
|
||||||
std::vector<char *> stringsToCharPtrs(const Strings & ss);
|
|
||||||
|
|
||||||
|
|
||||||
MakeError(FormatError, Error);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* String tokenizer.
|
|
||||||
*/
|
|
||||||
template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Concatenate the given strings with a separator between the
|
|
||||||
* elements.
|
|
||||||
*/
|
|
||||||
template<class C>
|
|
||||||
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();
|
|
||||||
std::string s;
|
|
||||||
s.reserve(size);
|
|
||||||
for (auto & i : ss) {
|
|
||||||
if (s.size() != 0) s += sep;
|
|
||||||
s += i;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ... Parts>
|
|
||||||
auto concatStrings(Parts && ... parts)
|
|
||||||
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string>
|
|
||||||
{
|
|
||||||
std::string_view views[sizeof...(parts)] = { parts... };
|
|
||||||
return concatStringsSep({}, views);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add quotes around a collection of strings.
|
|
||||||
*/
|
|
||||||
template<class C> Strings quoteStrings(const C & c)
|
|
||||||
{
|
|
||||||
Strings res;
|
|
||||||
for (auto & s : c)
|
|
||||||
res.push_back("'" + s + "'");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove trailing whitespace from a string.
|
|
||||||
*
|
|
||||||
* \todo return std::string_view.
|
|
||||||
*/
|
|
||||||
std::string chomp(std::string_view s);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove whitespace from the start and end of a string.
|
|
||||||
*/
|
|
||||||
std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t");
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace all occurrences of a string inside another string.
|
|
||||||
*/
|
|
||||||
std::string replaceStrings(
|
|
||||||
std::string s,
|
|
||||||
std::string_view from,
|
|
||||||
std::string_view to);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rewrites a string given a map of replacements, applying the replacements in
|
|
||||||
* sorted order, only once, considering only the strings appearing in the input
|
|
||||||
* string in performing replacement.
|
|
||||||
*
|
|
||||||
* - Replacements are not performed on intermediate strings. That is, for an input
|
|
||||||
* `"abb"` with replacements `{"ab" -> "ba"}`, the result is `"bab"`.
|
|
||||||
* - Transitive replacements are not performed. For example, for the input `"abcde"`
|
|
||||||
* with replacements `{"a" -> "b", "b" -> "c", "e" -> "b"}`, the result is
|
|
||||||
* `"bccdb"`.
|
|
||||||
*/
|
|
||||||
class Rewriter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string initials;
|
|
||||||
std::map<std::string, std::string> rewrites;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Rewriter(std::map<std::string, std::string> rewrites);
|
|
||||||
|
|
||||||
std::string operator()(std::string s);
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::string rewriteStrings(std::string s, const StringMap & rewrites)
|
|
||||||
{
|
|
||||||
return Rewriter(rewrites)(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a string into an integer.
|
|
||||||
*/
|
|
||||||
template<class N>
|
|
||||||
std::optional<N> string2Int(const std::string_view s)
|
|
||||||
{
|
|
||||||
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
|
||||||
return std::nullopt;
|
|
||||||
try {
|
|
||||||
return boost::lexical_cast<N>(s.data(), s.size());
|
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
|
||||||
* 'T' denoting a binary unit prefix.
|
|
||||||
*/
|
|
||||||
template<class N>
|
|
||||||
N string2IntWithUnitPrefix(std::string_view s)
|
|
||||||
{
|
|
||||||
N multiplier = 1;
|
|
||||||
if (!s.empty()) {
|
|
||||||
char u = std::toupper(*s.rbegin());
|
|
||||||
if (std::isalpha(u)) {
|
|
||||||
if (u == 'K') multiplier = 1ULL << 10;
|
|
||||||
else if (u == 'M') multiplier = 1ULL << 20;
|
|
||||||
else if (u == 'G') multiplier = 1ULL << 30;
|
|
||||||
else if (u == 'T') multiplier = 1ULL << 40;
|
|
||||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
|
||||||
s.remove_suffix(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (auto n = string2Int<N>(s))
|
|
||||||
return *n * multiplier;
|
|
||||||
throw UsageError("'%s' is not an integer", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a string into a float.
|
|
||||||
*/
|
|
||||||
template<class N>
|
|
||||||
std::optional<N> string2Float(const std::string_view s)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return boost::lexical_cast<N>(s.data(), s.size());
|
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a little-endian integer to host order.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
T readLittleEndian(unsigned char * p)
|
|
||||||
{
|
|
||||||
T x = 0;
|
|
||||||
for (size_t i = 0; i < sizeof(x); ++i, ++p) {
|
|
||||||
x |= ((T) *p) << (i * 8);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a string to lower case.
|
|
||||||
*/
|
|
||||||
std::string toLower(const std::string & s);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape a string as a shell word.
|
|
||||||
*/
|
|
||||||
std::string shellEscape(const std::string_view s);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception handling in destructors: print an error message, then
|
* Exception handling in destructors: print an error message, then
|
||||||
* ignore the exception.
|
* ignore the exception.
|
||||||
|
@ -301,38 +114,6 @@ void ignoreException(Verbosity lvl = lvlError);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tree formatting.
|
|
||||||
*/
|
|
||||||
constexpr char treeConn[] = "├───";
|
|
||||||
constexpr char treeLast[] = "└───";
|
|
||||||
constexpr char treeLine[] = "│ ";
|
|
||||||
constexpr char treeNull[] = " ";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base64 encoding/decoding.
|
|
||||||
*/
|
|
||||||
std::string base64Encode(std::string_view s);
|
|
||||||
std::string base64Decode(std::string_view s);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove common leading whitespace from the lines in the string
|
|
||||||
* 's'. For example, if every line is indented by at least 3 spaces,
|
|
||||||
* then we remove 3 spaces from the start of every line.
|
|
||||||
*/
|
|
||||||
std::string stripIndentation(std::string_view s);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the prefix of 's' up to and excluding the next line break (LF
|
|
||||||
* optionally preceded by CR), and the remainder following the line
|
|
||||||
* break.
|
|
||||||
*/
|
|
||||||
std::pair<std::string_view, std::string_view> getLine(std::string_view s);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a value for the specified key from an associate container.
|
* Get a value for the specified key from an associate container.
|
||||||
*/
|
*/
|
||||||
|
@ -443,33 +224,4 @@ template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||||
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||||
|
|
||||||
|
|
||||||
std::string showBytes(uint64_t bytes);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide an addition operator between strings and string_views
|
|
||||||
* inexplicably omitted from the standard library.
|
|
||||||
*/
|
|
||||||
inline std::string operator + (const std::string & s1, std::string_view s2)
|
|
||||||
{
|
|
||||||
auto s = s1;
|
|
||||||
s.append(s2);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string operator + (std::string && s, std::string_view s2)
|
|
||||||
{
|
|
||||||
s.append(s2);
|
|
||||||
return std::move(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string operator + (std::string_view s1, const char * s2)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
s.reserve(s1.size() + strlen(s2));
|
|
||||||
s.append(s1);
|
|
||||||
s.append(s2);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "tests/cli-literate-parser.hh"
|
#include "tests/cli-literate-parser.hh"
|
||||||
#include "tests/terminal-code-eater.hh"
|
#include "tests/terminal-code-eater.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "test-session.hh"
|
#include "test-session.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "escape-char.hh"
|
#include "escape-char.hh"
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "machines.hh"
|
#include "machines.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "strings.hh"
|
||||||
#include "tests/test-data.hh"
|
#include "tests/test-data.hh"
|
||||||
|
|
||||||
#include <gmock/gmock-matchers.h>
|
#include <gmock/gmock-matchers.h>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "shlex.hh"
|
#include "shlex.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "strings.hh"
|
||||||
|
|
||||||
static constexpr const bool DEBUG_PARSER = false;
|
static constexpr const bool DEBUG_PARSER = false;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "util.hh"
|
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
|
#include "strings.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "terminal.hh"
|
#include "terminal.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue