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