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 "nsStringGlue.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*
ParseVP(char * aPart,VersionPart & aResult)50 ParseVP(char* aPart, VersionPart& aResult)
51 {
52 char* dot;
53
54 aResult.numA = 0;
55 aResult.strB = nullptr;
56 aResult.strBlen = 0;
57 aResult.numC = 0;
58 aResult.extraD = nullptr;
59
60 if (!aPart) {
61 return aPart;
62 }
63
64 dot = strchr(aPart, '.');
65 if (dot) {
66 *dot = '\0';
67 }
68
69 if (aPart[0] == '*' && aPart[1] == '\0') {
70 aResult.numA = INT32_MAX;
71 aResult.strB = "";
72 } else {
73 aResult.numA = strtol(aPart, const_cast<char**>(&aResult.strB), 10);
74 }
75
76 if (!*aResult.strB) {
77 aResult.strB = nullptr;
78 aResult.strBlen = 0;
79 } else {
80 if (aResult.strB[0] == '+') {
81 static const char kPre[] = "pre";
82
83 ++aResult.numA;
84 aResult.strB = kPre;
85 aResult.strBlen = sizeof(kPre) - 1;
86 } else {
87 const char* numstart = strpbrk(aResult.strB, "0123456789+-");
88 if (!numstart) {
89 aResult.strBlen = strlen(aResult.strB);
90 } else {
91 aResult.strBlen = numstart - aResult.strB;
92
93 aResult.numC = strtol(numstart, &aResult.extraD, 10);
94 if (!*aResult.extraD) {
95 aResult.extraD = nullptr;
96 }
97 }
98 }
99 }
100
101 if (dot) {
102 ++dot;
103
104 if (!*dot) {
105 dot = nullptr;
106 }
107 }
108
109 return dot;
110 }
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*
ParseVP(wchar_t * aPart,VersionPartW & aResult)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 aResult.numA = INT32_MAX;
142 aResult.strB = L"";
143 } else {
144 aResult.numA = wcstol(aPart, const_cast<wchar_t**>(&aResult.strB), 10);
145 }
146
147 if (!*aResult.strB) {
148 aResult.strB = nullptr;
149 aResult.strBlen = 0;
150 } else {
151 if (aResult.strB[0] == '+') {
152 static wchar_t kPre[] = L"pre";
153
154 ++aResult.numA;
155 aResult.strB = kPre;
156 aResult.strBlen = sizeof(kPre) - 1;
157 } else {
158 const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-");
159 if (!numstart) {
160 aResult.strBlen = wcslen(aResult.strB);
161 } else {
162 aResult.strBlen = numstart - aResult.strB;
163
164 aResult.numC = wcstol(numstart, &aResult.extraD, 10);
165 if (!*aResult.extraD) {
166 aResult.extraD = nullptr;
167 }
168 }
169 }
170 }
171
172 if (dot) {
173 ++dot;
174
175 if (!*dot) {
176 dot = nullptr;
177 }
178 }
179
180 return dot;
181 }
182 #endif
183
184 // compare two null-terminated strings, which may be null pointers
185 static int32_t
ns_strcmp(const char * aStr1,const char * aStr2)186 ns_strcmp(const char* aStr1, const char* aStr2)
187 {
188 // any string is *before* no string
189 if (!aStr1) {
190 return aStr2 != 0;
191 }
192
193 if (!aStr2) {
194 return -1;
195 }
196
197 return strcmp(aStr1, aStr2);
198 }
199
200 // compare two length-specified string, which may be null pointers
201 static int32_t
ns_strnncmp(const char * aStr1,uint32_t aLen1,const char * aStr2,uint32_t aLen2)202 ns_strnncmp(const char* aStr1, uint32_t aLen1,
203 const char* aStr2, uint32_t aLen2)
204 {
205 // any string is *before* no string
206 if (!aStr1) {
207 return aStr2 != 0;
208 }
209
210 if (!aStr2) {
211 return -1;
212 }
213
214 for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
215 if (*aStr1 < *aStr2) {
216 return -1;
217 }
218
219 if (*aStr1 > *aStr2) {
220 return 1;
221 }
222 }
223
224 if (aLen1 == 0) {
225 return aLen2 == 0 ? 0 : -1;
226 }
227
228 return 1;
229 }
230
231 // compare two int32_t
232 static int32_t
ns_cmp(int32_t aNum1,int32_t aNum2)233 ns_cmp(int32_t aNum1, int32_t aNum2)
234 {
235 if (aNum1 < aNum2) {
236 return -1;
237 }
238
239 return aNum1 != aNum2;
240 }
241
242 /**
243 * Compares two VersionParts
244 */
245 static int32_t
CompareVP(VersionPart & aVer1,VersionPart & aVer2)246 CompareVP(VersionPart& aVer1, VersionPart& aVer2)
247 {
248 int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
249 if (r) {
250 return r;
251 }
252
253 r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
254 if (r) {
255 return r;
256 }
257
258 r = ns_cmp(aVer1.numC, aVer2.numC);
259 if (r) {
260 return r;
261 }
262
263 return ns_strcmp(aVer1.extraD, aVer2.extraD);
264 }
265
266 /**
267 * Compares two VersionParts
268 */
269 #ifdef XP_WIN
270 static int32_t
CompareVP(VersionPartW & aVer1,VersionPartW & aVer2)271 CompareVP(VersionPartW& aVer1, VersionPartW& aVer2)
272 {
273 int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
274 if (r) {
275 return r;
276 }
277
278 r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen));
279 if (r) {
280 return r;
281 }
282
283 r = ns_cmp(aVer1.numC, aVer2.numC);
284 if (r) {
285 return r;
286 }
287
288 if (!aVer1.extraD) {
289 return aVer2.extraD != 0;
290 }
291
292 if (!aVer2.extraD) {
293 return -1;
294 }
295
296 return wcscmp(aVer1.extraD, aVer2.extraD);
297 }
298 #endif
299
300 namespace mozilla {
301
302 #ifdef XP_WIN
303 int32_t
CompareVersions(const char16_t * aStrA,const char16_t * aStrB)304 CompareVersions(const char16_t* aStrA, const char16_t* aStrB)
305 {
306 wchar_t* A2 = wcsdup(char16ptr_t(aStrA));
307 if (!A2) {
308 return 1;
309 }
310
311 wchar_t* B2 = wcsdup(char16ptr_t(aStrB));
312 if (!B2) {
313 free(A2);
314 return 1;
315 }
316
317 int32_t result;
318 wchar_t* a = A2;
319 wchar_t* b = B2;
320
321 do {
322 VersionPartW va, vb;
323
324 a = ParseVP(a, va);
325 b = ParseVP(b, vb);
326
327 result = CompareVP(va, vb);
328 if (result) {
329 break;
330 }
331
332 } while (a || b);
333
334 free(A2);
335 free(B2);
336
337 return result;
338 }
339 #endif
340
341 int32_t
CompareVersions(const char * aStrA,const char * aStrB)342 CompareVersions(const char* aStrA, const char* aStrB)
343 {
344 char* A2 = strdup(aStrA);
345 if (!A2) {
346 return 1;
347 }
348
349 char* B2 = strdup(aStrB);
350 if (!B2) {
351 free(A2);
352 return 1;
353 }
354
355 int32_t result;
356 char* a = A2;
357 char* b = B2;
358
359 do {
360 VersionPart va, vb;
361
362 a = ParseVP(a, va);
363 b = ParseVP(b, vb);
364
365 result = CompareVP(va, vb);
366 if (result) {
367 break;
368 }
369
370 } while (a || b);
371
372 free(A2);
373 free(B2);
374
375 return result;
376 }
377
378 } // namespace mozilla
379
380