Line data Source code
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*
50 6236 : ParseVP(char* aPart, VersionPart& aResult)
51 : {
52 : char* dot;
53 :
54 6236 : aResult.numA = 0;
55 6236 : aResult.strB = nullptr;
56 6236 : aResult.strBlen = 0;
57 6236 : aResult.numC = 0;
58 6236 : aResult.extraD = nullptr;
59 :
60 6236 : if (!aPart) {
61 0 : return aPart;
62 : }
63 :
64 6236 : dot = strchr(aPart, '.');
65 6236 : if (dot) {
66 5686 : *dot = '\0';
67 : }
68 :
69 6236 : if (aPart[0] == '*' && aPart[1] == '\0') {
70 0 : aResult.numA = INT32_MAX;
71 0 : aResult.strB = "";
72 : } else {
73 6236 : aResult.numA = strtol(aPart, const_cast<char**>(&aResult.strB), 10);
74 : }
75 :
76 6236 : if (!*aResult.strB) {
77 5688 : aResult.strB = nullptr;
78 5688 : aResult.strBlen = 0;
79 : } else {
80 548 : if (aResult.strB[0] == '+') {
81 : static const char kPre[] = "pre";
82 :
83 0 : ++aResult.numA;
84 0 : aResult.strB = kPre;
85 0 : aResult.strBlen = sizeof(kPre) - 1;
86 : } else {
87 548 : const char* numstart = strpbrk(aResult.strB, "0123456789+-");
88 548 : if (!numstart) {
89 0 : aResult.strBlen = strlen(aResult.strB);
90 : } else {
91 548 : aResult.strBlen = numstart - aResult.strB;
92 :
93 548 : aResult.numC = strtol(numstart, &aResult.extraD, 10);
94 548 : if (!*aResult.extraD) {
95 548 : aResult.extraD = nullptr;
96 : }
97 : }
98 : }
99 : }
100 :
101 6236 : if (dot) {
102 5686 : ++dot;
103 :
104 5686 : if (!*dot) {
105 0 : dot = nullptr;
106 : }
107 : }
108 :
109 6236 : 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*
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
186 548 : ns_strcmp(const char* aStr1, const char* aStr2)
187 : {
188 : // any string is *before* no string
189 548 : if (!aStr1) {
190 548 : return aStr2 != 0;
191 : }
192 :
193 0 : if (!aStr2) {
194 0 : return -1;
195 : }
196 :
197 0 : return strcmp(aStr1, aStr2);
198 : }
199 :
200 : // compare two length-specified string, which may be null pointers
201 : static int32_t
202 548 : ns_strnncmp(const char* aStr1, uint32_t aLen1,
203 : const char* aStr2, uint32_t aLen2)
204 : {
205 : // any string is *before* no string
206 548 : if (!aStr1) {
207 274 : return aStr2 != 0;
208 : }
209 :
210 274 : if (!aStr2) {
211 0 : return -1;
212 : }
213 :
214 822 : for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
215 274 : if (*aStr1 < *aStr2) {
216 0 : return -1;
217 : }
218 :
219 274 : if (*aStr1 > *aStr2) {
220 0 : return 1;
221 : }
222 : }
223 :
224 274 : if (aLen1 == 0) {
225 274 : return aLen2 == 0 ? 0 : -1;
226 : }
227 :
228 0 : return 1;
229 : }
230 :
231 : // compare two int32_t
232 : static int32_t
233 3666 : ns_cmp(int32_t aNum1, int32_t aNum2)
234 : {
235 3666 : if (aNum1 < aNum2) {
236 804 : return -1;
237 : }
238 :
239 2862 : return aNum1 != aNum2;
240 : }
241 :
242 : /**
243 : * Compares two VersionParts
244 : */
245 : static int32_t
246 3118 : CompareVP(VersionPart& aVer1, VersionPart& aVer2)
247 : {
248 3118 : int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
249 3118 : if (r) {
250 2570 : return r;
251 : }
252 :
253 548 : r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
254 548 : if (r) {
255 0 : return r;
256 : }
257 :
258 548 : r = ns_cmp(aVer1.numC, aVer2.numC);
259 548 : if (r) {
260 0 : return r;
261 : }
262 :
263 548 : return ns_strcmp(aVer1.extraD, aVer2.extraD);
264 : }
265 :
266 : /**
267 : * Compares two VersionParts
268 : */
269 : #ifdef XP_WIN
270 : static int32_t
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
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
342 2844 : CompareVersions(const char* aStrA, const char* aStrB)
343 : {
344 2844 : char* A2 = strdup(aStrA);
345 2844 : if (!A2) {
346 0 : return 1;
347 : }
348 :
349 2844 : char* B2 = strdup(aStrB);
350 2844 : if (!B2) {
351 0 : free(A2);
352 0 : return 1;
353 : }
354 :
355 : int32_t result;
356 2844 : char* a = A2;
357 2844 : char* b = B2;
358 :
359 274 : do {
360 : VersionPart va, vb;
361 :
362 3118 : a = ParseVP(a, va);
363 3118 : b = ParseVP(b, vb);
364 :
365 3118 : result = CompareVP(va, vb);
366 3118 : if (result) {
367 2570 : break;
368 : }
369 :
370 548 : } while (a || b);
371 :
372 2844 : free(A2);
373 2844 : free(B2);
374 :
375 2844 : return result;
376 : }
377 :
378 : } // namespace mozilla
379 :
|