/src/mozilla-central/xpcom/base/nsVersionComparator.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsVersionComparator.h" |
8 | | |
9 | | #include <stdlib.h> |
10 | | #include <string.h> |
11 | | #include <stdint.h> |
12 | | #if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL) |
13 | | #include <wchar.h> |
14 | | #include "nsString.h" |
15 | | #endif |
16 | | |
17 | | struct VersionPart |
18 | | { |
19 | | int32_t numA; |
20 | | |
21 | | const char* strB; // NOT null-terminated, can be a null pointer |
22 | | uint32_t strBlen; |
23 | | |
24 | | int32_t numC; |
25 | | |
26 | | char* extraD; // null-terminated |
27 | | }; |
28 | | |
29 | | #ifdef XP_WIN |
30 | | struct VersionPartW |
31 | | { |
32 | | int32_t numA; |
33 | | |
34 | | wchar_t* strB; // NOT null-terminated, can be a null pointer |
35 | | uint32_t strBlen; |
36 | | |
37 | | int32_t numC; |
38 | | |
39 | | wchar_t* extraD; // null-terminated |
40 | | |
41 | | }; |
42 | | #endif |
43 | | |
44 | | /** |
45 | | * Parse a version part into a number and "extra text". |
46 | | * |
47 | | * @returns A pointer to the next versionpart, or null if none. |
48 | | */ |
49 | | static char* |
50 | | ParseVP(char* aPart, VersionPart& aResult) |
51 | 300 | { |
52 | 300 | char* dot; |
53 | 300 | |
54 | 300 | aResult.numA = 0; |
55 | 300 | aResult.strB = nullptr; |
56 | 300 | aResult.strBlen = 0; |
57 | 300 | aResult.numC = 0; |
58 | 300 | aResult.extraD = nullptr; |
59 | 300 | |
60 | 300 | if (!aPart) { |
61 | 0 | return aPart; |
62 | 0 | } |
63 | 300 | |
64 | 300 | dot = strchr(aPart, '.'); |
65 | 300 | if (dot) { |
66 | 288 | *dot = '\0'; |
67 | 288 | } |
68 | 300 | |
69 | 300 | if (aPart[0] == '*' && aPart[1] == '\0') { |
70 | 0 | aResult.numA = INT32_MAX; |
71 | 0 | aResult.strB = ""; |
72 | 300 | } else { |
73 | 300 | aResult.numA = strtol(aPart, const_cast<char**>(&aResult.strB), 10); |
74 | 300 | } |
75 | 300 | |
76 | 300 | if (!*aResult.strB) { |
77 | 288 | aResult.strB = nullptr; |
78 | 288 | aResult.strBlen = 0; |
79 | 288 | } else { |
80 | 12 | if (aResult.strB[0] == '+') { |
81 | 0 | static const char kPre[] = "pre"; |
82 | 0 |
|
83 | 0 | ++aResult.numA; |
84 | 0 | aResult.strB = kPre; |
85 | 0 | aResult.strBlen = sizeof(kPre) - 1; |
86 | 12 | } else { |
87 | 12 | const char* numstart = strpbrk(aResult.strB, "0123456789+-"); |
88 | 12 | if (!numstart) { |
89 | 0 | aResult.strBlen = strlen(aResult.strB); |
90 | 12 | } else { |
91 | 12 | aResult.strBlen = numstart - aResult.strB; |
92 | 12 | |
93 | 12 | aResult.numC = strtol(numstart, &aResult.extraD, 10); |
94 | 12 | if (!*aResult.extraD) { |
95 | 12 | aResult.extraD = nullptr; |
96 | 12 | } |
97 | 12 | } |
98 | 12 | } |
99 | 12 | } |
100 | 300 | |
101 | 300 | if (dot) { |
102 | 288 | ++dot; |
103 | 288 | |
104 | 288 | if (!*dot) { |
105 | 0 | dot = nullptr; |
106 | 0 | } |
107 | 288 | } |
108 | 300 | |
109 | 300 | return dot; |
110 | 300 | } |
111 | | |
112 | | |
113 | | /** |
114 | | * Parse a version part into a number and "extra text". |
115 | | * |
116 | | * @returns A pointer to the next versionpart, or null if none. |
117 | | */ |
118 | | #ifdef XP_WIN |
119 | | static wchar_t* |
120 | | ParseVP(wchar_t* aPart, VersionPartW& aResult) |
121 | | { |
122 | | |
123 | | wchar_t* dot; |
124 | | |
125 | | aResult.numA = 0; |
126 | | aResult.strB = nullptr; |
127 | | aResult.strBlen = 0; |
128 | | aResult.numC = 0; |
129 | | aResult.extraD = nullptr; |
130 | | |
131 | | if (!aPart) { |
132 | | return aPart; |
133 | | } |
134 | | |
135 | | dot = wcschr(aPart, '.'); |
136 | | if (dot) { |
137 | | *dot = '\0'; |
138 | | } |
139 | | |
140 | | if (aPart[0] == '*' && aPart[1] == '\0') { |
141 | | static wchar_t kEmpty[] = L""; |
142 | | |
143 | | aResult.numA = INT32_MAX; |
144 | | aResult.strB = kEmpty; |
145 | | } else { |
146 | | aResult.numA = wcstol(aPart, const_cast<wchar_t**>(&aResult.strB), 10); |
147 | | } |
148 | | |
149 | | if (!*aResult.strB) { |
150 | | aResult.strB = nullptr; |
151 | | aResult.strBlen = 0; |
152 | | } else { |
153 | | if (aResult.strB[0] == '+') { |
154 | | static wchar_t kPre[] = L"pre"; |
155 | | |
156 | | ++aResult.numA; |
157 | | aResult.strB = kPre; |
158 | | aResult.strBlen = sizeof(kPre) - 1; |
159 | | } else { |
160 | | const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-"); |
161 | | if (!numstart) { |
162 | | aResult.strBlen = wcslen(aResult.strB); |
163 | | } else { |
164 | | aResult.strBlen = numstart - aResult.strB; |
165 | | |
166 | | aResult.numC = wcstol(numstart, &aResult.extraD, 10); |
167 | | if (!*aResult.extraD) { |
168 | | aResult.extraD = nullptr; |
169 | | } |
170 | | } |
171 | | } |
172 | | } |
173 | | |
174 | | if (dot) { |
175 | | ++dot; |
176 | | |
177 | | if (!*dot) { |
178 | | dot = nullptr; |
179 | | } |
180 | | } |
181 | | |
182 | | return dot; |
183 | | } |
184 | | #endif |
185 | | |
186 | | // compare two null-terminated strings, which may be null pointers |
187 | | static int32_t |
188 | | ns_strcmp(const char* aStr1, const char* aStr2) |
189 | 12 | { |
190 | 12 | // any string is *before* no string |
191 | 12 | if (!aStr1) { |
192 | 12 | return aStr2 != 0; |
193 | 12 | } |
194 | 0 | |
195 | 0 | if (!aStr2) { |
196 | 0 | return -1; |
197 | 0 | } |
198 | 0 | |
199 | 0 | return strcmp(aStr1, aStr2); |
200 | 0 | } |
201 | | |
202 | | // compare two length-specified string, which may be null pointers |
203 | | static int32_t |
204 | | ns_strnncmp(const char* aStr1, uint32_t aLen1, |
205 | | const char* aStr2, uint32_t aLen2) |
206 | 12 | { |
207 | 12 | // any string is *before* no string |
208 | 12 | if (!aStr1) { |
209 | 6 | return aStr2 != 0; |
210 | 6 | } |
211 | 6 | |
212 | 6 | if (!aStr2) { |
213 | 0 | return -1; |
214 | 0 | } |
215 | 6 | |
216 | 12 | for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) { |
217 | 6 | if (*aStr1 < *aStr2) { |
218 | 0 | return -1; |
219 | 0 | } |
220 | 6 | |
221 | 6 | if (*aStr1 > *aStr2) { |
222 | 0 | return 1; |
223 | 0 | } |
224 | 6 | } |
225 | 6 | |
226 | 6 | if (aLen1 == 0) { |
227 | 6 | return aLen2 == 0 ? 0 : -1; |
228 | 6 | } |
229 | 0 | |
230 | 0 | return 1; |
231 | 0 | } |
232 | | |
233 | | // compare two int32_t |
234 | | static int32_t |
235 | | ns_cmp(int32_t aNum1, int32_t aNum2) |
236 | 162 | { |
237 | 162 | if (aNum1 < aNum2) { |
238 | 6 | return -1; |
239 | 6 | } |
240 | 156 | |
241 | 156 | return aNum1 != aNum2; |
242 | 156 | } |
243 | | |
244 | | /** |
245 | | * Compares two VersionParts |
246 | | */ |
247 | | static int32_t |
248 | | CompareVP(VersionPart& aVer1, VersionPart& aVer2) |
249 | 150 | { |
250 | 150 | int32_t r = ns_cmp(aVer1.numA, aVer2.numA); |
251 | 150 | if (r) { |
252 | 138 | return r; |
253 | 138 | } |
254 | 12 | |
255 | 12 | r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen); |
256 | 12 | if (r) { |
257 | 0 | return r; |
258 | 0 | } |
259 | 12 | |
260 | 12 | r = ns_cmp(aVer1.numC, aVer2.numC); |
261 | 12 | if (r) { |
262 | 0 | return r; |
263 | 0 | } |
264 | 12 | |
265 | 12 | return ns_strcmp(aVer1.extraD, aVer2.extraD); |
266 | 12 | } |
267 | | |
268 | | /** |
269 | | * Compares two VersionParts |
270 | | */ |
271 | | #ifdef XP_WIN |
272 | | static int32_t |
273 | | CompareVP(VersionPartW& aVer1, VersionPartW& aVer2) |
274 | | { |
275 | | int32_t r = ns_cmp(aVer1.numA, aVer2.numA); |
276 | | if (r) { |
277 | | return r; |
278 | | } |
279 | | |
280 | | r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen)); |
281 | | if (r) { |
282 | | return r; |
283 | | } |
284 | | |
285 | | r = ns_cmp(aVer1.numC, aVer2.numC); |
286 | | if (r) { |
287 | | return r; |
288 | | } |
289 | | |
290 | | if (!aVer1.extraD) { |
291 | | return aVer2.extraD != 0; |
292 | | } |
293 | | |
294 | | if (!aVer2.extraD) { |
295 | | return -1; |
296 | | } |
297 | | |
298 | | return wcscmp(aVer1.extraD, aVer2.extraD); |
299 | | } |
300 | | #endif |
301 | | |
302 | | namespace mozilla { |
303 | | |
304 | | #ifdef XP_WIN |
305 | | int32_t |
306 | | CompareVersions(const char16_t* aStrA, const char16_t* aStrB) |
307 | | { |
308 | | wchar_t* A2 = wcsdup(char16ptr_t(aStrA)); |
309 | | if (!A2) { |
310 | | return 1; |
311 | | } |
312 | | |
313 | | wchar_t* B2 = wcsdup(char16ptr_t(aStrB)); |
314 | | if (!B2) { |
315 | | free(A2); |
316 | | return 1; |
317 | | } |
318 | | |
319 | | int32_t result; |
320 | | wchar_t* a = A2; |
321 | | wchar_t* b = B2; |
322 | | |
323 | | do { |
324 | | VersionPartW va, vb; |
325 | | |
326 | | a = ParseVP(a, va); |
327 | | b = ParseVP(b, vb); |
328 | | |
329 | | result = CompareVP(va, vb); |
330 | | if (result) { |
331 | | break; |
332 | | } |
333 | | |
334 | | } while (a || b); |
335 | | |
336 | | free(A2); |
337 | | free(B2); |
338 | | |
339 | | return result; |
340 | | } |
341 | | #endif |
342 | | |
343 | | int32_t |
344 | | CompareVersions(const char* aStrA, const char* aStrB) |
345 | 144 | { |
346 | 144 | char* A2 = strdup(aStrA); |
347 | 144 | if (!A2) { |
348 | 0 | return 1; |
349 | 0 | } |
350 | 144 | |
351 | 144 | char* B2 = strdup(aStrB); |
352 | 144 | if (!B2) { |
353 | 0 | free(A2); |
354 | 0 | return 1; |
355 | 0 | } |
356 | 144 | |
357 | 144 | int32_t result; |
358 | 144 | char* a = A2; |
359 | 144 | char* b = B2; |
360 | 144 | |
361 | 150 | do { |
362 | 150 | VersionPart va, vb; |
363 | 150 | |
364 | 150 | a = ParseVP(a, va); |
365 | 150 | b = ParseVP(b, vb); |
366 | 150 | |
367 | 150 | result = CompareVP(va, vb); |
368 | 150 | if (result) { |
369 | 138 | break; |
370 | 138 | } |
371 | 12 | |
372 | 12 | } while (a || b); |
373 | 144 | |
374 | 144 | free(A2); |
375 | 144 | free(B2); |
376 | 144 | |
377 | 144 | return result; |
378 | 144 | } |
379 | | |
380 | | } // namespace mozilla |
381 | | |