libutil: add which()

Change-Id: I2bf80d09196b5c84891043e09b23e012fe6cfc4c
This commit is contained in:
Qyriad 2024-04-16 11:07:56 -06:00
parent dd7b11e496
commit 14bfadeec6
4 changed files with 57 additions and 8 deletions

View file

@ -1299,6 +1299,32 @@ void runProgram2(const RunOptions & options)
throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status)); throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status));
} }
std::optional<Path> which(std::string_view cmdName)
{
for (auto const & dir : tokenizeString<StringSet>(getEnv("PATH").value_or(""), ":")) {
auto supposedExe = dir + "/" + cmdName;
if (pathExists(supposedExe)) {
return std::move(supposedExe);
}
}
return std::nullopt;
}
std::vector<Path> whichAll(std::string_view cmdName)
{
std::vector<Path> results;
for (auto const & dir : tokenizeString<StringSet>(getEnv("PATH").value_or(""), ":")) {
auto supposedExe = dir + "/" + cmdName;
if (pathExists(supposedExe)) {
results.push_back(std::move(supposedExe));
}
}
return results;
}
void closeMostFDs(const std::set<int> & exceptions) void closeMostFDs(const std::set<int> & exceptions)
{ {

View file

@ -521,6 +521,31 @@ public:
*/ */
std::vector<char *> stringsToCharPtrs(const Strings & ss); std::vector<char *> stringsToCharPtrs(const Strings & ss);
/** Gets the first file called `cmdName` in colon-separated PATH.
*
* @param cmdName The name of the file to look for in PATH.
*
* @return std::nullopt if the specified filename was not found.
*
* @note This does *not* check if the found file is executable or not.
*
* @see whichAll()
*/
std::optional<Path> which(std::string_view cmdName);
/** Gets all files called `cmdName` in colon-separated PATH.
*
* @param cmdName The name of the file to look for in PATH.
*
* @return an std::vector of @ref Path "Paths", which will be empty if
* none were found.
*
* @note This does *not* check if the found files are executable or not.
*
* @see which()
*/
std::vector<Path> whichAll(std::string_view cmdName);
/** /**
* Close all file descriptors except those listed in the given set. * Close all file descriptors except those listed in the given set.
* Good practice in child processes. * Good practice in child processes.

View file

@ -77,9 +77,9 @@ struct CmdDoctor : StoreCommand
{ {
PathSet dirs; PathSet dirs;
for (auto & dir : tokenizeString<Strings>(getEnv("PATH").value_or(""), ":")) for (auto const & nixExeInPath : whichAll("nix-env")) {
if (pathExists(dir + "/nix-env")) dirs.insert(canonPath(dirOf(nixExeInPath), true));
dirs.insert(dirOf(canonPath(dir + "/nix-env", true))); }
if (dirs.size() != 1) { if (dirs.size() != 1) {
std::stringstream ss; std::stringstream ss;

View file

@ -104,11 +104,9 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
{ {
Path where; Path where;
for (auto & dir : tokenizeString<Strings>(getEnv("PATH").value_or(""), ":")) if (auto nixEnvPath = which("nix-env")) {
if (pathExists(dir + "/nix-env")) { where = dirOf(*nixEnvPath);
where = dir; }
break;
}
if (where == "") if (where == "")
throw Error("couldn't figure out how Nix is installed, so I can't upgrade it"); throw Error("couldn't figure out how Nix is installed, so I can't upgrade it");