Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * version.c - version handling functions
4 : : *
5 : : * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6 : : * Copyright © 2008-2014 Guillem Jover <guillem@debian.org>
7 : : *
8 : : * This is free software; you can redistribute it and/or modify
9 : : * it under the terms of the GNU General Public License as published by
10 : : * the Free Software Foundation; either version 2 of the License, or
11 : : * (at your option) any later version.
12 : : *
13 : : * This is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <config.h>
23 : : #include <compat.h>
24 : :
25 : : #include <dpkg/c-ctype.h>
26 : : #include <dpkg/ehandle.h>
27 : : #include <dpkg/string.h>
28 : : #include <dpkg/version.h>
29 : :
30 : : /**
31 : : * Turn the passed version into an empty version.
32 : : *
33 : : * This can be used to ensure the version is properly initialized.
34 : : *
35 : : * @param version The version to clear.
36 : : */
37 : : void
38 : 379 : dpkg_version_blank(struct dpkg_version *version)
39 : : {
40 : 379 : version->epoch = 0;
41 : 379 : version->version = NULL;
42 : 379 : version->revision = NULL;
43 : 379 : }
44 : :
45 : : /**
46 : : * Test if a version is not empty.
47 : : *
48 : : * @param version The version to test.
49 : : *
50 : : * @retval true If the version is informative (i.e. not an empty version).
51 : : * @retval false If the version is empty.
52 : : */
53 : : bool
54 : 10 : dpkg_version_is_informative(const struct dpkg_version *version)
55 : : {
56 [ + + ]: 9 : return (version->epoch ||
57 [ + + + + ]: 19 : str_is_set(version->version) ||
58 : 5 : str_is_set(version->revision));
59 : : }
60 : :
61 : : /**
62 : : * Give a weight to the character to order in the version comparison.
63 : : *
64 : : * @param c An ASCII character.
65 : : */
66 : : static int
67 : 64 : order(int c)
68 : : {
69 [ - + ]: 64 : if (c_isdigit(c))
70 : 0 : return 0;
71 [ + + ]: 64 : else if (c_isalpha(c))
72 : 24 : return c;
73 [ + + ]: 40 : else if (c == '~')
74 : 4 : return -1;
75 [ + - ]: 36 : else if (c)
76 : 36 : return c + 256;
77 : :
78 : 0 : return 0;
79 : : }
80 : :
81 : : static int
82 : 71 : verrevcmp(const char *a, const char *b)
83 : : {
84 [ + + ]: 71 : if (a == NULL)
85 : 2 : a = "";
86 [ + + ]: 71 : if (b == NULL)
87 : 2 : b = "";
88 : :
89 [ + + - + ]: 134 : while (*a || *b) {
90 : 79 : int first_diff = 0;
91 : :
92 [ + + + + : 107 : while ((*a && !c_isdigit(*a)) || (*b && !c_isdigit(*b))) {
+ + - + ]
93 : 32 : int ac = order(*a);
94 : 32 : int bc = order(*b);
95 : :
96 [ + + ]: 32 : if (ac != bc)
97 : 4 : return ac - bc;
98 : :
99 : 28 : a++;
100 : 28 : b++;
101 : : }
102 [ + + ]: 120 : while (*a == '0')
103 : 45 : a++;
104 [ + + ]: 120 : while (*b == '0')
105 : 45 : b++;
106 [ + + + - ]: 106 : while (c_isdigit(*a) && c_isdigit(*b)) {
107 [ + - ]: 31 : if (!first_diff)
108 : 31 : first_diff = *a - *b;
109 : 31 : a++;
110 : 31 : b++;
111 : : }
112 : :
113 [ - + ]: 75 : if (c_isdigit(*a))
114 : 0 : return 1;
115 [ - + ]: 75 : if (c_isdigit(*b))
116 : 0 : return -1;
117 [ + + ]: 75 : if (first_diff)
118 : 12 : return first_diff;
119 : : }
120 : :
121 : 55 : return 0;
122 : : }
123 : :
124 : : /**
125 : : * Compares two Debian versions.
126 : : *
127 : : * This function follows the convention of the comparator functions used by
128 : : * qsort().
129 : : *
130 : : * @see deb-version(5)
131 : : *
132 : : * @param a The first version.
133 : : * @param b The second version.
134 : : *
135 : : * @retval 0 If a and b are equal.
136 : : * @retval <0 If a is smaller than b.
137 : : * @retval >0 If a is greater than b.
138 : : */
139 : : int
140 : 45 : dpkg_version_compare(const struct dpkg_version *a,
141 : : const struct dpkg_version *b)
142 : : {
143 : : int rc;
144 : :
145 [ + + ]: 45 : if (a->epoch > b->epoch)
146 : 1 : return 1;
147 [ + + ]: 44 : if (a->epoch < b->epoch)
148 : 2 : return -1;
149 : :
150 : 42 : rc = verrevcmp(a->version, b->version);
151 [ + + ]: 42 : if (rc)
152 : 13 : return rc;
153 : :
154 : 29 : return verrevcmp(a->revision, b->revision);
155 : : }
156 : :
157 : : /**
158 : : * Check if two versions have a certain relation.
159 : : *
160 : : * @param a The first version.
161 : : * @param rel The relation.
162 : : * @param b The second version.
163 : : *
164 : : * @retval true If rel is #DPKG_RELATION_NONE or the expression “a rel b” is
165 : : * true.
166 : : * @retval false Otherwise.
167 : : *
168 : : * @warning If rel is not a valid relation, this function will terminate
169 : : * the program.
170 : : */
171 : : bool
172 : 16 : dpkg_version_relate(const struct dpkg_version *a,
173 : : enum dpkg_relation rel,
174 : : const struct dpkg_version *b)
175 : : {
176 : : int rc;
177 : :
178 [ + + ]: 16 : if (rel == DPKG_RELATION_NONE)
179 : 1 : return true;
180 : :
181 : 15 : rc = dpkg_version_compare(a, b);
182 : :
183 [ + + + + : 15 : switch (rel) {
+ - ]
184 : 3 : case DPKG_RELATION_EQ:
185 : 3 : return rc == 0;
186 : 3 : case DPKG_RELATION_LT:
187 : 3 : return rc < 0;
188 : 3 : case DPKG_RELATION_LE:
189 : 3 : return rc <= 0;
190 : 3 : case DPKG_RELATION_GT:
191 : 3 : return rc > 0;
192 : 3 : case DPKG_RELATION_GE:
193 : 3 : return rc >= 0;
194 : 0 : default:
195 : 0 : internerr("unknown dpkg_relation %d", rel);
196 : : }
197 : : return false;
198 : : }
|