mirror of
https://github.com/NixOS/nix
synced 2025-06-29 10:31:15 +02:00
Merge branch 'hash-always-has-type' of github.com:obsidiansystems/nix into better-ca-parse-errors
This commit is contained in:
commit
cc0d77f8c9
138 changed files with 8552 additions and 2192 deletions
|
@ -11,7 +11,7 @@ namespace nix {
|
|||
#define ANSI_GREEN "\e[32;1m"
|
||||
#define ANSI_YELLOW "\e[33;1m"
|
||||
#define ANSI_BLUE "\e[34;1m"
|
||||
#define ANSI_MAGENTA "\e[35m;1m"
|
||||
#define ANSI_CYAN "\e[36m;1m"
|
||||
#define ANSI_MAGENTA "\e[35;1m"
|
||||
#define ANSI_CYAN "\e[36;1m"
|
||||
|
||||
}
|
||||
|
|
|
@ -63,11 +63,12 @@ struct ParseSink
|
|||
virtual void createSymlink(const Path & path, const string & target) { };
|
||||
};
|
||||
|
||||
struct TeeSink : ParseSink
|
||||
struct TeeParseSink : ParseSink
|
||||
{
|
||||
StringSink saved;
|
||||
TeeSource source;
|
||||
|
||||
TeeSink(Source & source) : source(source) { }
|
||||
TeeParseSink(Source & source) : source(source, saved) { }
|
||||
};
|
||||
|
||||
void parseDump(ParseSink & sink, Source & source);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "args.hh"
|
||||
#include "hash.hh"
|
||||
|
||||
#include <glob.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
void Args::addFlag(Flag && flag_)
|
||||
|
@ -13,6 +15,20 @@ void Args::addFlag(Flag && flag_)
|
|||
if (flag->shortName) shortFlags[flag->shortName] = flag;
|
||||
}
|
||||
|
||||
bool pathCompletions = false;
|
||||
std::shared_ptr<std::set<std::string>> completions;
|
||||
|
||||
std::string completionMarker = "___COMPLETE___";
|
||||
|
||||
std::optional<std::string> needsCompletion(std::string_view s)
|
||||
{
|
||||
if (!completions) return {};
|
||||
auto i = s.find(completionMarker);
|
||||
if (i != std::string::npos)
|
||||
return std::string(s.begin(), i);
|
||||
return {};
|
||||
}
|
||||
|
||||
void Args::parseCmdline(const Strings & _cmdline)
|
||||
{
|
||||
Strings pendingArgs;
|
||||
|
@ -20,6 +36,14 @@ void Args::parseCmdline(const Strings & _cmdline)
|
|||
|
||||
Strings cmdline(_cmdline);
|
||||
|
||||
if (auto s = getEnv("NIX_GET_COMPLETIONS")) {
|
||||
size_t n = std::stoi(*s);
|
||||
assert(n > 0 && n <= cmdline.size());
|
||||
*std::next(cmdline.begin(), n - 1) += completionMarker;
|
||||
completions = std::make_shared<decltype(completions)::element_type>();
|
||||
verbosity = lvlError;
|
||||
}
|
||||
|
||||
for (auto pos = cmdline.begin(); pos != cmdline.end(); ) {
|
||||
|
||||
auto arg = *pos;
|
||||
|
@ -63,7 +87,7 @@ void Args::printHelp(const string & programName, std::ostream & out)
|
|||
for (auto & exp : expectedArgs) {
|
||||
std::cout << renderLabels({exp.label});
|
||||
// FIXME: handle arity > 1
|
||||
if (exp.arity == 0) std::cout << "...";
|
||||
if (exp.handler.arity == ArityAny) std::cout << "...";
|
||||
if (exp.optional) std::cout << "?";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
@ -99,18 +123,32 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
auto process = [&](const std::string & name, const Flag & flag) -> bool {
|
||||
++pos;
|
||||
std::vector<std::string> args;
|
||||
bool anyCompleted = false;
|
||||
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
||||
if (pos == end) {
|
||||
if (flag.handler.arity == ArityAny) break;
|
||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||
}
|
||||
if (flag.completer)
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
anyCompleted = true;
|
||||
flag.completer(n, *prefix);
|
||||
}
|
||||
args.push_back(*pos++);
|
||||
}
|
||||
flag.handler.fun(std::move(args));
|
||||
if (!anyCompleted)
|
||||
flag.handler.fun(std::move(args));
|
||||
return true;
|
||||
};
|
||||
|
||||
if (string(*pos, 0, 2) == "--") {
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
for (auto & [name, flag] : longFlags) {
|
||||
if (!hiddenCategories.count(flag->category)
|
||||
&& hasPrefix(name, std::string(*prefix, 2)))
|
||||
completions->insert("--" + name);
|
||||
}
|
||||
}
|
||||
auto i = longFlags.find(string(*pos, 2));
|
||||
if (i == longFlags.end()) return false;
|
||||
return process("--" + i->first, *i->second);
|
||||
|
@ -123,6 +161,14 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
return process(std::string("-") + c, *i->second);
|
||||
}
|
||||
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
if (prefix == "-") {
|
||||
completions->insert("--");
|
||||
for (auto & [flag, _] : shortFlags)
|
||||
completions->insert(std::string("-") + flag);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -138,12 +184,17 @@ bool Args::processArgs(const Strings & args, bool finish)
|
|||
|
||||
bool res = false;
|
||||
|
||||
if ((exp.arity == 0 && finish) ||
|
||||
(exp.arity > 0 && args.size() == exp.arity))
|
||||
if ((exp.handler.arity == ArityAny && finish) ||
|
||||
(exp.handler.arity != ArityAny && args.size() == exp.handler.arity))
|
||||
{
|
||||
std::vector<std::string> ss;
|
||||
for (auto & s : args) ss.push_back(s);
|
||||
exp.handler(std::move(ss));
|
||||
for (const auto &[n, s] : enumerate(args)) {
|
||||
ss.push_back(s);
|
||||
if (exp.completer)
|
||||
if (auto prefix = needsCompletion(s))
|
||||
exp.completer(n, *prefix);
|
||||
}
|
||||
exp.handler.fun(ss);
|
||||
expectedArgs.pop_front();
|
||||
res = true;
|
||||
}
|
||||
|
@ -154,6 +205,13 @@ bool Args::processArgs(const Strings & args, bool finish)
|
|||
return res;
|
||||
}
|
||||
|
||||
static void hashTypeCompleter(size_t index, std::string_view prefix)
|
||||
{
|
||||
for (auto & type : hashTypes)
|
||||
if (hasPrefix(type, prefix))
|
||||
completions->insert(type);
|
||||
}
|
||||
|
||||
Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
|
||||
{
|
||||
return Flag {
|
||||
|
@ -162,7 +220,8 @@ Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
|
|||
.labels = {"hash-algo"},
|
||||
.handler = {[ht](std::string s) {
|
||||
*ht = parseHashType(s);
|
||||
}}
|
||||
}},
|
||||
.completer = hashTypeCompleter
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -174,10 +233,42 @@ Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, std::optional<
|
|||
.labels = {"hash-algo"},
|
||||
.handler = {[oht](std::string s) {
|
||||
*oht = std::optional<HashType> { parseHashType(s) };
|
||||
}}
|
||||
}},
|
||||
.completer = hashTypeCompleter
|
||||
};
|
||||
}
|
||||
|
||||
static void completePath(std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
pathCompletions = true;
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
if (onlyDirs)
|
||||
flags |= GLOB_ONLYDIR;
|
||||
#endif
|
||||
if (glob((std::string(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
||||
for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
|
||||
if (onlyDirs) {
|
||||
auto st = lstat(globbuf.gl_pathv[i]);
|
||||
if (!S_ISDIR(st.st_mode)) continue;
|
||||
}
|
||||
completions->insert(globbuf.gl_pathv[i]);
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
}
|
||||
|
||||
void completePath(size_t, std::string_view prefix)
|
||||
{
|
||||
completePath(prefix, false);
|
||||
}
|
||||
|
||||
void completeDir(size_t, std::string_view prefix)
|
||||
{
|
||||
completePath(prefix, true);
|
||||
}
|
||||
|
||||
Strings argvToStrings(int argc, char * * argv)
|
||||
{
|
||||
Strings args;
|
||||
|
@ -225,18 +316,26 @@ void Command::printHelp(const string & programName, std::ostream & out)
|
|||
MultiCommand::MultiCommand(const Commands & commands)
|
||||
: commands(commands)
|
||||
{
|
||||
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
|
||||
assert(!command);
|
||||
auto cmd = ss[0];
|
||||
if (auto alias = get(deprecatedAliases, cmd)) {
|
||||
warn("'%s' is a deprecated alias for '%s'", cmd, *alias);
|
||||
cmd = *alias;
|
||||
}
|
||||
auto i = commands.find(cmd);
|
||||
if (i == commands.end())
|
||||
throw UsageError("'%s' is not a recognised command", cmd);
|
||||
command = {cmd, i->second()};
|
||||
}});
|
||||
expectArgs({
|
||||
.label = "command",
|
||||
.optional = true,
|
||||
.handler = {[=](std::string s) {
|
||||
assert(!command);
|
||||
if (auto alias = get(deprecatedAliases, s)) {
|
||||
warn("'%s' is a deprecated alias for '%s'", s, *alias);
|
||||
s = *alias;
|
||||
}
|
||||
if (auto prefix = needsCompletion(s)) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, *prefix))
|
||||
completions->insert(name);
|
||||
}
|
||||
auto i = commands.find(s);
|
||||
if (i == commands.end())
|
||||
throw UsageError("'%s' is not a recognised command", s);
|
||||
command = {s, i->second()};
|
||||
}}
|
||||
});
|
||||
|
||||
categories[Command::catDefault] = "Available commands";
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
MakeError(UsageError, Error);
|
||||
|
||||
enum HashType : char;
|
||||
|
||||
class Args
|
||||
|
@ -28,61 +26,67 @@ protected:
|
|||
|
||||
static const size_t ArityAny = std::numeric_limits<size_t>::max();
|
||||
|
||||
struct Handler
|
||||
{
|
||||
std::function<void(std::vector<std::string>)> fun;
|
||||
size_t arity;
|
||||
|
||||
Handler() {}
|
||||
|
||||
Handler(std::function<void(std::vector<std::string>)> && fun)
|
||||
: fun(std::move(fun))
|
||||
, arity(ArityAny)
|
||||
{ }
|
||||
|
||||
Handler(std::function<void()> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); })
|
||||
, arity(0)
|
||||
{ }
|
||||
|
||||
Handler(std::function<void(std::string)> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||
handler(std::move(ss[0]));
|
||||
})
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
Handler(std::function<void(std::string, std::string)> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||
handler(std::move(ss[0]), std::move(ss[1]));
|
||||
})
|
||||
, arity(2)
|
||||
{ }
|
||||
|
||||
Handler(std::vector<std::string> * dest)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = ss; })
|
||||
, arity(ArityAny)
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest, const T & val)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = val; })
|
||||
, arity(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
/* Flags. */
|
||||
struct Flag
|
||||
{
|
||||
typedef std::shared_ptr<Flag> ptr;
|
||||
|
||||
struct Handler
|
||||
{
|
||||
std::function<void(std::vector<std::string>)> fun;
|
||||
size_t arity;
|
||||
|
||||
Handler() {}
|
||||
|
||||
Handler(std::function<void(std::vector<std::string>)> && fun)
|
||||
: fun(std::move(fun))
|
||||
, arity(ArityAny)
|
||||
{ }
|
||||
|
||||
Handler(std::function<void()> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); })
|
||||
, arity(0)
|
||||
{ }
|
||||
|
||||
Handler(std::function<void(std::string)> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||
handler(std::move(ss[0]));
|
||||
})
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
Handler(std::function<void(std::string, std::string)> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||
handler(std::move(ss[0]), std::move(ss[1]));
|
||||
})
|
||||
, arity(2)
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest, const T & val)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = val; })
|
||||
, arity(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
std::string longName;
|
||||
char shortName = 0;
|
||||
std::string description;
|
||||
std::string category;
|
||||
Strings labels;
|
||||
Handler handler;
|
||||
std::function<void(size_t, std::string_view)> completer;
|
||||
|
||||
static Flag mkHashTypeFlag(std::string && longName, HashType * ht);
|
||||
static Flag mkHashTypeOptFlag(std::string && longName, std::optional<HashType> * oht);
|
||||
|
@ -99,9 +103,9 @@ protected:
|
|||
struct ExpectedArg
|
||||
{
|
||||
std::string label;
|
||||
size_t arity; // 0 = any
|
||||
bool optional;
|
||||
std::function<void(std::vector<std::string>)> handler;
|
||||
bool optional = false;
|
||||
Handler handler;
|
||||
std::function<void(size_t, std::string_view)> completer;
|
||||
};
|
||||
|
||||
std::list<ExpectedArg> expectedArgs;
|
||||
|
@ -175,20 +179,28 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
void expectArgs(ExpectedArg && arg)
|
||||
{
|
||||
expectedArgs.emplace_back(std::move(arg));
|
||||
}
|
||||
|
||||
/* Expect a string argument. */
|
||||
void expectArg(const std::string & label, string * dest, bool optional = false)
|
||||
{
|
||||
expectedArgs.push_back(ExpectedArg{label, 1, optional, [=](std::vector<std::string> ss) {
|
||||
*dest = ss[0];
|
||||
}});
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.optional = true,
|
||||
.handler = {dest}
|
||||
});
|
||||
}
|
||||
|
||||
/* Expect 0 or more arguments. */
|
||||
void expectArgs(const std::string & label, std::vector<std::string> * dest)
|
||||
{
|
||||
expectedArgs.push_back(ExpectedArg{label, 0, false, [=](std::vector<std::string> ss) {
|
||||
*dest = std::move(ss);
|
||||
}});
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.handler = {dest}
|
||||
});
|
||||
}
|
||||
|
||||
friend class MultiCommand;
|
||||
|
@ -259,4 +271,13 @@ typedef std::vector<std::pair<std::string, std::string>> Table2;
|
|||
|
||||
void printTable(std::ostream & out, const Table2 & table);
|
||||
|
||||
extern std::shared_ptr<std::set<std::string>> completions;
|
||||
extern bool pathCompletions;
|
||||
|
||||
std::optional<std::string> needsCompletion(std::string_view s);
|
||||
|
||||
void completePath(size_t, std::string_view prefix);
|
||||
|
||||
void completeDir(size_t, std::string_view prefix);
|
||||
|
||||
}
|
||||
|
|
|
@ -191,6 +191,7 @@ public:
|
|||
}
|
||||
|
||||
MakeError(Error, BaseError);
|
||||
MakeError(UsageError, Error);
|
||||
|
||||
class SysError : public Error
|
||||
{
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "archive.hh"
|
||||
#include "parser.hh"
|
||||
#include "util.hh"
|
||||
#include "istringstream_nocopy.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -17,6 +16,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static size_t regularHashSize(HashType type) {
|
||||
switch (type) {
|
||||
case htMD5: return md5HashSize;
|
||||
|
@ -27,6 +27,10 @@ static size_t regularHashSize(HashType type) {
|
|||
abort();
|
||||
}
|
||||
|
||||
|
||||
std::set<std::string> hashTypes = { "md5", "sha1", "sha256", "sha512" };
|
||||
|
||||
|
||||
Hash::Hash(HashType type) : type(type)
|
||||
{
|
||||
hashSize = regularHashSize(type);
|
||||
|
|
|
@ -18,6 +18,8 @@ const int sha1HashSize = 20;
|
|||
const int sha256HashSize = 32;
|
||||
const int sha512HashSize = 64;
|
||||
|
||||
extern std::set<std::string> hashTypes;
|
||||
|
||||
extern const string base32Chars;
|
||||
|
||||
enum Base : int { Base64, Base32, Base16, SRI };
|
||||
|
@ -124,6 +126,7 @@ Hash compressHash(const Hash & hash, unsigned int newSize);
|
|||
|
||||
/* Parse a string representing a hash type. */
|
||||
HashType parseHashType(std::string_view s);
|
||||
|
||||
/* Will return nothing on parse error */
|
||||
std::optional<HashType> parseHashTypeOpt(std::string_view s);
|
||||
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
/* This file provides a variant of std::istringstream that doesn't
|
||||
copy its string argument. This is useful for large strings. The
|
||||
caller must ensure that the string object is not destroyed while
|
||||
it's referenced by this object. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT>>
|
||||
class basic_istringbuf_nocopy : public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
public:
|
||||
typedef std::basic_string<CharT, Traits, Allocator> string_type;
|
||||
|
||||
typedef typename std::basic_streambuf<CharT, Traits>::off_type off_type;
|
||||
|
||||
typedef typename std::basic_streambuf<CharT, Traits>::pos_type pos_type;
|
||||
|
||||
typedef typename std::basic_streambuf<CharT, Traits>::int_type int_type;
|
||||
|
||||
typedef typename std::basic_streambuf<CharT, Traits>::traits_type traits_type;
|
||||
|
||||
private:
|
||||
const string_type & s;
|
||||
|
||||
off_type off;
|
||||
|
||||
public:
|
||||
basic_istringbuf_nocopy(const string_type & s) : s{s}, off{0}
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which)
|
||||
{
|
||||
if (which & std::ios_base::in) {
|
||||
this->off = dir == std::ios_base::beg
|
||||
? off
|
||||
: (dir == std::ios_base::end
|
||||
? s.size() + off
|
||||
: this->off + off);
|
||||
}
|
||||
return pos_type(this->off);
|
||||
}
|
||||
|
||||
pos_type seekpos(pos_type pos, std::ios_base::openmode which)
|
||||
{
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
std::streamsize showmanyc()
|
||||
{
|
||||
return s.size() - off;
|
||||
}
|
||||
|
||||
int_type underflow()
|
||||
{
|
||||
if (typename string_type::size_type(off) == s.size())
|
||||
return traits_type::eof();
|
||||
return traits_type::to_int_type(s[off]);
|
||||
}
|
||||
|
||||
int_type uflow()
|
||||
{
|
||||
if (typename string_type::size_type(off) == s.size())
|
||||
return traits_type::eof();
|
||||
return traits_type::to_int_type(s[off++]);
|
||||
}
|
||||
|
||||
int_type pbackfail(int_type ch)
|
||||
{
|
||||
if (off == 0 || (ch != traits_type::eof() && ch != s[off - 1]))
|
||||
return traits_type::eof();
|
||||
|
||||
return traits_type::to_int_type(s[--off]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT>>
|
||||
class basic_istringstream_nocopy : public std::basic_iostream<CharT, Traits>
|
||||
{
|
||||
typedef basic_istringbuf_nocopy<CharT, Traits, Allocator> buf_type;
|
||||
buf_type buf;
|
||||
public:
|
||||
basic_istringstream_nocopy(const typename buf_type::string_type & s) :
|
||||
std::basic_iostream<CharT, Traits>(&buf), buf(s) {};
|
||||
};
|
||||
|
||||
typedef basic_istringstream_nocopy<char> istringstream_nocopy;
|
|
@ -166,17 +166,30 @@ struct StringSource : Source
|
|||
};
|
||||
|
||||
|
||||
/* Adapter class of a Source that saves all data read to `s'. */
|
||||
/* A sink that writes all incoming data to two other sinks. */
|
||||
struct TeeSink : Sink
|
||||
{
|
||||
Sink & sink1, & sink2;
|
||||
TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) { }
|
||||
virtual void operator () (const unsigned char * data, size_t len)
|
||||
{
|
||||
sink1(data, len);
|
||||
sink2(data, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Adapter class of a Source that saves all data read to a sink. */
|
||||
struct TeeSource : Source
|
||||
{
|
||||
Source & orig;
|
||||
ref<std::string> data;
|
||||
TeeSource(Source & orig)
|
||||
: orig(orig), data(make_ref<std::string>()) { }
|
||||
Sink & sink;
|
||||
TeeSource(Source & orig, Sink & sink)
|
||||
: orig(orig), sink(sink) { }
|
||||
size_t read(unsigned char * data, size_t len)
|
||||
{
|
||||
size_t n = orig.read(data, len);
|
||||
this->data->append((const char *) data, n);
|
||||
sink(data, len);
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
@ -336,4 +349,27 @@ Source & operator >> (Source & in, bool & b)
|
|||
}
|
||||
|
||||
|
||||
/* An adapter that converts a std::basic_istream into a source. */
|
||||
struct StreamToSourceAdapter : Source
|
||||
{
|
||||
std::shared_ptr<std::basic_istream<char>> istream;
|
||||
|
||||
StreamToSourceAdapter(std::shared_ptr<std::basic_istream<char>> istream)
|
||||
: istream(istream)
|
||||
{ }
|
||||
|
||||
size_t read(unsigned char * data, size_t len) override
|
||||
{
|
||||
if (!istream->read((char *) data, len)) {
|
||||
if (istream->eof()) {
|
||||
if (istream->gcount() == 0)
|
||||
throw EndOfFile("end of file");
|
||||
} else
|
||||
throw Error("I/O error in StreamToSourceAdapter");
|
||||
}
|
||||
return istream->gcount();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -79,7 +80,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
|
|||
}
|
||||
|
||||
|
||||
Path absPath(Path path, std::optional<Path> dir)
|
||||
Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
|
||||
{
|
||||
if (path[0] != '/') {
|
||||
if (!dir) {
|
||||
|
@ -100,7 +101,7 @@ Path absPath(Path path, std::optional<Path> dir)
|
|||
}
|
||||
path = *dir + "/" + path;
|
||||
}
|
||||
return canonPath(path);
|
||||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
||||
|
||||
|
@ -345,7 +346,6 @@ void writeFile(const Path & path, Source & source, mode_t mode)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
string readLine(int fd)
|
||||
{
|
||||
string s;
|
||||
|
@ -581,20 +581,31 @@ Paths createDirs(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
void createSymlink(const Path & target, const Path & link)
|
||||
void createSymlink(const Path & target, const Path & link,
|
||||
std::optional<time_t> mtime)
|
||||
{
|
||||
if (symlink(target.c_str(), link.c_str()))
|
||||
throw SysError("creating symlink from '%1%' to '%2%'", link, target);
|
||||
if (mtime) {
|
||||
struct timeval times[2];
|
||||
times[0].tv_sec = *mtime;
|
||||
times[0].tv_usec = 0;
|
||||
times[1].tv_sec = *mtime;
|
||||
times[1].tv_usec = 0;
|
||||
if (lutimes(link.c_str(), times))
|
||||
throw SysError("setting time of symlink '%s'", link);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void replaceSymlink(const Path & target, const Path & link)
|
||||
void replaceSymlink(const Path & target, const Path & link,
|
||||
std::optional<time_t> mtime)
|
||||
{
|
||||
for (unsigned int n = 0; true; n++) {
|
||||
Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link)));
|
||||
|
||||
try {
|
||||
createSymlink(target, tmp);
|
||||
createSymlink(target, tmp, mtime);
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == EEXIST) continue;
|
||||
throw;
|
||||
|
@ -1006,12 +1017,14 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss)
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Output = "standard out" output stream
|
||||
string runProgram(Path program, bool searchPath, const Strings & args,
|
||||
const std::optional<std::string> & input)
|
||||
{
|
||||
RunOptions opts(program, args);
|
||||
opts.searchPath = searchPath;
|
||||
// This allows you to refer to a program with a pathname relative to the
|
||||
// PATH variable.
|
||||
opts.input = input;
|
||||
|
||||
auto res = runProgram(opts);
|
||||
|
@ -1022,6 +1035,7 @@ string runProgram(Path program, bool searchPath, const Strings & args,
|
|||
return res.second;
|
||||
}
|
||||
|
||||
// Output = error code + "standard out" output stream
|
||||
std::pair<int, std::string> runProgram(const RunOptions & options_)
|
||||
{
|
||||
RunOptions options(options_);
|
||||
|
@ -1094,6 +1108,8 @@ void runProgram2(const RunOptions & options)
|
|||
|
||||
if (options.searchPath)
|
||||
execvp(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||
// This allows you to refer to a program with a pathname relative
|
||||
// to the PATH variable.
|
||||
else
|
||||
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@ void clearEnv();
|
|||
/* Return an absolutized path, resolving paths relative to the
|
||||
specified directory, or the current directory otherwise. The path
|
||||
is also canonicalised. */
|
||||
Path absPath(Path path, std::optional<Path> dir = {});
|
||||
Path absPath(Path path,
|
||||
std::optional<Path> dir = {},
|
||||
bool resolveSymlinks = false);
|
||||
|
||||
/* Canonicalise a path by removing all `.' or `..' components and
|
||||
double or trailing slashes. Optionally resolves all symlink
|
||||
|
@ -147,10 +149,12 @@ Path getDataDir();
|
|||
Paths createDirs(const Path & path);
|
||||
|
||||
/* Create a symlink. */
|
||||
void createSymlink(const Path & target, const Path & link);
|
||||
void createSymlink(const Path & target, const Path & link,
|
||||
std::optional<time_t> mtime = {});
|
||||
|
||||
/* Atomically create or replace a symlink. */
|
||||
void replaceSymlink(const Path & target, const Path & link);
|
||||
void replaceSymlink(const Path & target, const Path & link,
|
||||
std::optional<time_t> mtime = {});
|
||||
|
||||
|
||||
/* Wrappers arount read()/write() that read/write exactly the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue