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
dpkg_version_blank(struct dpkg_version * version)38 dpkg_version_blank(struct dpkg_version *version)
39 {
40 	version->epoch = 0;
41 	version->version = NULL;
42 	version->revision = NULL;
43 }
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
dpkg_version_is_informative(const struct dpkg_version * version)54 dpkg_version_is_informative(const struct dpkg_version *version)
55 {
56 	return (version->epoch ||
57 	        str_is_set(version->version) ||
58 	        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
order(int c)67 order(int c)
68 {
69 	if (c_isdigit(c))
70 		return 0;
71 	else if (c_isalpha(c))
72 		return c;
73 	else if (c == '~')
74 		return -1;
75 	else if (c)
76 		return c + 256;
77 	else
78 		return 0;
79 }
80 
81 static int
verrevcmp(const char * a,const char * b)82 verrevcmp(const char *a, const char *b)
83 {
84 	if (a == NULL)
85 		a = "";
86 	if (b == NULL)
87 		b = "";
88 
89 	while (*a || *b) {
90 		int first_diff = 0;
91 
92 		while ((*a && !c_isdigit(*a)) || (*b && !c_isdigit(*b))) {
93 			int ac = order(*a);
94 			int bc = order(*b);
95 
96 			if (ac != bc)
97 				return ac - bc;
98 
99 			a++;
100 			b++;
101 		}
102 		while (*a == '0')
103 			a++;
104 		while (*b == '0')
105 			b++;
106 		while (c_isdigit(*a) && c_isdigit(*b)) {
107 			if (!first_diff)
108 				first_diff = *a - *b;
109 			a++;
110 			b++;
111 		}
112 
113 		if (c_isdigit(*a))
114 			return 1;
115 		if (c_isdigit(*b))
116 			return -1;
117 		if (first_diff)
118 			return first_diff;
119 	}
120 
121 	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
dpkg_version_compare(const struct dpkg_version * a,const struct dpkg_version * b)140 dpkg_version_compare(const struct dpkg_version *a,
141                      const struct dpkg_version *b)
142 {
143 	int rc;
144 
145 	if (a->epoch > b->epoch)
146 		return 1;
147 	if (a->epoch < b->epoch)
148 		return -1;
149 
150 	rc = verrevcmp(a->version, b->version);
151 	if (rc)
152 		return rc;
153 
154 	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 the expression “a rel b” is true.
165  * @retval true If rel is #DPKG_RELATION_NONE.
166  * @retval false Otherwise.
167  *
168  * @warning If rel is not a valid relation, this function will terminate
169  *          the program.
170  */
171 bool
dpkg_version_relate(const struct dpkg_version * a,enum dpkg_relation rel,const struct dpkg_version * b)172 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 	if (rel == DPKG_RELATION_NONE)
179 		return true;
180 
181 	rc = dpkg_version_compare(a, b);
182 
183 	switch (rel) {
184 	case DPKG_RELATION_EQ:
185 		return rc == 0;
186 	case DPKG_RELATION_LT:
187 		return rc < 0;
188 	case DPKG_RELATION_LE:
189 		return rc <= 0;
190 	case DPKG_RELATION_GT:
191 		return rc > 0;
192 	case DPKG_RELATION_GE:
193 		return rc >= 0;
194 	default:
195 		internerr("unknown dpkg_relation %d", rel);
196 	}
197 	return false;
198 }
199