michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsVersionComparator.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL) michael@0: #include michael@0: #include "nsStringGlue.h" michael@0: #endif michael@0: michael@0: struct VersionPart { michael@0: int32_t numA; michael@0: michael@0: const char *strB; // NOT null-terminated, can be a null pointer michael@0: uint32_t strBlen; michael@0: michael@0: int32_t numC; michael@0: michael@0: char *extraD; // null-terminated michael@0: }; michael@0: michael@0: #ifdef XP_WIN michael@0: struct VersionPartW { michael@0: int32_t numA; michael@0: michael@0: wchar_t *strB; // NOT null-terminated, can be a null pointer michael@0: uint32_t strBlen; michael@0: michael@0: int32_t numC; michael@0: michael@0: wchar_t *extraD; // null-terminated michael@0: michael@0: }; michael@0: #endif michael@0: michael@0: /** michael@0: * Parse a version part into a number and "extra text". michael@0: * michael@0: * @returns A pointer to the next versionpart, or null if none. michael@0: */ michael@0: static char* michael@0: ParseVP(char *part, VersionPart &result) michael@0: { michael@0: char *dot; michael@0: michael@0: result.numA = 0; michael@0: result.strB = nullptr; michael@0: result.strBlen = 0; michael@0: result.numC = 0; michael@0: result.extraD = nullptr; michael@0: michael@0: if (!part) michael@0: return part; michael@0: michael@0: dot = strchr(part, '.'); michael@0: if (dot) michael@0: *dot = '\0'; michael@0: michael@0: if (part[0] == '*' && part[1] == '\0') { michael@0: result.numA = INT32_MAX; michael@0: result.strB = ""; michael@0: } michael@0: else { michael@0: result.numA = strtol(part, const_cast(&result.strB), 10); michael@0: } michael@0: michael@0: if (!*result.strB) { michael@0: result.strB = nullptr; michael@0: result.strBlen = 0; michael@0: } michael@0: else { michael@0: if (result.strB[0] == '+') { michael@0: static const char kPre[] = "pre"; michael@0: michael@0: ++result.numA; michael@0: result.strB = kPre; michael@0: result.strBlen = sizeof(kPre) - 1; michael@0: } michael@0: else { michael@0: const char *numstart = strpbrk(result.strB, "0123456789+-"); michael@0: if (!numstart) { michael@0: result.strBlen = strlen(result.strB); michael@0: } michael@0: else { michael@0: result.strBlen = numstart - result.strB; michael@0: michael@0: result.numC = strtol(numstart, &result.extraD, 10); michael@0: if (!*result.extraD) michael@0: result.extraD = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (dot) { michael@0: ++dot; michael@0: michael@0: if (!*dot) michael@0: dot = nullptr; michael@0: } michael@0: michael@0: return dot; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Parse a version part into a number and "extra text". michael@0: * michael@0: * @returns A pointer to the next versionpart, or null if none. michael@0: */ michael@0: #ifdef XP_WIN michael@0: static wchar_t* michael@0: ParseVP(wchar_t *part, VersionPartW &result) michael@0: { michael@0: michael@0: wchar_t *dot; michael@0: michael@0: result.numA = 0; michael@0: result.strB = nullptr; michael@0: result.strBlen = 0; michael@0: result.numC = 0; michael@0: result.extraD = nullptr; michael@0: michael@0: if (!part) michael@0: return part; michael@0: michael@0: dot = wcschr(part, '.'); michael@0: if (dot) michael@0: *dot = '\0'; michael@0: michael@0: if (part[0] == '*' && part[1] == '\0') { michael@0: result.numA = INT32_MAX; michael@0: result.strB = L""; michael@0: } michael@0: else { michael@0: result.numA = wcstol(part, const_cast(&result.strB), 10); michael@0: } michael@0: michael@0: if (!*result.strB) { michael@0: result.strB = nullptr; michael@0: result.strBlen = 0; michael@0: } michael@0: else { michael@0: if (result.strB[0] == '+') { michael@0: static wchar_t kPre[] = L"pre"; michael@0: michael@0: ++result.numA; michael@0: result.strB = kPre; michael@0: result.strBlen = sizeof(kPre) - 1; michael@0: } michael@0: else { michael@0: const wchar_t *numstart = wcspbrk(result.strB, L"0123456789+-"); michael@0: if (!numstart) { michael@0: result.strBlen = wcslen(result.strB); michael@0: } michael@0: else { michael@0: result.strBlen = numstart - result.strB; michael@0: michael@0: result.numC = wcstol(numstart, &result.extraD, 10); michael@0: if (!*result.extraD) michael@0: result.extraD = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (dot) { michael@0: ++dot; michael@0: michael@0: if (!*dot) michael@0: dot = nullptr; michael@0: } michael@0: michael@0: return dot; michael@0: } michael@0: #endif michael@0: michael@0: // compare two null-terminated strings, which may be null pointers michael@0: static int32_t michael@0: ns_strcmp(const char *str1, const char *str2) michael@0: { michael@0: // any string is *before* no string michael@0: if (!str1) michael@0: return str2 != 0; michael@0: michael@0: if (!str2) michael@0: return -1; michael@0: michael@0: return strcmp(str1, str2); michael@0: } michael@0: michael@0: // compare two length-specified string, which may be null pointers michael@0: static int32_t michael@0: ns_strnncmp(const char *str1, uint32_t len1, const char *str2, uint32_t len2) michael@0: { michael@0: // any string is *before* no string michael@0: if (!str1) michael@0: return str2 != 0; michael@0: michael@0: if (!str2) michael@0: return -1; michael@0: michael@0: for (; len1 && len2; --len1, --len2, ++str1, ++str2) { michael@0: if (*str1 < *str2) michael@0: return -1; michael@0: michael@0: if (*str1 > *str2) michael@0: return 1; michael@0: } michael@0: michael@0: if (len1 == 0) michael@0: return len2 == 0 ? 0 : -1; michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: // compare two int32_t michael@0: static int32_t michael@0: ns_cmp(int32_t n1, int32_t n2) michael@0: { michael@0: if (n1 < n2) michael@0: return -1; michael@0: michael@0: return n1 != n2; michael@0: } michael@0: michael@0: /** michael@0: * Compares two VersionParts michael@0: */ michael@0: static int32_t michael@0: CompareVP(VersionPart &v1, VersionPart &v2) michael@0: { michael@0: int32_t r = ns_cmp(v1.numA, v2.numA); michael@0: if (r) michael@0: return r; michael@0: michael@0: r = ns_strnncmp(v1.strB, v1.strBlen, v2.strB, v2.strBlen); michael@0: if (r) michael@0: return r; michael@0: michael@0: r = ns_cmp(v1.numC, v2.numC); michael@0: if (r) michael@0: return r; michael@0: michael@0: return ns_strcmp(v1.extraD, v2.extraD); michael@0: } michael@0: michael@0: /** michael@0: * Compares two VersionParts michael@0: */ michael@0: #ifdef XP_WIN michael@0: static int32_t michael@0: CompareVP(VersionPartW &v1, VersionPartW &v2) michael@0: { michael@0: int32_t r = ns_cmp(v1.numA, v2.numA); michael@0: if (r) michael@0: return r; michael@0: michael@0: r = wcsncmp(v1.strB, v2.strB, XPCOM_MIN(v1.strBlen,v2.strBlen)); michael@0: if (r) michael@0: return r; michael@0: michael@0: r = ns_cmp(v1.numC, v2.numC); michael@0: if (r) michael@0: return r; michael@0: michael@0: if (!v1.extraD) michael@0: return v2.extraD != 0; michael@0: michael@0: if (!v2.extraD) michael@0: return -1; michael@0: michael@0: return wcscmp(v1.extraD, v2.extraD); michael@0: } michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: #ifdef XP_WIN michael@0: int32_t michael@0: CompareVersions(const char16_t *A, const char16_t *B) michael@0: { michael@0: wchar_t *A2 = wcsdup(char16ptr_t(A)); michael@0: if (!A2) michael@0: return 1; michael@0: michael@0: wchar_t *B2 = wcsdup(char16ptr_t(B)); michael@0: if (!B2) { michael@0: free(A2); michael@0: return 1; michael@0: } michael@0: michael@0: int32_t result; michael@0: wchar_t *a = A2, *b = B2; michael@0: michael@0: do { michael@0: VersionPartW va, vb; michael@0: michael@0: a = ParseVP(a, va); michael@0: b = ParseVP(b, vb); michael@0: michael@0: result = CompareVP(va, vb); michael@0: if (result) michael@0: break; michael@0: michael@0: } while (a || b); michael@0: michael@0: free(A2); michael@0: free(B2); michael@0: michael@0: return result; michael@0: } michael@0: #endif michael@0: michael@0: int32_t michael@0: CompareVersions(const char *A, const char *B) michael@0: { michael@0: char *A2 = strdup(A); michael@0: if (!A2) michael@0: return 1; michael@0: michael@0: char *B2 = strdup(B); michael@0: if (!B2) { michael@0: free(A2); michael@0: return 1; michael@0: } michael@0: michael@0: int32_t result; michael@0: char *a = A2, *b = B2; michael@0: michael@0: do { michael@0: VersionPart va, vb; michael@0: michael@0: a = ParseVP(a, va); michael@0: b = ParseVP(b, vb); michael@0: michael@0: result = CompareVP(va, vb); michael@0: if (result) michael@0: break; michael@0: michael@0: } while (a || b); michael@0: michael@0: free(A2); michael@0: free(B2); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: