dpkg (1.18.25) stretch; urgency=medium
[dpkg] / lib / dpkg / version.c
CommitLineData
1479465f
GJ
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 */
37void
38dpkg_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 */
53bool
54dpkg_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 */
66static int
67order(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
81static int
82verrevcmp(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 */
139int
140dpkg_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 */
171bool
172dpkg_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}