Bug 1919083 - [ci] Enable os-integration variant for more suites, r=jmaher
[gecko.git] / xpcom / base / nsVersionComparator.cpp
blob94a7a8c04515f12c8ad111595100252476cb1d3b
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/. */
7 #include "nsVersionComparator.h"
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdint.h>
12 #include <errno.h>
13 #include "mozilla/CheckedInt.h"
14 #if defined(XP_WIN)
15 # if !defined(UPDATER_NO_STRING_GLUE_STL)
16 # include <wchar.h>
17 # include "nsString.h"
18 # endif
19 # include "nsAlgorithm.h"
20 #endif
22 struct VersionPart {
23 int32_t numA;
25 const char* strB; // NOT null-terminated, can be a null pointer
26 uint32_t strBlen;
28 int32_t numC;
30 char* extraD; // null-terminated
33 #ifdef XP_WIN
34 struct VersionPartW {
35 int32_t numA;
37 wchar_t* strB; // NOT null-terminated, can be a null pointer
38 uint32_t strBlen;
40 int32_t numC;
42 wchar_t* extraD; // null-terminated
44 #endif
46 static int32_t ns_strtol(const char* aPart, char** aNext) {
47 errno = 0;
48 long result_long = strtol(aPart, aNext, 10);
50 // Different platforms seem to disagree on what to return when the value
51 // is out of range so we ensure that it is always what we want it to be.
52 // We choose 0 firstly because that is the default when the number doesn't
53 // exist at all and also because it would be easier to recover from should
54 // you somehow end up in a situation where an old version is invalid. It is
55 // much easier to create a version either larger or smaller than 0, much
56 // harder to do the same with INT_MAX.
57 if (errno != 0) {
58 return 0;
61 mozilla::CheckedInt<int32_t> result = result_long;
62 if (!result.isValid()) {
63 return 0;
66 return result.value();
69 /**
70 * Parse a version part into a number and "extra text".
72 * @returns A pointer to the next versionpart, or null if none.
74 static char* ParseVP(char* aPart, VersionPart& aResult) {
75 char* dot;
77 aResult.numA = 0;
78 aResult.strB = nullptr;
79 aResult.strBlen = 0;
80 aResult.numC = 0;
81 aResult.extraD = nullptr;
83 if (!aPart) {
84 return aPart;
87 dot = strchr(aPart, '.');
88 if (dot) {
89 *dot = '\0';
92 if (aPart[0] == '*' && aPart[1] == '\0') {
93 aResult.numA = INT32_MAX;
94 aResult.strB = "";
95 } else {
96 aResult.numA = ns_strtol(aPart, const_cast<char**>(&aResult.strB));
99 if (!*aResult.strB) {
100 aResult.strB = nullptr;
101 aResult.strBlen = 0;
102 } else {
103 if (aResult.strB[0] == '+') {
104 static const char kPre[] = "pre";
106 ++aResult.numA;
107 aResult.strB = kPre;
108 aResult.strBlen = sizeof(kPre) - 1;
109 } else {
110 const char* numstart = strpbrk(aResult.strB, "0123456789+-");
111 if (!numstart) {
112 aResult.strBlen = strlen(aResult.strB);
113 } else {
114 aResult.strBlen = numstart - aResult.strB;
115 aResult.numC = ns_strtol(numstart, const_cast<char**>(&aResult.extraD));
117 if (!*aResult.extraD) {
118 aResult.extraD = nullptr;
124 if (dot) {
125 ++dot;
127 if (!*dot) {
128 dot = nullptr;
132 return dot;
136 * Parse a version part into a number and "extra text".
138 * @returns A pointer to the next versionpart, or null if none.
140 #ifdef XP_WIN
142 static int32_t ns_wcstol(const wchar_t* aPart, wchar_t** aNext) {
143 errno = 0;
144 long result_long = wcstol(aPart, aNext, 10);
146 // See above for the rationale for using 0 here.
147 if (errno != 0) {
148 return 0;
151 mozilla::CheckedInt<int32_t> result = result_long;
152 if (!result.isValid()) {
153 return 0;
156 return result.value();
159 static wchar_t* ParseVP(wchar_t* aPart, VersionPartW& aResult) {
160 wchar_t* dot;
162 aResult.numA = 0;
163 aResult.strB = nullptr;
164 aResult.strBlen = 0;
165 aResult.numC = 0;
166 aResult.extraD = nullptr;
168 if (!aPart) {
169 return aPart;
172 dot = wcschr(aPart, '.');
173 if (dot) {
174 *dot = '\0';
177 if (aPart[0] == '*' && aPart[1] == '\0') {
178 static wchar_t kEmpty[] = L"";
180 aResult.numA = INT32_MAX;
181 aResult.strB = kEmpty;
182 } else {
183 aResult.numA = ns_wcstol(aPart, const_cast<wchar_t**>(&aResult.strB));
186 if (!*aResult.strB) {
187 aResult.strB = nullptr;
188 aResult.strBlen = 0;
189 } else {
190 if (aResult.strB[0] == '+') {
191 static wchar_t kPre[] = L"pre";
193 ++aResult.numA;
194 aResult.strB = kPre;
195 aResult.strBlen = sizeof(kPre) - 1;
196 } else {
197 const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-");
198 if (!numstart) {
199 aResult.strBlen = wcslen(aResult.strB);
200 } else {
201 aResult.strBlen = numstart - aResult.strB;
202 aResult.numC =
203 ns_wcstol(numstart, const_cast<wchar_t**>(&aResult.extraD));
205 if (!*aResult.extraD) {
206 aResult.extraD = nullptr;
212 if (dot) {
213 ++dot;
215 if (!*dot) {
216 dot = nullptr;
220 return dot;
222 #endif
224 // compare two null-terminated strings, which may be null pointers
225 static int32_t ns_strcmp(const char* aStr1, const char* aStr2) {
226 // any string is *before* no string
227 if (!aStr1) {
228 return aStr2 != 0;
231 if (!aStr2) {
232 return -1;
235 return strcmp(aStr1, aStr2);
238 // compare two length-specified string, which may be null pointers
239 static int32_t ns_strnncmp(const char* aStr1, uint32_t aLen1, const char* aStr2,
240 uint32_t aLen2) {
241 // any string is *before* no string
242 if (!aStr1) {
243 return aStr2 != 0;
246 if (!aStr2) {
247 return -1;
250 for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
251 if (*aStr1 < *aStr2) {
252 return -1;
255 if (*aStr1 > *aStr2) {
256 return 1;
260 if (aLen1 == 0) {
261 return aLen2 == 0 ? 0 : -1;
264 return 1;
267 // compare two int32_t
268 static int32_t ns_cmp(int32_t aNum1, int32_t aNum2) {
269 if (aNum1 < aNum2) {
270 return -1;
273 return aNum1 != aNum2;
277 * Compares two VersionParts
279 static int32_t CompareVP(VersionPart& aVer1, VersionPart& aVer2) {
280 int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
281 if (r) {
282 return r;
285 r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
286 if (r) {
287 return r;
290 r = ns_cmp(aVer1.numC, aVer2.numC);
291 if (r) {
292 return r;
295 return ns_strcmp(aVer1.extraD, aVer2.extraD);
299 * Compares two VersionParts
301 #ifdef XP_WIN
302 static int32_t CompareVP(VersionPartW& aVer1, VersionPartW& aVer2) {
303 int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
304 if (r) {
305 return r;
308 r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen));
309 if (r) {
310 return r;
313 r = ns_cmp(aVer1.numC, aVer2.numC);
314 if (r) {
315 return r;
318 if (!aVer1.extraD) {
319 return aVer2.extraD != 0;
322 if (!aVer2.extraD) {
323 return -1;
326 return wcscmp(aVer1.extraD, aVer2.extraD);
328 #endif
330 namespace mozilla {
332 #ifdef XP_WIN
333 int32_t CompareVersions(const char16_t* aStrA, const char16_t* aStrB) {
334 wchar_t* A2 = wcsdup(char16ptr_t(aStrA));
335 if (!A2) {
336 return 1;
339 wchar_t* B2 = wcsdup(char16ptr_t(aStrB));
340 if (!B2) {
341 free(A2);
342 return 1;
345 int32_t result;
346 wchar_t* a = A2;
347 wchar_t* b = B2;
349 do {
350 VersionPartW va, vb;
352 a = ParseVP(a, va);
353 b = ParseVP(b, vb);
355 result = CompareVP(va, vb);
356 if (result) {
357 break;
360 } while (a || b);
362 free(A2);
363 free(B2);
365 return result;
367 #endif
369 int32_t CompareVersions(const char* aStrA, const char* aStrB) {
370 char* A2 = strdup(aStrA);
371 if (!A2) {
372 return 1;
375 char* B2 = strdup(aStrB);
376 if (!B2) {
377 free(A2);
378 return 1;
381 int32_t result;
382 char* a = A2;
383 char* b = B2;
385 do {
386 VersionPart va, vb;
388 a = ParseVP(a, va);
389 b = ParseVP(b, vb);
391 result = CompareVP(va, vb);
392 if (result) {
393 break;
396 } while (a || b);
398 free(A2);
399 free(B2);
401 return result;
404 } // namespace mozilla