1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is Mozilla XPCOM.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Benjamin Smedberg <benjamin@smedbergs.us>.
18 : *
19 : * Portions created by the Initial Developer are Copyright (C) 2005
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsVersionComparator.h"
39 :
40 : #include <stdlib.h>
41 : #include <string.h>
42 : #if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
43 : #include <wchar.h>
44 : #include "nsStringGlue.h"
45 : #endif
46 :
47 : struct VersionPart {
48 : PRInt32 numA;
49 :
50 : const char *strB; // NOT null-terminated, can be a null pointer
51 : PRUint32 strBlen;
52 :
53 : PRInt32 numC;
54 :
55 : char *extraD; // null-terminated
56 : };
57 :
58 : #ifdef XP_WIN
59 : struct VersionPartW {
60 : PRInt32 numA;
61 :
62 : const PRUnichar *strB; // NOT null-terminated, can be a null pointer
63 : PRUint32 strBlen;
64 :
65 : PRInt32 numC;
66 :
67 : PRUnichar *extraD; // null-terminated
68 :
69 : };
70 : #endif
71 :
72 : /**
73 : * Parse a version part into a number and "extra text".
74 : *
75 : * @returns A pointer to the next versionpart, or null if none.
76 : */
77 : static char*
78 39172 : ParseVP(char *part, VersionPart &result)
79 : {
80 : char *dot;
81 :
82 39172 : result.numA = 0;
83 39172 : result.strB = nsnull;
84 39172 : result.strBlen = 0;
85 39172 : result.numC = 0;
86 39172 : result.extraD = nsnull;
87 :
88 39172 : if (!part)
89 1082 : return part;
90 :
91 38090 : dot = strchr(part, '.');
92 38090 : if (dot)
93 9038 : *dot = '\0';
94 :
95 38090 : if (part[0] == '*' && part[1] == '\0') {
96 408 : result.numA = PR_INT32_MAX;
97 408 : result.strB = "";
98 : }
99 : else {
100 37682 : result.numA = strtol(part, const_cast<char**>(&result.strB), 10);
101 : }
102 :
103 38090 : if (!*result.strB) {
104 37875 : result.strB = nsnull;
105 37875 : result.strBlen = 0;
106 : }
107 : else {
108 215 : if (result.strB[0] == '+') {
109 : static const char kPre[] = "pre";
110 :
111 10 : ++result.numA;
112 10 : result.strB = kPre;
113 10 : result.strBlen = sizeof(kPre) - 1;
114 : }
115 : else {
116 205 : const char *numstart = strpbrk(result.strB, "0123456789+-");
117 205 : if (!numstart) {
118 42 : result.strBlen = strlen(result.strB);
119 : }
120 : else {
121 163 : result.strBlen = numstart - result.strB;
122 :
123 163 : result.numC = strtol(numstart, &result.extraD, 10);
124 163 : if (!*result.extraD)
125 110 : result.extraD = nsnull;
126 : }
127 : }
128 : }
129 :
130 38090 : if (dot) {
131 9038 : ++dot;
132 :
133 9038 : if (!*dot)
134 0 : dot = nsnull;
135 : }
136 :
137 38090 : return dot;
138 : }
139 :
140 :
141 : /**
142 : * Parse a version part into a number and "extra text".
143 : *
144 : * @returns A pointer to the next versionpart, or null if none.
145 : */
146 : #ifdef XP_WIN
147 : static PRUnichar*
148 : ParseVP(PRUnichar *part, VersionPartW &result)
149 : {
150 :
151 : PRUnichar *dot;
152 :
153 : result.numA = 0;
154 : result.strB = nsnull;
155 : result.strBlen = 0;
156 : result.numC = 0;
157 : result.extraD = nsnull;
158 :
159 : if (!part)
160 : return part;
161 :
162 : dot = wcschr(part, '.');
163 : if (dot)
164 : *dot = '\0';
165 :
166 : if (part[0] == '*' && part[1] == '\0') {
167 : result.numA = PR_INT32_MAX;
168 : result.strB = L"";
169 : }
170 : else {
171 : result.numA = wcstol(part, const_cast<PRUnichar**>(&result.strB), 10);
172 : }
173 :
174 : if (!*result.strB) {
175 : result.strB = nsnull;
176 : result.strBlen = 0;
177 : }
178 : else {
179 : if (result.strB[0] == '+') {
180 : static const PRUnichar kPre[] = L"pre";
181 :
182 : ++result.numA;
183 : result.strB = kPre;
184 : result.strBlen = sizeof(kPre) - 1;
185 : }
186 : else {
187 : const PRUnichar *numstart = wcspbrk(result.strB, L"0123456789+-");
188 : if (!numstart) {
189 : result.strBlen = wcslen(result.strB);
190 : }
191 : else {
192 : result.strBlen = numstart - result.strB;
193 :
194 : result.numC = wcstol(numstart, &result.extraD, 10);
195 : if (!*result.extraD)
196 : result.extraD = nsnull;
197 : }
198 : }
199 : }
200 :
201 : if (dot) {
202 : ++dot;
203 :
204 : if (!*dot)
205 : dot = nsnull;
206 : }
207 :
208 : return dot;
209 : }
210 : #endif
211 :
212 : // compare two null-terminated strings, which may be null pointers
213 : static PRInt32
214 8856 : ns_strcmp(const char *str1, const char *str2)
215 : {
216 : // any string is *before* no string
217 8856 : if (!str1)
218 8852 : return str2 != 0;
219 :
220 4 : if (!str2)
221 2 : return -1;
222 :
223 2 : return strcmp(str1, str2);
224 : }
225 :
226 : // compare two length-specified string, which may be null pointers
227 : static PRInt32
228 8920 : ns_strnncmp(const char *str1, PRUint32 len1, const char *str2, PRUint32 len2)
229 : {
230 : // any string is *before* no string
231 8920 : if (!str1)
232 8858 : return str2 != 0;
233 :
234 62 : if (!str2)
235 24 : return -1;
236 :
237 152 : for (; len1 && len2; --len1, --len2, ++str1, ++str2) {
238 114 : if (*str1 < *str2)
239 0 : return -1;
240 :
241 114 : if (*str1 > *str2)
242 0 : return 1;
243 : }
244 :
245 38 : if (len1 == 0)
246 38 : return len2 == 0 ? 0 : -1;
247 :
248 0 : return 1;
249 : }
250 :
251 : // compare two PRInt32
252 : static PRInt32
253 28460 : ns_cmp(PRInt32 n1, PRInt32 n2)
254 : {
255 28460 : if (n1 < n2)
256 7012 : return -1;
257 :
258 21448 : return n1 != n2;
259 : }
260 :
261 : /**
262 : * Compares two VersionParts
263 : */
264 : static PRInt32
265 19586 : CompareVP(VersionPart &v1, VersionPart &v2)
266 : {
267 19586 : PRInt32 r = ns_cmp(v1.numA, v2.numA);
268 19586 : if (r)
269 10666 : return r;
270 :
271 8920 : r = ns_strnncmp(v1.strB, v1.strBlen, v2.strB, v2.strBlen);
272 8920 : if (r)
273 46 : return r;
274 :
275 8874 : r = ns_cmp(v1.numC, v2.numC);
276 8874 : if (r)
277 18 : return r;
278 :
279 8856 : return ns_strcmp(v1.extraD, v2.extraD);
280 : }
281 :
282 : /**
283 : * Compares two VersionParts
284 : */
285 : #ifdef XP_WIN
286 : static PRInt32
287 : CompareVP(VersionPartW &v1, VersionPartW &v2)
288 : {
289 : PRInt32 r = ns_cmp(v1.numA, v2.numA);
290 : if (r)
291 : return r;
292 :
293 : r = wcsncmp(v1.strB, v2.strB, NS_MIN(v1.strBlen,v2.strBlen));
294 : if (r)
295 : return r;
296 :
297 : r = ns_cmp(v1.numC, v2.numC);
298 : if (r)
299 : return r;
300 :
301 : if (!v1.extraD)
302 : return v2.extraD != 0;
303 :
304 : if (!v2.extraD)
305 : return -1;
306 :
307 : return wcscmp(v1.extraD, v2.extraD);
308 : }
309 :
310 :
311 : PRInt32
312 : NS_CompareVersions(const PRUnichar *A, const PRUnichar *B)
313 : {
314 : PRUnichar *A2 = wcsdup(A);
315 : if (!A2)
316 : return 1;
317 :
318 : PRUnichar *B2 = wcsdup(B);
319 : if (!B2) {
320 : free(A2);
321 : return 1;
322 : }
323 :
324 : PRInt32 result;
325 : PRUnichar *a = A2, *b = B2;
326 :
327 : do {
328 : VersionPartW va, vb;
329 :
330 : a = ParseVP(a, va);
331 : b = ParseVP(b, vb);
332 :
333 : result = CompareVP(va, vb);
334 : if (result)
335 : break;
336 :
337 : } while (a || b);
338 :
339 : free(A2);
340 : free(B2);
341 :
342 : return result;
343 : }
344 : #endif
345 :
346 : PRInt32
347 17603 : NS_CompareVersions(const char *A, const char *B)
348 : {
349 17603 : char *A2 = strdup(A);
350 17603 : if (!A2)
351 0 : return 1;
352 :
353 17603 : char *B2 = strdup(B);
354 17603 : if (!B2) {
355 0 : free(A2);
356 0 : return 1;
357 : }
358 :
359 : PRInt32 result;
360 17603 : char *a = A2, *b = B2;
361 :
362 8852 : do {
363 : VersionPart va, vb;
364 :
365 19586 : a = ParseVP(a, va);
366 19586 : b = ParseVP(b, vb);
367 :
368 19586 : result = CompareVP(va, vb);
369 19586 : if (result)
370 10734 : break;
371 :
372 : } while (a || b);
373 :
374 17603 : free(A2);
375 17603 : free(B2);
376 :
377 17603 : return result;
378 : }
379 :
|