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