diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am index 5353a7a6b8..bb418ae06c 100644 --- a/src/nix-env/Makefile.am +++ b/src/nix-env/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS = nix-env -nix_env_SOURCES = main.cc help.txt +nix_env_SOURCES = main.cc names.cc names.hh help.txt nix_env_LDADD = ../libmain/libmain.a ../libexpr/libexpr.a \ ../libstore/libstore.a ../libutil/libutil.a \ ../boost/format/libformat.a -L../../externals/inst/lib -ldb_cxx \ diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index 00b6c7f8d3..0512366818 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -1,5 +1,6 @@ #include +#include "names.hh" #include "globals.hh" #include "normalise.hh" #include "shared.hh" @@ -253,136 +254,6 @@ void createUserEnv(EvalState & state, const DrvInfos & drvs, } -struct DrvName -{ - string fullName; - string name; - string version; - unsigned int hits; - - /* Parse a derivation name. The `name' part of a derivation name - is everything up to but not including the first dash *not* - followed by a letter. The `version' part is the rest - (excluding the separating dash). E.g., `apache-httpd-2.0.48' - is parsed to (`apache-httpd', '2.0.48'). */ - DrvName(const string & s) : hits(0) - { - name = fullName = s; - for (unsigned int i = 0; i < s.size(); ++i) { - /* !!! isalpha/isdigit are affected by the locale. */ - if (s[i] == '-' && i + 1 < s.size() && !isalpha(s[i + 1])) { - name = string(s, 0, i); - version = string(s, i + 1); - break; - } - } - } - - bool matches(DrvName & n) - { - if (name != "*" && name != n.name) return false; - if (version != "" && version != n.version) return false; - return true; - } -}; - - -string nextComponent(string::const_iterator & p, - const string::const_iterator end) -{ - /* Skip any dots and dashes (component separators). */ - while (p != end && (*p == '.' || *p == '-')) ++p; - - if (p == end) return ""; - - /* If the first character is a digit, consume the longest sequence - of digits. Otherwise, consume the longest sequence of - non-digit, non-separator characters. */ - string s; - if (isdigit(*p)) - while (p != end && isdigit(*p)) s += *p++; - else - while (p != end && (!isdigit(*p) && *p != '.' && *p != '-')) - s += *p++; - - return s; -} - - -#include - -bool parseInt(const string & s, int & n) -{ - istringstream st(s); - st >> n; - return !st.fail(); -} - - -static bool componentsLT(const string & c1, const string & c2) -{ - int n1, n2; - bool c1Num = parseInt(c1, n1), c2Num = parseInt(c2, n2); - - if (c1Num && c2Num) return n1 < n2; - else if (c1 == "" && c2Num) return true; - else if (c1 == "pre" && c2 != "pre") return true; - else if (c2 == "pre") return false; - /* Assume that `2.3a' < `2.3.1'. */ - else if (c2Num) return true; - else if (c1Num) return false; - else return c1 < c2; -} - - -static int compareVersions(const string & v1, const string & v2) -{ - string::const_iterator p1 = v1.begin(); - string::const_iterator p2 = v2.begin(); - - while (p1 != v1.end() || p2 != v2.end()) { - string c1 = nextComponent(p1, v1.end()); - string c2 = nextComponent(p2, v2.end()); - if (componentsLT(c1, c2)) return -1; - else if (componentsLT(c2, c1)) return 1; - } - - return 0; -} - - -static void testCompareVersions() -{ -#define TEST(v1, v2, n) assert( \ - compareVersions(v1, v2) == n && compareVersions(v2, v1) == -n) - TEST("1.0", "2.3", -1); - TEST("2.1", "2.3", -1); - TEST("2.3", "2.3", 0); - TEST("2.5", "2.3", 1); - TEST("3.1", "2.3", 1); - TEST("2.3.1", "2.3", 1); - TEST("2.3.1", "2.3a", 1); - TEST("2.3pre1", "2.3", -1); - TEST("2.3pre3", "2.3pre12", -1); - TEST("2.3a", "2.3c", -1); - TEST("2.3pre1", "2.3c", -1); - TEST("2.3pre1", "2.3q", -1); -} - - -typedef list DrvNames; - - -static DrvNames drvNamesFromArgs(const Strings & opArgs) -{ - DrvNames result; - for (Strings::const_iterator i = opArgs.begin(); - i != opArgs.end(); ++i) - result.push_back(DrvName(*i)); - return result; -} - - static void installDerivations(EvalState & state, Path nePath, DrvNames & selectors, const Path & profile) { diff --git a/src/nix-env/names.cc b/src/nix-env/names.cc new file mode 100644 index 0000000000..252b07b91c --- /dev/null +++ b/src/nix-env/names.cc @@ -0,0 +1,121 @@ +#include "names.hh" + + +/* Parse a derivation name. The `name' part of a derivation name is + everything up to but not including the first dash *not* followed by + a letter. The `version' part is the rest (excluding the separating + dash). E.g., `apache-httpd-2.0.48' is parsed to (`apache-httpd', + '2.0.48'). */ +DrvName::DrvName(const string & s) : hits(0) +{ + name = fullName = s; + for (unsigned int i = 0; i < s.size(); ++i) { + /* !!! isalpha/isdigit are affected by the locale. */ + if (s[i] == '-' && i + 1 < s.size() && !isalpha(s[i + 1])) { + name = string(s, 0, i); + version = string(s, i + 1); + break; + } + } +} + + +bool DrvName::matches(DrvName & n) +{ + if (name != "*" && name != n.name) return false; + if (version != "" && version != n.version) return false; + return true; +} + + +static string nextComponent(string::const_iterator & p, + const string::const_iterator end) +{ + /* Skip any dots and dashes (component separators). */ + while (p != end && (*p == '.' || *p == '-')) ++p; + + if (p == end) return ""; + + /* If the first character is a digit, consume the longest sequence + of digits. Otherwise, consume the longest sequence of + non-digit, non-separator characters. */ + string s; + if (isdigit(*p)) + while (p != end && isdigit(*p)) s += *p++; + else + while (p != end && (!isdigit(*p) && *p != '.' && *p != '-')) + s += *p++; + + return s; +} + + +#include + +static bool parseInt(const string & s, int & n) +{ + istringstream st(s); + st >> n; + return !st.fail(); +} + + +static bool componentsLT(const string & c1, const string & c2) +{ + int n1, n2; + bool c1Num = parseInt(c1, n1), c2Num = parseInt(c2, n2); + + if (c1Num && c2Num) return n1 < n2; + else if (c1 == "" && c2Num) return true; + else if (c1 == "pre" && c2 != "pre") return true; + else if (c2 == "pre") return false; + /* Assume that `2.3a' < `2.3.1'. */ + else if (c2Num) return true; + else if (c1Num) return false; + else return c1 < c2; +} + + +int compareVersions(const string & v1, const string & v2) +{ + string::const_iterator p1 = v1.begin(); + string::const_iterator p2 = v2.begin(); + + while (p1 != v1.end() || p2 != v2.end()) { + string c1 = nextComponent(p1, v1.end()); + string c2 = nextComponent(p2, v2.end()); + if (componentsLT(c1, c2)) return -1; + else if (componentsLT(c2, c1)) return 1; + } + + return 0; +} + + +static void testCompareVersions() +{ +#define TEST(v1, v2, n) assert( \ + compareVersions(v1, v2) == n && compareVersions(v2, v1) == -n) + TEST("1.0", "2.3", -1); + TEST("2.1", "2.3", -1); + TEST("2.3", "2.3", 0); + TEST("2.5", "2.3", 1); + TEST("3.1", "2.3", 1); + TEST("2.3.1", "2.3", 1); + TEST("2.3.1", "2.3a", 1); + TEST("2.3pre1", "2.3", -1); + TEST("2.3pre3", "2.3pre12", -1); + TEST("2.3a", "2.3c", -1); + TEST("2.3pre1", "2.3c", -1); + TEST("2.3pre1", "2.3q", -1); +} + + +DrvNames drvNamesFromArgs(const Strings & opArgs) +{ + DrvNames result; + for (Strings::const_iterator i = opArgs.begin(); + i != opArgs.end(); ++i) + result.push_back(DrvName(*i)); + return result; +} diff --git a/src/nix-env/names.hh b/src/nix-env/names.hh new file mode 100644 index 0000000000..0fc9b57d07 --- /dev/null +++ b/src/nix-env/names.hh @@ -0,0 +1,29 @@ +#ifndef __NAMES_H +#define __NAMES_H + +#include +#include + +#include "util.hh" + + +struct DrvName +{ + string fullName; + string name; + string version; + unsigned int hits; + + DrvName(const string & s); + bool matches(DrvName & n); +}; + + +typedef list DrvNames; + + +int compareVersions(const string & v1, const string & v2); +DrvNames drvNamesFromArgs(const Strings & opArgs); + + +#endif /* !__NAMES_H */