aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorRebecca Turner <rbt@sent.as>2024-08-25 11:58:55 -0700
committerRebecca Turner <rbt@sent.as>2024-08-25 15:54:22 -0700
commit690f07272e58bfe86d12adb0bd6c81c031f930fd (patch)
treee01efa0e48d38eb44eb1d3445719a6adab29a33a /src/libutil
parent5fc6fcb31035f79a8e590f07d73dc6cc592e9e29 (diff)
Support relative and `~/` paths in config settings
Change-Id: I5566a9858ba255f4ac5051d1368c7dfb24460f0a
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/config.cc14
-rw-r--r--src/libutil/file-system.cc15
-rw-r--r--src/libutil/file-system.hh10
3 files changed, 35 insertions, 4 deletions
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index 333deb388..778da1413 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -126,7 +126,7 @@ static void applyConfigInner(const std::string & contents, const ApplyConfigOpti
if (!options.path) {
throw UsageError("can only include configuration '%1%' from files", tokens[1]);
}
- auto pathToInclude = absPath(tokens[1], dirOf(*options.path));
+ auto pathToInclude = absPath(tildePath(tokens[1], options.home), dirOf(*options.path));
if (pathExists(pathToInclude)) {
auto includeOptions = ApplyConfigOptions {
.path = pathToInclude,
@@ -437,10 +437,16 @@ template class BaseSetting<DeprecatedFeatures>;
static Path parsePath(const AbstractSetting & s, const std::string & str, const ApplyConfigOptions & options)
{
- if (str == "")
+ if (str == "") {
throw UsageError("setting '%s' is a path and paths cannot be empty", s.name);
- else
- return canonPath(str);
+ } else {
+ auto tildeResolvedPath = tildePath(str, options.home);
+ if (options.path) {
+ return absPath(tildeResolvedPath, dirOf(*options.path));
+ } else {
+ return canonPath(tildeResolvedPath);
+ }
+ }
}
template<> Path PathsSetting<Path>::parse(const std::string & str, const ApplyConfigOptions & options) const
diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc
index 631cf076b..234c73163 100644
--- a/src/libutil/file-system.cc
+++ b/src/libutil/file-system.cc
@@ -117,6 +117,21 @@ Path realPath(Path const & path)
return ret;
}
+Path tildePath(Path const & path, const std::optional<Path> & home)
+{
+ if (path.starts_with("~/")) {
+ if (home) {
+ return *home + "/" + path.substr(2);
+ } else {
+ throw UsageError("`~` path not allowed: %1%", path);
+ }
+ } else if (path.starts_with('~')) {
+ throw UsageError("`~` paths must start with `~/`: %1%", path);
+ } else {
+ return path;
+ }
+}
+
void chmodPath(const Path & path, mode_t mode)
{
if (chmod(path.c_str(), mode) == -1)
diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh
index e49323e84..0a54d1a3b 100644
--- a/src/libutil/file-system.hh
+++ b/src/libutil/file-system.hh
@@ -63,6 +63,16 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
Path realPath(Path const & path);
/**
+ * Resolve a tilde path like `~/puppy.nix` into an absolute path.
+ *
+ * If `home` is given, it's substituted for `~/` at the start of the input
+ * `path`. Otherwise, an error is thrown.
+ *
+ * If the path starts with `~` but not `~/`, an error is thrown.
+ */
+Path tildePath(Path const & path, const std::optional<Path> & home = std::nullopt);
+
+/**
* Change the permissions of a path
* Not called `chmod` as it shadows and could be confused with
* `int chmod(char *, mode_t)`, which does not handle errors