Compare commits
18 Commits
unix-sourc
...
2.31.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4006d0fe11 | ||
|
|
13d1be04b3 | ||
|
|
e8a54769a1 | ||
|
|
1fc4d526a3 | ||
|
|
c7e35e1ff8 | ||
|
|
92066f468e | ||
|
|
1285e9485c | ||
|
|
05884fc103 | ||
|
|
258e41004e | ||
|
|
f8245ffcee | ||
|
|
7aa0aca968 | ||
|
|
0cea128243 | ||
|
|
30682ec93b | ||
|
|
8e46456dfe | ||
|
|
9adbc08576 | ||
|
|
ec6ba866d1 | ||
|
|
752d0ef1c0 | ||
|
|
f777aa70d3 |
6
flake.lock
generated
6
flake.lock
generated
@@ -63,11 +63,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1755442223,
|
||||
"narHash": "sha256-VtMQg02B3kt1oejwwrGn50U9Xbjgzfbb5TV5Wtx8dKI=",
|
||||
"lastModified": 1756178832,
|
||||
"narHash": "sha256-O2CIn7HjZwEGqBrwu9EU76zlmA5dbmna7jL1XUmAId8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cd32a774ac52caaa03bcfc9e7591ac8c18617ced",
|
||||
"rev": "d98ce345cdab58477ca61855540999c86577d19d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
linux32BitSystems = [ "i686-linux" ];
|
||||
linux64BitSystems = [
|
||||
|
||||
@@ -71,6 +71,12 @@ toml11 = dependency(
|
||||
method : 'cmake',
|
||||
include_type : 'system',
|
||||
)
|
||||
|
||||
configdata_priv.set(
|
||||
'HAVE_TOML11_4',
|
||||
toml11.version().version_compare('>= 4.0.0').to_int(),
|
||||
)
|
||||
|
||||
deps_other += toml11
|
||||
|
||||
config_priv_h = configure_file(
|
||||
|
||||
@@ -185,7 +185,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value ** args
|
||||
{.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||
.pos = state.positions[pos]});
|
||||
|
||||
auto parsedURL = parseURL(*fromStoreUrl);
|
||||
auto parsedURL = parseURL(*fromStoreUrl, /*lenient=*/true);
|
||||
|
||||
if (parsedURL.scheme != "http" && parsedURL.scheme != "https"
|
||||
&& !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||
|
||||
@@ -1,73 +1,140 @@
|
||||
#include "nix/expr/primops.hh"
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
|
||||
#include "expr-config-private.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <toml.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
#if HAVE_TOML11_4
|
||||
|
||||
/**
|
||||
* This is what toml11 < 4.0 did when choosing the subsecond precision.
|
||||
* TOML 1.0.0 spec doesn't define how sub-millisecond ranges should be handled and calls it
|
||||
* implementation defined behavior. For a lack of a better choice we stick with what older versions
|
||||
* of toml11 did [1].
|
||||
*
|
||||
* [1]: https://github.com/ToruNiina/toml11/blob/dcfe39a783a94e8d52c885e5883a6fbb21529019/toml/datetime.hpp#L282
|
||||
*/
|
||||
static size_t normalizeSubsecondPrecision(toml::local_time lt)
|
||||
{
|
||||
auto millis = lt.millisecond;
|
||||
auto micros = lt.microsecond;
|
||||
auto nanos = lt.nanosecond;
|
||||
if (millis != 0 || micros != 0 || nanos != 0) {
|
||||
if (micros != 0 || nanos != 0) {
|
||||
if (nanos != 0)
|
||||
return 9;
|
||||
return 6;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize date/time formats to serialize to the same strings as versions prior to toml11 4.0.
|
||||
*
|
||||
* Several things to consider:
|
||||
*
|
||||
* 1. Sub-millisecond range is represented the same way as in toml11 versions prior to 4.0. Precisioun is rounded
|
||||
* towards the next multiple of 3 or capped at 9 digits.
|
||||
* 2. Seconds must be specified. This may become optional in (yet unreleased) TOML 1.1.0, but 1.0.0 defined local time
|
||||
* in terms of RFC3339 [1].
|
||||
* 3. date-time separator (`t`, `T` or space ` `) is canonicalized to an upper T. This is compliant with RFC3339
|
||||
* [1] 5.6:
|
||||
* > Applications that generate this format SHOULD use upper case letters.
|
||||
*
|
||||
* [1]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
|
||||
*/
|
||||
static void normalizeDatetimeFormat(toml::value & t)
|
||||
{
|
||||
if (t.is_local_datetime()) {
|
||||
auto & ldt = t.as_local_datetime();
|
||||
t.as_local_datetime_fmt() = {
|
||||
.delimiter = toml::datetime_delimiter_kind::upper_T,
|
||||
// https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
|
||||
.has_seconds = true, // Mandated by TOML 1.0.0
|
||||
.subsecond_precision = normalizeSubsecondPrecision(ldt.time),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.is_offset_datetime()) {
|
||||
auto & odt = t.as_offset_datetime();
|
||||
t.as_offset_datetime_fmt() = {
|
||||
.delimiter = toml::datetime_delimiter_kind::upper_T,
|
||||
// https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
|
||||
.has_seconds = true, // Mandated by TOML 1.0.0
|
||||
.subsecond_precision = normalizeSubsecondPrecision(odt.time),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.is_local_time()) {
|
||||
auto & lt = t.as_local_time();
|
||||
t.as_local_time_fmt() = {
|
||||
.has_seconds = true, // Mandated by TOML 1.0.0
|
||||
.subsecond_precision = normalizeSubsecondPrecision(lt),
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Value & val)
|
||||
{
|
||||
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
|
||||
|
||||
std::istringstream tomlStream(std::string{toml});
|
||||
|
||||
std::function<void(Value &, toml::value)> visit;
|
||||
|
||||
visit = [&](Value & v, toml::value t) {
|
||||
auto visit = [&](auto & self, Value & v, toml::value t) -> void {
|
||||
switch (t.type()) {
|
||||
case toml::value_t::table: {
|
||||
auto table = toml::get<toml::table>(t);
|
||||
|
||||
size_t size = 0;
|
||||
for (auto & i : table) {
|
||||
(void) i;
|
||||
size++;
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(size);
|
||||
auto attrs = state.buildBindings(table.size());
|
||||
|
||||
for (auto & elem : table) {
|
||||
forceNoNullByte(elem.first);
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
self(self, attrs.alloc(elem.first), elem.second);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::array: {
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
auto list = state.buildList(array.size());
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
visit(*(v = state.allocValue()), array[n]);
|
||||
self(self, *(v = state.allocValue()), array[n]);
|
||||
v.mkList(list);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::boolean:
|
||||
v.mkBool(toml::get<bool>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::integer:
|
||||
v.mkInt(toml::get<int64_t>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::floating:
|
||||
v.mkFloat(toml::get<NixFloat>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::string: {
|
||||
auto s = toml::get<std::string_view>(t);
|
||||
forceNoNullByte(s);
|
||||
v.mkString(s);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
case toml::value_t::local_date:
|
||||
case toml::value_t::local_time: {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
|
||||
#if HAVE_TOML11_4
|
||||
normalizeDatetimeFormat(t);
|
||||
#endif
|
||||
auto attrs = state.buildBindings(2);
|
||||
attrs.alloc("_type").mkString("timestamp");
|
||||
std::ostringstream s;
|
||||
@@ -80,16 +147,24 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
}
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::empty:
|
||||
v.mkNull();
|
||||
break;
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
|
||||
visit(
|
||||
visit,
|
||||
val,
|
||||
toml::parse(
|
||||
tomlStream,
|
||||
"fromTOML" /* the "filename" */
|
||||
#if HAVE_TOML11_4
|
||||
,
|
||||
toml::spec::v(1, 0, 0) // Be explicit that we are parsing TOML 1.0.0 without extensions
|
||||
#endif
|
||||
));
|
||||
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||
state.error<EvalError>("while parsing TOML: %s", e.what()).atPos(pos).debugThrow();
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ TEST(getNameFromURL, getNameFromURL)
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:~/repos/nixpkgs#packages.x86_64-linux.Hello")), "Hello");
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:.#nonStandardAttr.mylaptop")), "mylaptop");
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:./repos/myflake#nonStandardAttr.mylaptop")), "mylaptop");
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:./nixpkgs#packages.x86_64-linux.complex^bin,man")), "complex");
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:./myproj#packages.x86_64-linux.default^*")), "myproj");
|
||||
ASSERT_EQ(
|
||||
getNameFromURL(parseURL("path:./nixpkgs#packages.x86_64-linux.complex^bin,man", /*lenient=*/true)), "complex");
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:./myproj#packages.x86_64-linux.default^*", /*lenient=*/true)), "myproj");
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:./myproj#defaultPackage.x86_64-linux")), "myproj");
|
||||
|
||||
ASSERT_EQ(getNameFromURL(parseURL("github:NixOS/nixpkgs#packages.x86_64-linux.hello")), "hello");
|
||||
@@ -80,6 +81,6 @@ TEST(getNameFromURL, getNameFromURL)
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:.")), std::nullopt);
|
||||
ASSERT_EQ(getNameFromURL(parseURL("file:.#")), std::nullopt);
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:.#packages.x86_64-linux.default")), std::nullopt);
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:.#packages.x86_64-linux.default^*")), std::nullopt);
|
||||
ASSERT_EQ(getNameFromURL(parseURL("path:.#packages.x86_64-linux.default^*", /*lenient=*/true)), std::nullopt);
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
@@ -82,7 +82,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
auto succeeds = std::regex_match(url, match, pathFlakeRegex);
|
||||
assert(succeeds);
|
||||
auto path = match[1].str();
|
||||
auto query = decodeQuery(match[3]);
|
||||
auto query = decodeQuery(match[3].str(), /*lenient=*/true);
|
||||
auto fragment = percentDecode(match[5].str());
|
||||
|
||||
if (baseDir) {
|
||||
@@ -210,7 +210,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
|
||||
bool isFlake)
|
||||
{
|
||||
try {
|
||||
auto parsed = parseURL(url);
|
||||
auto parsed = parseURL(url, /*lenient=*/true);
|
||||
if (baseDir && (parsed.scheme == "path" || parsed.scheme == "git+file") && !isAbsolute(parsed.path))
|
||||
parsed.path = absPath(parsed.path, *baseDir);
|
||||
return fromParsedURL(fetchSettings, std::move(parsed), isFlake);
|
||||
@@ -289,7 +289,7 @@ FlakeRef FlakeRef::canonicalize() const
|
||||
filtering the `dir` query parameter from the URL. */
|
||||
if (auto url = fetchers::maybeGetStrAttr(flakeRef.input.attrs, "url")) {
|
||||
try {
|
||||
auto parsed = parseURL(*url);
|
||||
auto parsed = parseURL(*url, /*lenient=*/true);
|
||||
if (auto dir2 = get(parsed.query, "dir")) {
|
||||
if (flakeRef.subdir != "" && flakeRef.subdir == *dir2)
|
||||
parsed.query.erase("dir");
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "nix/util/url.hh"
|
||||
#include "nix/util/signals.hh"
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/vfs.h>
|
||||
#endif
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <atomic>
|
||||
@@ -60,6 +64,28 @@ static void traceSQL(void * x, const char * sql)
|
||||
|
||||
SQLite::SQLite(const std::filesystem::path & path, SQLiteOpenMode mode)
|
||||
{
|
||||
// Work around a ZFS issue where SQLite's truncate() call on
|
||||
// db.sqlite-shm can randomly take up to a few seconds. See
|
||||
// https://github.com/openzfs/zfs/issues/14290#issuecomment-3074672917.
|
||||
// Remove this workaround when a fix is widely installed, perhaps 2027? Candidate:
|
||||
// https://github.com/search?q=repo%3Aopenzfs%2Fzfs+%22Linux%3A+zfs_putpage%3A+complete+async+page+writeback+immediately%22&type=commits
|
||||
#ifdef __linux__
|
||||
try {
|
||||
auto shmFile = path;
|
||||
shmFile += "-shm";
|
||||
AutoCloseFD fd = open(shmFile.string().c_str(), O_RDWR | O_CLOEXEC);
|
||||
if (fd) {
|
||||
struct statfs fs;
|
||||
if (fstatfs(fd.get(), &fs))
|
||||
throw SysError("statfs() on '%s'", shmFile);
|
||||
if (fs.f_type == /* ZFS_SUPER_MAGIC */ 801189825 && fdatasync(fd.get()) != 0)
|
||||
throw SysError("fsync() on '%s'", shmFile);
|
||||
}
|
||||
} catch (...) {
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
|
||||
// useSQLiteWAL also indicates what virtual file system we need. Using
|
||||
// `unix-dotfile` is needed on NFS file systems and on Windows' Subsystem
|
||||
// for Linux (WSL) where useSQLiteWAL should be false by default.
|
||||
|
||||
@@ -45,7 +45,7 @@ StoreReference StoreReference::parse(const std::string & uri, const StoreReferen
|
||||
{
|
||||
auto params = extraParams;
|
||||
try {
|
||||
auto parsedUri = parseURL(uri);
|
||||
auto parsedUri = parseURL(uri, /*lenient=*/true);
|
||||
params.insert(parsedUri.query.begin(), parsedUri.query.end());
|
||||
|
||||
auto baseURI = parsedUri.authority.value_or(ParsedURL::Authority{}).to_string() + parsedUri.path;
|
||||
@@ -107,7 +107,7 @@ std::pair<std::string, StoreReference::Params> splitUriAndParams(const std::stri
|
||||
StoreReference::Params params;
|
||||
auto q = uri.find('?');
|
||||
if (q != std::string::npos) {
|
||||
params = decodeQuery(uri.substr(q + 1));
|
||||
params = decodeQuery(uri.substr(q + 1), /*lenient=*/true);
|
||||
uri = uri_.substr(0, q);
|
||||
}
|
||||
return {uri, params};
|
||||
|
||||
@@ -221,15 +221,20 @@ TEST(parseURL, parsedUrlsWithUnescapedChars)
|
||||
* 2. Unescaped spaces and quotes in query.
|
||||
*/
|
||||
auto s = "http://www.example.org/file.tar.gz?query \"= 123\"#shevron^quote\"space ";
|
||||
auto url = parseURL(s);
|
||||
|
||||
ASSERT_EQ(url.fragment, "shevron^quote\"space ");
|
||||
/* Without leniency for back compat, this should throw. */
|
||||
EXPECT_THROW(parseURL(s), Error);
|
||||
|
||||
/* With leniency for back compat, this should parse. */
|
||||
auto url = parseURL(s, /*lenient=*/true);
|
||||
|
||||
EXPECT_EQ(url.fragment, "shevron^quote\"space ");
|
||||
|
||||
auto query = StringMap{
|
||||
{"query \"", " 123\""},
|
||||
};
|
||||
|
||||
ASSERT_EQ(url.query, query);
|
||||
EXPECT_EQ(url.query, query);
|
||||
}
|
||||
|
||||
TEST(parseURL, parseFTPUrl)
|
||||
@@ -268,6 +273,23 @@ TEST(parseURL, emptyStringIsInvalidURL)
|
||||
ASSERT_THROW(parseURL(""), Error);
|
||||
}
|
||||
|
||||
TEST(parseURL, parsesHttpUrlWithEmptyPort)
|
||||
{
|
||||
auto s = "http://www.example.org:/file.tar.gz?foo=bar";
|
||||
auto parsed = parseURL(s);
|
||||
|
||||
ParsedURL expected{
|
||||
.scheme = "http",
|
||||
.authority = Authority{.hostType = HostType::Name, .host = "www.example.org"},
|
||||
.path = "/file.tar.gz",
|
||||
.query = (StringMap) {{"foo", "bar"}},
|
||||
.fragment = "",
|
||||
};
|
||||
|
||||
ASSERT_EQ(parsed, expected);
|
||||
ASSERT_EQ("http://www.example.org/file.tar.gz?foo=bar", parsed.to_string());
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* decodeQuery
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
@@ -96,14 +96,18 @@ MakeError(BadURL, Error);
|
||||
std::string percentDecode(std::string_view in);
|
||||
std::string percentEncode(std::string_view s, std::string_view keep = "");
|
||||
|
||||
StringMap decodeQuery(const std::string & query);
|
||||
/**
|
||||
* @param lenient @see parseURL
|
||||
*/
|
||||
StringMap decodeQuery(std::string_view query, bool lenient = false);
|
||||
|
||||
std::string encodeQuery(const StringMap & query);
|
||||
|
||||
/**
|
||||
* Parse a Nix URL into a ParsedURL.
|
||||
* Parse a URL into a ParsedURL.
|
||||
*
|
||||
* Nix URI is mostly compliant with RFC3986, but with some deviations:
|
||||
* @parm lenient Also allow some long-supported Nix URIs that are not quite compliant with RFC3986.
|
||||
* Here are the deviations:
|
||||
* - Fragments can contain unescaped (not URL encoded) '^', '"' or space literals.
|
||||
* - Queries may contain unescaped '"' or spaces.
|
||||
*
|
||||
@@ -111,7 +115,7 @@ std::string encodeQuery(const StringMap & query);
|
||||
*
|
||||
* @throws BadURL
|
||||
*/
|
||||
ParsedURL parseURL(std::string_view url);
|
||||
ParsedURL parseURL(std::string_view url, bool lenient = false);
|
||||
|
||||
/**
|
||||
* Although that’s not really standardized anywhere, an number of tools
|
||||
|
||||
@@ -33,7 +33,7 @@ ParsedURL::Authority ParsedURL::Authority::parse(std::string_view encodedAuthori
|
||||
}();
|
||||
|
||||
auto port = [&]() -> std::optional<uint16_t> {
|
||||
if (!parsed->has_port())
|
||||
if (!parsed->has_port() || parsed->port() == "")
|
||||
return std::nullopt;
|
||||
/* If the port number is non-zero and representable. */
|
||||
if (auto portNumber = parsed->port_number())
|
||||
@@ -108,41 +108,48 @@ static std::string percentEncodeCharSet(std::string_view s, auto charSet)
|
||||
return res;
|
||||
}
|
||||
|
||||
ParsedURL parseURL(std::string_view url)
|
||||
ParsedURL parseURL(std::string_view url, bool lenient)
|
||||
try {
|
||||
/* Account for several non-standard properties of nix urls (for back-compat):
|
||||
* - Allow unescaped spaces ' ' and '"' characters in queries.
|
||||
* - Allow '"', ' ' and '^' characters in the fragment component.
|
||||
* We could write our own grammar for this, but fixing it up here seems
|
||||
* more concise, since the deviation is rather minor.
|
||||
*
|
||||
* If `!lenient` don't bother initializing, because we can just
|
||||
* parse `url` directly`.
|
||||
*/
|
||||
std::string fixedEncodedUrl = [&]() {
|
||||
std::string fixed;
|
||||
std::string_view view = url;
|
||||
std::string fixedEncodedUrl;
|
||||
|
||||
if (auto beforeQuery = splitPrefixTo(view, '?')) {
|
||||
fixed += *beforeQuery;
|
||||
fixed += '?';
|
||||
auto fragmentStart = view.find('#');
|
||||
auto queryView = view.substr(0, fragmentStart);
|
||||
auto fixedQuery = percentEncodeCharSet(queryView, extraAllowedCharsInQuery);
|
||||
fixed += fixedQuery;
|
||||
view.remove_prefix(std::min(fragmentStart, view.size()));
|
||||
}
|
||||
if (lenient) {
|
||||
fixedEncodedUrl = [&] {
|
||||
std::string fixed;
|
||||
std::string_view view = url;
|
||||
|
||||
if (auto beforeFragment = splitPrefixTo(view, '#')) {
|
||||
fixed += *beforeFragment;
|
||||
fixed += '#';
|
||||
auto fixedFragment = percentEncodeCharSet(view, extraAllowedCharsInFragment);
|
||||
fixed += fixedFragment;
|
||||
if (auto beforeQuery = splitPrefixTo(view, '?')) {
|
||||
fixed += *beforeQuery;
|
||||
fixed += '?';
|
||||
auto fragmentStart = view.find('#');
|
||||
auto queryView = view.substr(0, fragmentStart);
|
||||
auto fixedQuery = percentEncodeCharSet(queryView, extraAllowedCharsInQuery);
|
||||
fixed += fixedQuery;
|
||||
view.remove_prefix(std::min(fragmentStart, view.size()));
|
||||
}
|
||||
|
||||
if (auto beforeFragment = splitPrefixTo(view, '#')) {
|
||||
fixed += *beforeFragment;
|
||||
fixed += '#';
|
||||
auto fixedFragment = percentEncodeCharSet(view, extraAllowedCharsInFragment);
|
||||
fixed += fixedFragment;
|
||||
return fixed;
|
||||
}
|
||||
|
||||
fixed += view;
|
||||
return fixed;
|
||||
}
|
||||
}();
|
||||
}
|
||||
|
||||
fixed += view;
|
||||
return fixed;
|
||||
}();
|
||||
|
||||
auto urlView = boost::urls::url_view(fixedEncodedUrl);
|
||||
auto urlView = boost::urls::url_view(lenient ? fixedEncodedUrl : url);
|
||||
|
||||
if (!urlView.has_scheme())
|
||||
throw BadURL("'%s' doesn't have a scheme", url);
|
||||
@@ -179,7 +186,7 @@ try {
|
||||
.scheme = scheme,
|
||||
.authority = authority,
|
||||
.path = path,
|
||||
.query = decodeQuery(std::string(query)),
|
||||
.query = decodeQuery(query, lenient),
|
||||
.fragment = fragment,
|
||||
};
|
||||
} catch (boost::system::system_error & e) {
|
||||
@@ -201,14 +208,17 @@ std::string percentEncode(std::string_view s, std::string_view keep)
|
||||
s, [keep](char c) { return boost::urls::unreserved_chars(c) || keep.find(c) != keep.npos; });
|
||||
}
|
||||
|
||||
StringMap decodeQuery(const std::string & query)
|
||||
StringMap decodeQuery(std::string_view query, bool lenient)
|
||||
try {
|
||||
/* For back-compat unescaped characters are allowed. */
|
||||
auto fixedEncodedQuery = percentEncodeCharSet(query, extraAllowedCharsInQuery);
|
||||
/* When `lenient = true`, for back-compat unescaped characters are allowed. */
|
||||
std::string fixedEncodedQuery;
|
||||
if (lenient) {
|
||||
fixedEncodedQuery = percentEncodeCharSet(query, extraAllowedCharsInQuery);
|
||||
}
|
||||
|
||||
StringMap result;
|
||||
|
||||
auto encodedQuery = boost::urls::params_encoded_view(fixedEncodedQuery);
|
||||
auto encodedQuery = boost::urls::params_encoded_view(lenient ? fixedEncodedQuery : query);
|
||||
for (auto && [key, value, value_specified] : encodedQuery) {
|
||||
if (!value_specified) {
|
||||
warn("dubious URI query '%s' is missing equal sign '%s', ignoring", std::string_view(key), "=");
|
||||
|
||||
@@ -647,7 +647,7 @@ struct CmdDevelop : Common, MixEnvironment
|
||||
nixpkgs = i->nixpkgsFlakeRef();
|
||||
|
||||
auto bashInstallable = make_ref<InstallableFlake>(
|
||||
this,
|
||||
nullptr, //< Don't barf when the command is run with --arg/--argstr
|
||||
state,
|
||||
std::move(nixpkgs),
|
||||
"bashInteractive",
|
||||
|
||||
@@ -105,7 +105,8 @@ std::string getNameFromElement(const ProfileElement & element)
|
||||
{
|
||||
std::optional<std::string> result = std::nullopt;
|
||||
if (element.source) {
|
||||
result = getNameFromURL(parseURL(element.source->to_string()));
|
||||
// Seems to be for Flake URLs
|
||||
result = getNameFromURL(parseURL(element.source->to_string(), /*lenient=*/true));
|
||||
}
|
||||
return result.value_or(element.identifier());
|
||||
}
|
||||
@@ -160,11 +161,15 @@ struct ProfileManifest
|
||||
e["outputs"].get<ExtendedOutputsSpec>()};
|
||||
}
|
||||
|
||||
std::string name =
|
||||
elems.is_object() ? elem.key()
|
||||
: element.source
|
||||
? getNameFromURL(parseURL(element.source->to_string())).value_or(element.identifier())
|
||||
: element.identifier();
|
||||
std::string name = [&] {
|
||||
if (elems.is_object())
|
||||
return elem.key();
|
||||
if (element.source) {
|
||||
if (auto optName = getNameFromURL(parseURL(element.source->to_string(), /*lenient=*/true)))
|
||||
return *optName;
|
||||
}
|
||||
return element.identifier();
|
||||
}();
|
||||
|
||||
addElement(name, std::move(element));
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt2 = { _type = "timestamp"; value = "00:32:00.999999"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; }
|
||||
{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt10 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456789"; }; ldt11 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456789"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T07:32:00.100"; }; ldt3 = { _type = "timestamp"; value = "1979-05-27T07:32:00.120"; }; ldt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123"; }; ldt5 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123400"; }; ldt6 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123450"; }; ldt7 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456"; }; ldt8 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456700"; }; ldt9 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456780"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt10 = { _type = "timestamp"; value = "00:32:00.123456789"; }; lt11 = { _type = "timestamp"; value = "00:32:00.123456789"; }; lt2 = { _type = "timestamp"; value = "00:32:00.100"; }; lt3 = { _type = "timestamp"; value = "00:32:00.120"; }; lt4 = { _type = "timestamp"; value = "00:32:00.123"; }; lt5 = { _type = "timestamp"; value = "00:32:00.123400"; }; lt6 = { _type = "timestamp"; value = "00:32:00.123450"; }; lt7 = { _type = "timestamp"; value = "00:32:00.123456"; }; lt8 = { _type = "timestamp"; value = "00:32:00.123456700"; }; lt9 = { _type = "timestamp"; value = "00:32:00.123456780"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt10 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456Z"; }; odt11 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456700Z"; }; odt12 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456780Z"; }; odt13 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456789Z"; }; odt14 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456789Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt5 = { _type = "timestamp"; value = "1979-05-27T07:32:00.100Z"; }; odt6 = { _type = "timestamp"; value = "1979-05-27T07:32:00.120Z"; }; odt7 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123Z"; }; odt8 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123400Z"; }; odt9 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123450Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; }
|
||||
|
||||
@@ -55,11 +55,53 @@ builtins.fromTOML ''
|
||||
odt2 = 1979-05-27T00:32:00-07:00
|
||||
odt3 = 1979-05-27T00:32:00.999999-07:00
|
||||
odt4 = 1979-05-27 07:32:00Z
|
||||
# milliseconds
|
||||
odt5 = 1979-05-27 07:32:00.1Z
|
||||
odt6 = 1979-05-27 07:32:00.12Z
|
||||
odt7 = 1979-05-27 07:32:00.123Z
|
||||
# microseconds
|
||||
odt8 = 1979-05-27t07:32:00.1234Z
|
||||
odt9 = 1979-05-27t07:32:00.12345Z
|
||||
odt10 = 1979-05-27t07:32:00.123456Z
|
||||
# nanoseconds
|
||||
odt11 = 1979-05-27 07:32:00.1234567Z
|
||||
odt12 = 1979-05-27 07:32:00.12345678Z
|
||||
odt13 = 1979-05-27 07:32:00.123456789Z
|
||||
# no more precision after nanoseconds
|
||||
odt14 = 1979-05-27t07:32:00.1234567891Z
|
||||
|
||||
ldt1 = 1979-05-27T07:32:00
|
||||
ldt2 = 1979-05-27T00:32:00.999999
|
||||
# milliseconds
|
||||
ldt2 = 1979-05-27T07:32:00.1
|
||||
ldt3 = 1979-05-27T07:32:00.12
|
||||
ldt4 = 1979-05-27T07:32:00.123
|
||||
# microseconds
|
||||
ldt5 = 1979-05-27t00:32:00.1234
|
||||
ldt6 = 1979-05-27t00:32:00.12345
|
||||
ldt7 = 1979-05-27t00:32:00.123456
|
||||
# nanoseconds
|
||||
ldt8 = 1979-05-27 00:32:00.1234567
|
||||
ldt9 = 1979-05-27 00:32:00.12345678
|
||||
ldt10 = 1979-05-27 00:32:00.123456789
|
||||
# no more precision after nanoseconds
|
||||
ldt11 = 1979-05-27t00:32:00.1234567891
|
||||
|
||||
ld1 = 1979-05-27
|
||||
lt1 = 07:32:00
|
||||
lt2 = 00:32:00.999999
|
||||
# milliseconds
|
||||
lt2 = 00:32:00.1
|
||||
lt3 = 00:32:00.12
|
||||
lt4 = 00:32:00.123
|
||||
# microseconds
|
||||
lt5 = 00:32:00.1234
|
||||
lt6 = 00:32:00.12345
|
||||
lt7 = 00:32:00.123456
|
||||
# nanoseconds
|
||||
lt8 = 00:32:00.1234567
|
||||
lt9 = 00:32:00.12345678
|
||||
lt10 = 00:32:00.123456789
|
||||
# no more precision after nanoseconds
|
||||
lt11 = 00:32:00.1234567891
|
||||
|
||||
arr1 = [ 1, 2, 3 ]
|
||||
arr2 = [ "red", "yellow", "green" ]
|
||||
|
||||
Reference in New Issue
Block a user