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));
}
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)
{

View file

@ -521,6 +521,31 @@ public:
*/
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.
* Good practice in child processes.

View file

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

View file

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