1 : #include "names.hh"
2 : #include "util.hh"
3 :
4 :
5 : namespace nix {
6 :
7 :
8 : DrvName::DrvName()
9 2 : {
10 1 : name = "";
11 : }
12 :
13 :
14 : /* Parse a derivation name. The `name' part of a derivation name is
15 : everything up to but not including the first dash *not* followed by
16 : a letter. The `version' part is the rest (excluding the separating
17 : dash). E.g., `apache-httpd-2.0.48' is parsed to (`apache-httpd',
18 : '2.0.48'). */
19 163 : DrvName::DrvName(const string & s) : hits(0)
20 163 : {
21 163 : name = fullName = s;
22 660 : for (unsigned int i = 0; i < s.size(); ++i) {
23 : /* !!! isalpha/isdigit are affected by the locale. */
24 615 : if (s[i] == '-' && i + 1 < s.size() && !isalpha(s[i + 1])) {
25 118 : name = string(s, 0, i);
26 118 : version = string(s, i + 1);
27 118 : break;
28 : }
29 : }
30 : }
31 :
32 :
33 : bool DrvName::matches(DrvName & n)
34 84 : {
35 84 : if (name != "*" && name != n.name) return false;
36 68 : if (version != "" && version != n.version) return false;
37 59 : return true;
38 : }
39 :
40 :
41 : static string nextComponent(string::const_iterator & p,
42 : const string::const_iterator end)
43 38 : {
44 : /* Skip any dots and dashes (component separators). */
45 49 : while (p != end && (*p == '.' || *p == '-')) ++p;
46 :
47 38 : if (p == end) return "";
48 :
49 : /* If the first character is a digit, consume the longest sequence
50 : of digits. Otherwise, consume the longest sequence of
51 : non-digit, non-separator characters. */
52 34 : string s;
53 34 : if (isdigit(*p))
54 58 : while (p != end && isdigit(*p)) s += *p++;
55 : else
56 20 : while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
57 15 : s += *p++;
58 :
59 34 : return s;
60 : }
61 :
62 :
63 : static bool componentsLT(const string & c1, const string & c2)
64 37 : {
65 37 : int n1, n2;
66 37 : bool c1Num = string2Int(c1, n1), c2Num = string2Int(c2, n2);
67 :
68 37 : if (c1Num && c2Num) return n1 < n2;
69 9 : else if (c1 == "" && c2Num) return true;
70 8 : else if (c1 == "pre" && c2 != "pre") return true;
71 5 : else if (c2 == "pre") return false;
72 : /* Assume that `2.3a' < `2.3.1'. */
73 1 : else if (c2Num) return true;
74 1 : else if (c1Num) return false;
75 0 : else return c1 < c2;
76 : }
77 :
78 :
79 : int compareVersions(const string & v1, const string & v2)
80 8 : {
81 8 : string::const_iterator p1 = v1.begin();
82 8 : string::const_iterator p2 = v2.begin();
83 :
84 20 : while (p1 != v1.end() || p2 != v2.end()) {
85 19 : string c1 = nextComponent(p1, v1.end());
86 19 : string c2 = nextComponent(p2, v2.end());
87 19 : if (componentsLT(c1, c2)) return -1;
88 18 : else if (componentsLT(c2, c1)) return 1;
89 : }
90 :
91 1 : return 0;
92 : }
93 :
94 :
95 : static void testCompareVersions()
96 0 : {
97 : #define TEST(v1, v2, n) assert( \
98 : compareVersions(v1, v2) == n && compareVersions(v2, v1) == -n)
99 0 : TEST("1.0", "2.3", -1);
100 0 : TEST("2.1", "2.3", -1);
101 0 : TEST("2.3", "2.3", 0);
102 0 : TEST("2.5", "2.3", 1);
103 0 : TEST("3.1", "2.3", 1);
104 0 : TEST("2.3.1", "2.3", 1);
105 0 : TEST("2.3.1", "2.3a", 1);
106 0 : TEST("2.3pre1", "2.3", -1);
107 0 : TEST("2.3pre3", "2.3pre12", -1);
108 0 : TEST("2.3a", "2.3c", -1);
109 0 : TEST("2.3pre1", "2.3c", -1);
110 0 : TEST("2.3pre1", "2.3q", -1);
111 : }
112 :
113 :
114 : DrvNames drvNamesFromArgs(const Strings & opArgs)
115 40 : {
116 40 : DrvNames result;
117 81 : for (Strings::const_iterator i = opArgs.begin();
118 : i != opArgs.end(); ++i)
119 123 : result.push_back(DrvName(*i));
120 0 : return result;
121 : }
122 :
123 :
124 : }
|