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