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"
13 #include "mozilla/CheckedInt.h"
15 # if !defined(UPDATER_NO_STRING_GLUE_STL)
17 # include "nsString.h"
19 # include "nsAlgorithm.h"
25 const char* strB
; // NOT null-terminated, can be a null pointer
30 char* extraD
; // null-terminated
37 wchar_t* strB
; // NOT null-terminated, can be a null pointer
42 wchar_t* extraD
; // null-terminated
46 static int32_t ns_strtol(const char* aPart
, char** aNext
) {
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.
61 mozilla::CheckedInt
<int32_t> result
= result_long
;
62 if (!result
.isValid()) {
66 return result
.value();
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
) {
78 aResult
.strB
= nullptr;
81 aResult
.extraD
= nullptr;
87 dot
= strchr(aPart
, '.');
92 if (aPart
[0] == '*' && aPart
[1] == '\0') {
93 aResult
.numA
= INT32_MAX
;
96 aResult
.numA
= ns_strtol(aPart
, const_cast<char**>(&aResult
.strB
));
100 aResult
.strB
= nullptr;
103 if (aResult
.strB
[0] == '+') {
104 static const char kPre
[] = "pre";
108 aResult
.strBlen
= sizeof(kPre
) - 1;
110 const char* numstart
= strpbrk(aResult
.strB
, "0123456789+-");
112 aResult
.strBlen
= strlen(aResult
.strB
);
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;
136 * Parse a version part into a number and "extra text".
138 * @returns A pointer to the next versionpart, or null if none.
142 static int32_t ns_wcstol(const wchar_t* aPart
, wchar_t** aNext
) {
144 long result_long
= wcstol(aPart
, aNext
, 10);
146 // See above for the rationale for using 0 here.
151 mozilla::CheckedInt
<int32_t> result
= result_long
;
152 if (!result
.isValid()) {
156 return result
.value();
159 static wchar_t* ParseVP(wchar_t* aPart
, VersionPartW
& aResult
) {
163 aResult
.strB
= nullptr;
166 aResult
.extraD
= nullptr;
172 dot
= wcschr(aPart
, '.');
177 if (aPart
[0] == '*' && aPart
[1] == '\0') {
178 static wchar_t kEmpty
[] = L
"";
180 aResult
.numA
= INT32_MAX
;
181 aResult
.strB
= kEmpty
;
183 aResult
.numA
= ns_wcstol(aPart
, const_cast<wchar_t**>(&aResult
.strB
));
186 if (!*aResult
.strB
) {
187 aResult
.strB
= nullptr;
190 if (aResult
.strB
[0] == '+') {
191 static wchar_t kPre
[] = L
"pre";
195 aResult
.strBlen
= sizeof(kPre
) - 1;
197 const wchar_t* numstart
= wcspbrk(aResult
.strB
, L
"0123456789+-");
199 aResult
.strBlen
= wcslen(aResult
.strB
);
201 aResult
.strBlen
= numstart
- aResult
.strB
;
203 ns_wcstol(numstart
, const_cast<wchar_t**>(&aResult
.extraD
));
205 if (!*aResult
.extraD
) {
206 aResult
.extraD
= nullptr;
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
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
,
241 // any string is *before* no string
250 for (; aLen1
&& aLen2
; --aLen1
, --aLen2
, ++aStr1
, ++aStr2
) {
251 if (*aStr1
< *aStr2
) {
255 if (*aStr1
> *aStr2
) {
261 return aLen2
== 0 ? 0 : -1;
267 // compare two int32_t
268 static int32_t ns_cmp(int32_t aNum1
, int32_t aNum2
) {
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
);
285 r
= ns_strnncmp(aVer1
.strB
, aVer1
.strBlen
, aVer2
.strB
, aVer2
.strBlen
);
290 r
= ns_cmp(aVer1
.numC
, aVer2
.numC
);
295 return ns_strcmp(aVer1
.extraD
, aVer2
.extraD
);
299 * Compares two VersionParts
302 static int32_t CompareVP(VersionPartW
& aVer1
, VersionPartW
& aVer2
) {
303 int32_t r
= ns_cmp(aVer1
.numA
, aVer2
.numA
);
308 r
= wcsncmp(aVer1
.strB
, aVer2
.strB
, XPCOM_MIN(aVer1
.strBlen
, aVer2
.strBlen
));
313 r
= ns_cmp(aVer1
.numC
, aVer2
.numC
);
319 return aVer2
.extraD
!= 0;
326 return wcscmp(aVer1
.extraD
, aVer2
.extraD
);
333 int32_t CompareVersions(const char16_t
* aStrA
, const char16_t
* aStrB
) {
334 wchar_t* A2
= wcsdup(char16ptr_t(aStrA
));
339 wchar_t* B2
= wcsdup(char16ptr_t(aStrB
));
355 result
= CompareVP(va
, vb
);
369 int32_t CompareVersions(const char* aStrA
, const char* aStrB
) {
370 char* A2
= strdup(aStrA
);
375 char* B2
= strdup(aStrB
);
391 result
= CompareVP(va
, vb
);
404 } // namespace mozilla