diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 9c85fef62..8f72c926f 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -22,6 +22,7 @@ typedef std::map StringMap; /* Paths are just strings. */ typedef string Path; +typedef std::string_view PathView; typedef list Paths; typedef set PathSet; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9edd69c64..47876709e 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -106,16 +106,16 @@ Path absPath(Path path, std::optional dir, bool resolveSymlinks) } -Path canonPath(const Path & path, bool resolveSymlinks) +Path canonPath(PathView path, bool resolveSymlinks) { assert(path != ""); string s; + s.reserve(256); if (path[0] != '/') throw Error("not an absolute path: '%1%'", path); - string::const_iterator i = path.begin(), end = path.end(); string temp; /* Count the number of times we follow a symlink and stop at some @@ -125,33 +125,37 @@ Path canonPath(const Path & path, bool resolveSymlinks) while (1) { /* Skip slashes. */ - while (i != end && *i == '/') i++; - if (i == end) break; + while (!path.empty() && path[0] == '/') path.remove_prefix(1); + if (path.empty()) break; /* Ignore `.'. */ - if (*i == '.' && (i + 1 == end || i[1] == '/')) - i++; + if (path == "." || path.substr(0, 2) == "./") + path.remove_prefix(1); /* If `..', delete the last component. */ - else if (*i == '.' && i + 1 < end && i[1] == '.' && - (i + 2 == end || i[2] == '/')) + else if (path == ".." || path.substr(0, 3) == "../") { if (!s.empty()) s.erase(s.rfind('/')); - i += 2; + path.remove_prefix(2); } /* Normal component; copy it. */ else { s += '/'; - while (i != end && *i != '/') s += *i++; + if (const auto slash = path.find('/'); slash == string::npos) { + s += path; + path = {}; + } else { + s += path.substr(0, slash); + path = path.substr(slash + 1); + } /* If s points to a symlink, resolve it and continue from there */ if (resolveSymlinks && isLink(s)) { if (++followCount >= maxFollow) throw Error("infinite symlink recursion in path '%1%'", path); - temp = readLink(s) + string(i, end); - i = temp.begin(); - end = temp.end(); + temp = concatStrings(readLink(s), path); + path = temp; if (!temp.empty() && temp[0] == '/') { s.clear(); /* restart for symlinks pointing to absolute path */ } else { @@ -164,7 +168,7 @@ Path canonPath(const Path & path, bool resolveSymlinks) } } - return s.empty() ? "/" : s; + return s.empty() ? "/" : std::move(s); } @@ -1229,23 +1233,22 @@ void _interrupted() ////////////////////////////////////////////////////////////////////// -template C tokenizeString(std::string_view s, const string & separators) +template C tokenizeString(std::string_view s, std::string_view separators) { C result; string::size_type pos = s.find_first_not_of(separators, 0); while (pos != string::npos) { string::size_type end = s.find_first_of(separators, pos + 1); if (end == string::npos) end = s.size(); - string token(s, pos, end - pos); - result.insert(result.end(), token); + result.insert(result.end(), string(s, pos, end - pos)); pos = s.find_first_not_of(separators, end); } return result; } -template Strings tokenizeString(std::string_view s, const string & separators); -template StringSet tokenizeString(std::string_view s, const string & separators); -template vector tokenizeString(std::string_view s, const string & separators); +template Strings tokenizeString(std::string_view s, std::string_view separators); +template StringSet tokenizeString(std::string_view s, std::string_view separators); +template vector tokenizeString(std::string_view s, std::string_view separators); string chomp(std::string_view s) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index c900033f8..369c44f78 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -56,7 +56,7 @@ Path absPath(Path path, double or trailing slashes. Optionally resolves all symlink components such that each component of the resulting path is *not* a symbolic link. */ -Path canonPath(const Path & path, bool resolveSymlinks = false); +Path canonPath(PathView path, bool resolveSymlinks = false); /* Return the directory part of the given canonical path, i.e., everything before the final `/'. If the path is the root or an @@ -368,15 +368,19 @@ MakeError(FormatError, Error); /* String tokenizer. */ -template C tokenizeString(std::string_view s, const string & separators = " \t\n\r"); +template C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); /* Concatenate the given strings with a separator between the elements. */ template -string concatStringsSep(const string & sep, const C & ss) +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(); string s; + s.reserve(size); for (auto & i : ss) { if (s.size() != 0) s += sep; s += i; @@ -384,6 +388,14 @@ string concatStringsSep(const string & sep, const C & ss) return s; } +template +auto concatStrings(Parts && ... parts) + -> std::enable_if_t<(... && std::is_convertible_v), string> +{ + std::string_view views[sizeof...(parts)] = { parts... }; + return concatStringsSep({}, views); +} + /* Add quotes around a collection of strings. */ template Strings quoteStrings(const C & c)