aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2017-07-17 19:02:56 +0200
committerEelco Dolstra <edolstra@gmail.com>2017-07-20 13:33:13 +0200
commit90825dea518ea078f0783a72cc471a5b3716d198 (patch)
treeb6f6fed6e8c7a96a6769344060409f19a56290b9
parent3162ad5ff497b92fc25cd3f397eaff01d67340cc (diff)
Add "nix search" command
-rw-r--r--src/libexpr/get-drvs.cc44
-rw-r--r--src/libexpr/get-drvs.hh39
-rw-r--r--src/libutil/args.cc1
-rw-r--r--src/libutil/args.hh4
-rw-r--r--src/nix-env/nix-env.cc90
-rw-r--r--src/nix-env/user-env.cc7
-rw-r--r--src/nix/command.hh34
-rw-r--r--src/nix/installables.cc16
-rw-r--r--src/nix/search.cc130
9 files changed, 263 insertions, 102 deletions
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 4200e8fd6..b7e16de7f 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -9,7 +9,34 @@
namespace nix {
-string DrvInfo::queryDrvPath()
+DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
+ : state(&state), attrs(attrs), attrPath(attrPath)
+{
+}
+
+
+string DrvInfo::queryName() const
+{
+ if (name == "" && attrs) {
+ auto i = attrs->find(state->sName);
+ if (i == attrs->end()) throw TypeError("derivation name missing");
+ name = state->forceStringNoCtx(*i->value);
+ }
+ return name;
+}
+
+
+string DrvInfo::querySystem() const
+{
+ if (system == "" && attrs) {
+ auto i = attrs->find(state->sSystem);
+ system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos);
+ }
+ return system;
+}
+
+
+string DrvInfo::queryDrvPath() const
{
if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
@@ -20,7 +47,7 @@ string DrvInfo::queryDrvPath()
}
-string DrvInfo::queryOutPath()
+string DrvInfo::queryOutPath() const
{
if (outPath == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
@@ -76,7 +103,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
}
-string DrvInfo::queryOutputName()
+string DrvInfo::queryOutputName() const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
@@ -225,17 +252,12 @@ static bool getDerivation(EvalState & state, Value & v,
if (done.find(v.attrs) != done.end()) return false;
done.insert(v.attrs);
- Bindings::iterator i = v.attrs->find(state.sName);
- /* !!! We really would like to have a decent back trace here. */
- if (i == v.attrs->end()) throw TypeError("derivation name missing");
+ DrvInfo drv(state, attrPath, v.attrs);
- Bindings::iterator i2 = v.attrs->find(state.sSystem);
-
- DrvInfo drv(state, state.forceStringNoCtx(*i->value), attrPath,
- i2 == v.attrs->end() ? "unknown" : state.forceStringNoCtx(*i2->value, *i2->pos),
- v.attrs);
+ drv.queryName();
drvs.push_back(drv);
+
return false;
} catch (AssertionError & e) {
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 37fcbe829..82fb8a3ac 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -17,31 +17,32 @@ public:
private:
EvalState * state;
- string drvPath;
- string outPath;
- string outputName;
+ mutable string name;
+ mutable string system;
+ mutable string drvPath;
+ mutable string outPath;
+ mutable string outputName;
Outputs outputs;
- bool failed; // set if we get an AssertionError
+ bool failed = false; // set if we get an AssertionError
- Bindings * attrs, * meta;
+ Bindings * attrs = nullptr, * meta = nullptr;
Bindings * getMeta();
bool checkMeta(Value & v);
public:
- string name;
string attrPath; /* path towards the derivation */
- string system;
- DrvInfo(EvalState & state) : state(&state), failed(false), attrs(0), meta(0) { };
- DrvInfo(EvalState & state, const string & name, const string & attrPath, const string & system, Bindings * attrs)
- : state(&state), failed(false), attrs(attrs), meta(0), name(name), attrPath(attrPath), system(system) { };
+ DrvInfo(EvalState & state) : state(&state) { };
+ DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs);
- string queryDrvPath();
- string queryOutPath();
- string queryOutputName();
+ string queryName() const;
+ string querySystem() const;
+ string queryDrvPath() const;
+ string queryOutPath() const;
+ string queryOutputName() const;
/** Return the list of outputs. The "outputs to install" are determined by `mesa.outputsToInstall`. */
Outputs queryOutputs(bool onlyOutputsToInstall = false);
@@ -58,15 +59,9 @@ public:
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
*/
- void setDrvPath(const string & s)
- {
- drvPath = s;
- }
-
- void setOutPath(const string & s)
- {
- outPath = s;
- }
+ void setName(const string & s) { name = s; }
+ void setDrvPath(const string & s) { drvPath = s; }
+ void setOutPath(const string & s) { outPath = s; }
void setFailed() { failed = true; };
bool hasFailed() { return failed; };
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index 0eed49454..19a45d7e9 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -66,6 +66,7 @@ void Args::printHelp(const string & programName, std::ostream & out)
std::cout << renderLabels({exp.label});
// FIXME: handle arity > 1
if (exp.arity == 0) std::cout << "...";
+ if (exp.optional) std::cout << "?";
}
std::cout << "\n";
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index ef8a7953e..37e780dd1 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -164,9 +164,9 @@ public:
}
/* Expect a string argument. */
- void expectArg(const std::string & label, string * dest)
+ void expectArg(const std::string & label, string * dest, bool optional = false)
{
- expectedArgs.push_back(ExpectedArg{label, 1, false, [=](Strings ss) {
+ expectedArgs.push_back(ExpectedArg{label, 1, optional, [=](Strings ss) {
*dest = ss.front();
}});
}
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 10100d6a6..8620cd255 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -186,7 +186,7 @@ static void loadDerivations(EvalState & state, Path nixExprPath,
system. */
for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) {
j = i; j++;
- if (systemFilter != "*" && i->system != systemFilter)
+ if (systemFilter != "*" && i->querySystem() != systemFilter)
elems.erase(i);
}
}
@@ -247,7 +247,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
for (DrvInfos::const_iterator j = allElems.begin();
j != allElems.end(); ++j, ++n)
{
- DrvName drvName(j->name);
+ DrvName drvName(j->queryName());
if (i.matches(drvName)) {
i.hits++;
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
@@ -269,36 +269,36 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
StringSet multiple;
for (auto & j : matches) {
- DrvName drvName(j.first.name);
+ DrvName drvName(j.first.queryName());
int d = 1;
Newest::iterator k = newest.find(drvName.name);
if (k != newest.end()) {
- d = j.first.system == k->second.first.system ? 0 :
- j.first.system == settings.thisSystem ? 1 :
- k->second.first.system == settings.thisSystem ? -1 : 0;
+ d = j.first.querySystem() == k->second.first.querySystem() ? 0 :
+ j.first.querySystem() == settings.thisSystem ? 1 :
+ k->second.first.querySystem() == settings.thisSystem ? -1 : 0;
if (d == 0)
d = comparePriorities(state, j.first, k->second.first);
if (d == 0)
- d = compareVersions(drvName.version, DrvName(k->second.first.name).version);
+ d = compareVersions(drvName.version, DrvName(k->second.first.queryName()).version);
}
if (d > 0) {
newest.erase(drvName.name);
newest.insert(Newest::value_type(drvName.name, j));
- multiple.erase(j.first.name);
+ multiple.erase(j.first.queryName());
} else if (d == 0) {
- multiple.insert(j.first.name);
+ multiple.insert(j.first.queryName());
}
}
matches.clear();
for (auto & j : newest) {
- if (multiple.find(j.second.first.name) != multiple.end())
+ if (multiple.find(j.second.first.queryName()) != multiple.end())
printInfo(
- format("warning: there are multiple derivations named ‘%1%’; using the first one")
- % j.second.first.name);
+ "warning: there are multiple derivations named ‘%1%’; using the first one",
+ j.second.first.queryName());
matches.push_back(j.second);
}
}
@@ -386,7 +386,8 @@ static void queryInstSources(EvalState & state,
if (dash != string::npos)
name = string(name, dash + 1);
- DrvInfo elem(state, name, "", "", 0);
+ DrvInfo elem(state, "", nullptr);
+ elem.setName(name);
if (isDerivation(path)) {
elem.setDrvPath(path);
@@ -468,8 +469,8 @@ static void installDerivations(Globals & globals,
path is not the one we want (e.g., `java-front' versus
`java-front-0.9pre15899'). */
if (globals.forceName != "")
- i.name = globals.forceName;
- newNames.insert(DrvName(i.name).name);
+ i.setName(globals.forceName);
+ newNames.insert(DrvName(i.queryName()).name);
}
@@ -484,17 +485,17 @@ static void installDerivations(Globals & globals,
DrvInfos installedElems = queryInstalled(*globals.state, profile);
for (auto & i : installedElems) {
- DrvName drvName(i.name);
+ DrvName drvName(i.queryName());
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
!keep(i))
- printInfo(format("replacing old ‘%1%’") % i.name);
+ printInfo("replacing old ‘%s’", i.queryName());
else
allElems.push_back(i);
}
for (auto & i : newElems)
- printInfo(format("installing ‘%1%’") % i.name);
+ printInfo("installing ‘%s’", i.queryName());
}
printMissing(*globals.state, newElems);
@@ -548,7 +549,7 @@ static void upgradeDerivations(Globals & globals,
/* Go through all installed derivations. */
DrvInfos newElems;
for (auto & i : installedElems) {
- DrvName drvName(i.name);
+ DrvName drvName(i.queryName());
try {
@@ -569,7 +570,7 @@ static void upgradeDerivations(Globals & globals,
for (auto j = availElems.begin(); j != availElems.end(); ++j) {
if (comparePriorities(*globals.state, i, *j) > 0)
continue;
- DrvName newName(j->name);
+ DrvName newName(j->queryName());
if (newName.name == drvName.name) {
int d = compareVersions(drvName.version, newName.version);
if ((upgradeType == utLt && d < 0) ||
@@ -596,14 +597,13 @@ static void upgradeDerivations(Globals & globals,
{
const char * action = compareVersions(drvName.version, bestVersion) <= 0
? "upgrading" : "downgrading";
- printInfo(
- format("%1% ‘%2%’ to ‘%3%’")
- % action % i.name % bestElem->name);
+ printInfo("%1% ‘%2%’ to ‘%3%’",
+ action, i.queryName(), bestElem->queryName());
newElems.push_back(*bestElem);
} else newElems.push_back(i);
} catch (Error & e) {
- e.addPrefix(format("while trying to find an upgrade for ‘%1%’:\n") % i.name);
+ e.addPrefix(fmt("while trying to find an upgrade for ‘%s’:\n", i.queryName()));
throw;
}
}
@@ -663,10 +663,10 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
/* Update all matching derivations. */
for (auto & i : installedElems) {
- DrvName drvName(i.name);
+ DrvName drvName(i.queryName());
for (auto & j : selectors)
if (j.matches(drvName)) {
- printInfo(format("setting flag on ‘%1%’") % i.name);
+ printInfo("setting flag on ‘%1%’", i.queryName());
j.hits++;
setMetaFlag(*globals.state, i, flagName, flagValue);
break;
@@ -702,7 +702,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
DrvInfo & drv(elems.front());
if (globals.forceName != "")
- drv.name = globals.forceName;
+ drv.setName(globals.forceName);
if (drv.queryDrvPath() != "") {
PathSet paths = {drv.queryDrvPath()};
@@ -732,7 +732,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
DrvInfos newElems;
for (auto & i : installedElems) {
- DrvName drvName(i.name);
+ DrvName drvName(i.queryName());
bool found = false;
for (auto & j : selectors)
/* !!! the repeated calls to followLinksToStorePath()
@@ -740,7 +740,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
if ((isPath(j) && i.queryOutPath() == globals.state->store->followLinksToStorePath(j))
|| DrvName(j).matches(drvName))
{
- printInfo(format("uninstalling ‘%1%’") % i.name);
+ printInfo("uninstalling ‘%s’", i.queryName());
found = true;
break;
}
@@ -771,9 +771,11 @@ static bool cmpChars(char a, char b)
static bool cmpElemByName(const DrvInfo & a, const DrvInfo & b)
{
+ auto a_name = a.queryName();
+ auto b_name = b.queryName();
return lexicographical_compare(
- a.name.begin(), a.name.end(),
- b.name.begin(), b.name.end(), cmpChars);
+ a_name.begin(), a_name.end(),
+ b_name.begin(), b_name.end(), cmpChars);
}
@@ -822,13 +824,13 @@ typedef enum { cvLess, cvEqual, cvGreater, cvUnavail } VersionDiff;
static VersionDiff compareVersionAgainstSet(
const DrvInfo & elem, const DrvInfos & elems, string & version)
{
- DrvName name(elem.name);
+ DrvName name(elem.queryName());
VersionDiff diff = cvUnavail;
version = "?";
for (auto & i : elems) {
- DrvName name2(i.name);
+ DrvName name2(i.queryName());
if (name.name == name2.name) {
int d = compareVersions(name.version, name2.version);
if (d < 0) {
@@ -857,8 +859,8 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
for (auto & i : elems) {
JSONObject pkgObj = topObj.object(i.attrPath);
- pkgObj.attr("name", i.name);
- pkgObj.attr("system", i.system);
+ pkgObj.attr("name", i.queryName());
+ pkgObj.attr("system", i.querySystem());
JSONObject metaObj = pkgObj.object("meta");
StringSet metaNames = i.queryMetaNames();
@@ -866,7 +868,7 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
auto placeholder = metaObj.placeholder(j);
Value * v = i.queryMeta(j);
if (!v) {
- printError(format("derivation ‘%1%’ has invalid meta attribute ‘%2%’") % i.name % j);
+ printError("derivation ‘%s’ has invalid meta attribute ‘%s’", i.queryName(), j);
placeholder.write(nullptr);
} else {
PathSet context;
@@ -963,7 +965,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
try {
paths.insert(i.queryOutPath());
} catch (AssertionError & e) {
- printMsg(lvlTalkative, format("skipping derivation named ‘%1%’ which gives an assertion failure") % i.name);
+ printMsg(lvlTalkative, "skipping derivation named ‘%s’ which gives an assertion failure", i.queryName());
i.setFailed();
}
validPaths = globals.state->store->queryValidPaths(paths);
@@ -1024,9 +1026,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
columns.push_back(i.attrPath);
if (xmlOutput)
- attrs["name"] = i.name;
+ attrs["name"] = i.queryName();
else if (printName)
- columns.push_back(i.name);
+ columns.push_back(i.queryName());
if (compareVersions) {
/* Compare this element against the versions of the
@@ -1059,10 +1061,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
}
if (xmlOutput) {
- if (i.system != "") attrs["system"] = i.system;
+ if (i.querySystem() != "") attrs["system"] = i.querySystem();
}
else if (printSystem)
- columns.push_back(i.system);
+ columns.push_back(i.querySystem());
if (printDrvPath) {
string drvPath = i.queryDrvPath();
@@ -1110,7 +1112,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
attrs2["name"] = j;
Value * v = i.queryMeta(j);
if (!v)
- printError(format("derivation ‘%1%’ has invalid meta attribute ‘%2%’") % i.name % j);
+ printError("derivation ‘%s’ has invalid meta attribute ‘%s’", i.queryName(), j);
else {
if (v->type == tString) {
attrs2["type"] = "string";
@@ -1161,9 +1163,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
cout.flush();
} catch (AssertionError & e) {
- printMsg(lvlTalkative, format("skipping derivation named ‘%1%’ which gives an assertion failure") % i.name);
+ printMsg(lvlTalkative, "skipping derivation named ‘%1%’ which gives an assertion failure", i.queryName());
} catch (Error & e) {
- e.addPrefix(format("while querying the derivation named ‘%1%’:\n") % i.name);
+ e.addPrefix(fmt("while querying the derivation named ‘%1%’:\n", i.queryName()));
throw;
}
}
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index e9997fae5..df5105f12 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -56,9 +56,10 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkAttrs(v, 16);
mkString(*state.allocAttr(v, state.sType), "derivation");
- mkString(*state.allocAttr(v, state.sName), i.name);
- if (!i.system.empty())
- mkString(*state.allocAttr(v, state.sSystem), i.system);
+ mkString(*state.allocAttr(v, state.sName), i.queryName());
+ auto system = i.querySystem();
+ if (!system.empty())
+ mkString(*state.allocAttr(v, state.sSystem), system);
mkString(*state.allocAttr(v, state.sOutPath), i.queryOutPath());
if (drvPath != "")
mkString(*state.allocAttr(v, state.sDrvPath), i.queryDrvPath());
diff --git a/src/nix/command.hh b/src/nix/command.hh
index ae7709b5d..536802653 100644
--- a/src/nix/command.hh
+++ b/src/nix/command.hh
@@ -62,17 +62,13 @@ struct Installable
}
};
-/* A command that operates on a list of "installables", which can be
- store paths, attribute paths, Nix expressions, etc. */
-struct InstallablesCommand : virtual Args, StoreCommand
+struct SourceExprCommand : virtual Args, StoreCommand
{
- std::vector<std::shared_ptr<Installable>> installables;
Path file;
- InstallablesCommand()
+ SourceExprCommand()
{
mkFlag('f', "file", "file", "evaluate FILE rather than the default", &file);
- expectArgs("installables", &_installables);
}
/* Return a value representing the Nix expression from which we
@@ -81,14 +77,32 @@ struct InstallablesCommand : virtual Args, StoreCommand
= import ...; bla = import ...; }’. */
Value * getSourceExpr(EvalState & state);
+ ref<EvalState> getEvalState();
+
+private:
+
+ std::shared_ptr<EvalState> evalState;
+
+ Value * vSourceExpr = 0;
+};
+
+/* A command that operates on a list of "installables", which can be
+ store paths, attribute paths, Nix expressions, etc. */
+struct InstallablesCommand : virtual Args, SourceExprCommand
+{
+ std::vector<std::shared_ptr<Installable>> installables;
+
+ InstallablesCommand()
+ {
+ expectArgs("installables", &_installables);
+ }
+
std::vector<std::shared_ptr<Installable>> parseInstallables(ref<Store> store, Strings ss);
enum ToStorePathsMode { Build, NoBuild, DryRun };
PathSet toStorePaths(ref<Store> store, ToStorePathsMode mode);
- ref<EvalState> getEvalState();
-
void prepare() override;
virtual bool useDefaultInstallables() { return true; }
@@ -96,10 +110,6 @@ struct InstallablesCommand : virtual Args, StoreCommand
private:
Strings _installables;
-
- std::shared_ptr<EvalState> evalState;
-
- Value * vSourceExpr = 0;
};
/* A command that operates on zero or more store paths. */
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index 7fad8fe41..4da736f4d 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -12,7 +12,7 @@
namespace nix {
-Value * InstallablesCommand::getSourceExpr(EvalState & state)
+Value * SourceExprCommand::getSourceExpr(EvalState & state)
{
if (vSourceExpr) return vSourceExpr;
@@ -59,6 +59,13 @@ Value * InstallablesCommand::getSourceExpr(EvalState & state)
return vSourceExpr;
}
+ref<EvalState> SourceExprCommand::getEvalState()
+{
+ if (!evalState)
+ evalState = std::make_shared<EvalState>(Strings{}, getStore());
+ return ref<EvalState>(evalState);
+}
+
struct InstallableStoreDrv : Installable
{
Path storePath;
@@ -237,13 +244,6 @@ PathSet InstallablesCommand::toStorePaths(ref<Store> store, ToStorePathsMode mod
return outPaths;
}
-ref<EvalState> InstallablesCommand::getEvalState()
-{
- if (!evalState)
- evalState = std::make_shared<EvalState>(Strings{}, getStore());
- return ref<EvalState>(evalState);
-}
-
void InstallablesCommand::prepare()
{
installables = parseInstallables(getStore(), _installables);
diff --git a/src/nix/search.cc b/src/nix/search.cc
new file mode 100644
index 000000000..813f6d0a6
--- /dev/null
+++ b/src/nix/search.cc
@@ -0,0 +1,130 @@
+#include "command.hh"
+#include "globals.hh"
+#include "eval.hh"
+#include "eval-inline.hh"
+#include "names.hh"
+#include "get-drvs.hh"
+
+#include <regex>
+
+using namespace nix;
+
+std::string hilite(const std::string & s, const std::smatch & m)
+{
+ return
+ m.empty()
+ ? s
+ : std::string(m.prefix())
+ + ANSI_RED + std::string(m.str()) + ANSI_NORMAL
+ + std::string(m.suffix());
+}
+
+struct CmdSearch : SourceExprCommand
+{
+ std::string re;
+
+ CmdSearch()
+ {
+ expectArg("regex", &re, true);
+ }
+
+ std::string name() override
+ {
+ return "search";
+ }
+
+ std::string description() override
+ {
+ return "query available packages";
+ }
+
+ void run(ref<Store> store) override
+ {
+ settings.readOnlyMode = true;
+
+ std::regex regex(re, std::regex::extended | std::regex::icase);
+
+ auto state = getEvalState();
+
+ std::function<void(Value *, std::string, bool)> doExpr;
+
+ bool first = true;
+
+ doExpr = [&](Value * v, std::string attrPath, bool toplevel) {
+ debug("at attribute ‘%s’", attrPath);
+
+ try {
+
+ state->forceValue(*v);
+
+ if (v->type == tLambda && toplevel) {
+ Value * v2 = state->allocValue();
+ state->autoCallFunction(*state->allocBindings(1), *v, *v2);
+ v = v2;
+ state->forceValue(*v);
+ }
+
+ if (state->isDerivation(*v)) {
+
+ DrvInfo drv(*state, attrPath, v->attrs);
+
+ DrvName parsed(drv.queryName());
+
+ std::smatch attrPathMatch;
+ std::regex_search(attrPath, attrPathMatch, regex);
+
+ auto name = parsed.name;
+ std::smatch nameMatch;
+ std::regex_search(name, nameMatch, regex);
+
+ std::string description = drv.queryMetaString("description");
+ std::replace(description.begin(), description.end(), '\n', ' ');
+ std::smatch descriptionMatch;
+ std::regex_search(description, descriptionMatch, regex);
+
+ if (!attrPathMatch.empty()
+ || !nameMatch.empty()
+ || !descriptionMatch.empty())
+ {
+ if (!first) std::cout << "\n";
+ first = false;
+
+ std::cout << fmt(
+ "Attribute name: %s\n"
+ "Package name: %s\n"
+ "Version: %s\n"
+ "Description: %s\n",
+ hilite(attrPath, attrPathMatch),
+ hilite(name, nameMatch),
+ parsed.version,
+ hilite(description, descriptionMatch));
+ }
+ }
+
+ else if (v->type == tAttrs) {
+
+ if (!toplevel) {
+ auto attrs = v->attrs;
+ Bindings::iterator j = attrs->find(state->symbols.create("recurseForDerivations"));
+ if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) return;
+ }
+
+ Bindings::iterator j = v->attrs->find(state->symbols.create("_toplevel"));
+ bool toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos);
+
+ for (auto & i : *v->attrs) {
+ doExpr(i.value,
+ attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name,
+ toplevel2);
+ }
+ }
+
+ } catch (AssertionError & e) {
+ }
+ };
+
+ doExpr(getSourceExpr(*state), "", true);
+ }
+};
+
+static RegisterCommand r1(make_ref<CmdSearch>());