mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	# ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D35622 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			401 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			401 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "nsVersionComparator.h"
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <errno.h>
 | 
						|
#include "mozilla/CheckedInt.h"
 | 
						|
#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
 | 
						|
#  include <wchar.h>
 | 
						|
#  include "nsString.h"
 | 
						|
#endif
 | 
						|
 | 
						|
struct VersionPart {
 | 
						|
  int32_t numA;
 | 
						|
 | 
						|
  const char* strB;  // NOT null-terminated, can be a null pointer
 | 
						|
  uint32_t strBlen;
 | 
						|
 | 
						|
  int32_t numC;
 | 
						|
 | 
						|
  char* extraD;  // null-terminated
 | 
						|
};
 | 
						|
 | 
						|
#ifdef XP_WIN
 | 
						|
struct VersionPartW {
 | 
						|
  int32_t numA;
 | 
						|
 | 
						|
  wchar_t* strB;  // NOT null-terminated, can be a null pointer
 | 
						|
  uint32_t strBlen;
 | 
						|
 | 
						|
  int32_t numC;
 | 
						|
 | 
						|
  wchar_t* extraD;  // null-terminated
 | 
						|
};
 | 
						|
#endif
 | 
						|
 | 
						|
static int32_t ns_strtol(const char* aPart, char** aNext) {
 | 
						|
  errno = 0;
 | 
						|
  long result_long = strtol(aPart, aNext, 10);
 | 
						|
 | 
						|
  // Different platforms seem to disagree on what to return when the value
 | 
						|
  // is out of range so we ensure that it is always what we want it to be.
 | 
						|
  // We choose 0 firstly because that is the default when the number doesn't
 | 
						|
  // exist at all and also because it would be easier to recover from should
 | 
						|
  // you somehow end up in a situation where an old version is invalid. It is
 | 
						|
  // much easier to create a version either larger or smaller than 0, much
 | 
						|
  // harder to do the same with INT_MAX.
 | 
						|
  if (errno != 0) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  mozilla::CheckedInt<int32_t> result = result_long;
 | 
						|
  if (!result.isValid()) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return result.value();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse a version part into a number and "extra text".
 | 
						|
 *
 | 
						|
 * @returns A pointer to the next versionpart, or null if none.
 | 
						|
 */
 | 
						|
static char* ParseVP(char* aPart, VersionPart& aResult) {
 | 
						|
  char* dot;
 | 
						|
 | 
						|
  aResult.numA = 0;
 | 
						|
  aResult.strB = nullptr;
 | 
						|
  aResult.strBlen = 0;
 | 
						|
  aResult.numC = 0;
 | 
						|
  aResult.extraD = nullptr;
 | 
						|
 | 
						|
  if (!aPart) {
 | 
						|
    return aPart;
 | 
						|
  }
 | 
						|
 | 
						|
  dot = strchr(aPart, '.');
 | 
						|
  if (dot) {
 | 
						|
    *dot = '\0';
 | 
						|
  }
 | 
						|
 | 
						|
  if (aPart[0] == '*' && aPart[1] == '\0') {
 | 
						|
    aResult.numA = INT32_MAX;
 | 
						|
    aResult.strB = "";
 | 
						|
  } else {
 | 
						|
    aResult.numA = ns_strtol(aPart, const_cast<char**>(&aResult.strB));
 | 
						|
  }
 | 
						|
 | 
						|
  if (!*aResult.strB) {
 | 
						|
    aResult.strB = nullptr;
 | 
						|
    aResult.strBlen = 0;
 | 
						|
  } else {
 | 
						|
    if (aResult.strB[0] == '+') {
 | 
						|
      static const char kPre[] = "pre";
 | 
						|
 | 
						|
      ++aResult.numA;
 | 
						|
      aResult.strB = kPre;
 | 
						|
      aResult.strBlen = sizeof(kPre) - 1;
 | 
						|
    } else {
 | 
						|
      const char* numstart = strpbrk(aResult.strB, "0123456789+-");
 | 
						|
      if (!numstart) {
 | 
						|
        aResult.strBlen = strlen(aResult.strB);
 | 
						|
      } else {
 | 
						|
        aResult.strBlen = numstart - aResult.strB;
 | 
						|
        aResult.numC = ns_strtol(numstart, const_cast<char**>(&aResult.extraD));
 | 
						|
 | 
						|
        if (!*aResult.extraD) {
 | 
						|
          aResult.extraD = nullptr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (dot) {
 | 
						|
    ++dot;
 | 
						|
 | 
						|
    if (!*dot) {
 | 
						|
      dot = nullptr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return dot;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse a version part into a number and "extra text".
 | 
						|
 *
 | 
						|
 * @returns A pointer to the next versionpart, or null if none.
 | 
						|
 */
 | 
						|
#ifdef XP_WIN
 | 
						|
 | 
						|
static int32_t ns_wcstol(const wchar_t* aPart, wchar_t** aNext) {
 | 
						|
  errno = 0;
 | 
						|
  long result_long = wcstol(aPart, aNext, 10);
 | 
						|
 | 
						|
  // See above for the rationale for using 0 here.
 | 
						|
  if (errno != 0) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  mozilla::CheckedInt<int32_t> result = result_long;
 | 
						|
  if (!result.isValid()) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return result.value();
 | 
						|
}
 | 
						|
 | 
						|
static wchar_t* ParseVP(wchar_t* aPart, VersionPartW& aResult) {
 | 
						|
  wchar_t* dot;
 | 
						|
 | 
						|
  aResult.numA = 0;
 | 
						|
  aResult.strB = nullptr;
 | 
						|
  aResult.strBlen = 0;
 | 
						|
  aResult.numC = 0;
 | 
						|
  aResult.extraD = nullptr;
 | 
						|
 | 
						|
  if (!aPart) {
 | 
						|
    return aPart;
 | 
						|
  }
 | 
						|
 | 
						|
  dot = wcschr(aPart, '.');
 | 
						|
  if (dot) {
 | 
						|
    *dot = '\0';
 | 
						|
  }
 | 
						|
 | 
						|
  if (aPart[0] == '*' && aPart[1] == '\0') {
 | 
						|
    static wchar_t kEmpty[] = L"";
 | 
						|
 | 
						|
    aResult.numA = INT32_MAX;
 | 
						|
    aResult.strB = kEmpty;
 | 
						|
  } else {
 | 
						|
    aResult.numA = ns_wcstol(aPart, const_cast<wchar_t**>(&aResult.strB));
 | 
						|
  }
 | 
						|
 | 
						|
  if (!*aResult.strB) {
 | 
						|
    aResult.strB = nullptr;
 | 
						|
    aResult.strBlen = 0;
 | 
						|
  } else {
 | 
						|
    if (aResult.strB[0] == '+') {
 | 
						|
      static wchar_t kPre[] = L"pre";
 | 
						|
 | 
						|
      ++aResult.numA;
 | 
						|
      aResult.strB = kPre;
 | 
						|
      aResult.strBlen = sizeof(kPre) - 1;
 | 
						|
    } else {
 | 
						|
      const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-");
 | 
						|
      if (!numstart) {
 | 
						|
        aResult.strBlen = wcslen(aResult.strB);
 | 
						|
      } else {
 | 
						|
        aResult.strBlen = numstart - aResult.strB;
 | 
						|
        aResult.numC =
 | 
						|
            ns_wcstol(numstart, const_cast<wchar_t**>(&aResult.extraD));
 | 
						|
 | 
						|
        if (!*aResult.extraD) {
 | 
						|
          aResult.extraD = nullptr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (dot) {
 | 
						|
    ++dot;
 | 
						|
 | 
						|
    if (!*dot) {
 | 
						|
      dot = nullptr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return dot;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
// compare two null-terminated strings, which may be null pointers
 | 
						|
static int32_t ns_strcmp(const char* aStr1, const char* aStr2) {
 | 
						|
  // any string is *before* no string
 | 
						|
  if (!aStr1) {
 | 
						|
    return aStr2 != 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aStr2) {
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return strcmp(aStr1, aStr2);
 | 
						|
}
 | 
						|
 | 
						|
// compare two length-specified string, which may be null pointers
 | 
						|
static int32_t ns_strnncmp(const char* aStr1, uint32_t aLen1, const char* aStr2,
 | 
						|
                           uint32_t aLen2) {
 | 
						|
  // any string is *before* no string
 | 
						|
  if (!aStr1) {
 | 
						|
    return aStr2 != 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aStr2) {
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
 | 
						|
    if (*aStr1 < *aStr2) {
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (*aStr1 > *aStr2) {
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (aLen1 == 0) {
 | 
						|
    return aLen2 == 0 ? 0 : -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
// compare two int32_t
 | 
						|
static int32_t ns_cmp(int32_t aNum1, int32_t aNum2) {
 | 
						|
  if (aNum1 < aNum2) {
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return aNum1 != aNum2;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Compares two VersionParts
 | 
						|
 */
 | 
						|
static int32_t CompareVP(VersionPart& aVer1, VersionPart& aVer2) {
 | 
						|
  int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
 | 
						|
  if (r) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
 | 
						|
  if (r) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  r = ns_cmp(aVer1.numC, aVer2.numC);
 | 
						|
  if (r) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  return ns_strcmp(aVer1.extraD, aVer2.extraD);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Compares two VersionParts
 | 
						|
 */
 | 
						|
#ifdef XP_WIN
 | 
						|
static int32_t CompareVP(VersionPartW& aVer1, VersionPartW& aVer2) {
 | 
						|
  int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
 | 
						|
  if (r) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen));
 | 
						|
  if (r) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  r = ns_cmp(aVer1.numC, aVer2.numC);
 | 
						|
  if (r) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aVer1.extraD) {
 | 
						|
    return aVer2.extraD != 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aVer2.extraD) {
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return wcscmp(aVer1.extraD, aVer2.extraD);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
#ifdef XP_WIN
 | 
						|
int32_t CompareVersions(const char16_t* aStrA, const char16_t* aStrB) {
 | 
						|
  wchar_t* A2 = wcsdup(char16ptr_t(aStrA));
 | 
						|
  if (!A2) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  wchar_t* B2 = wcsdup(char16ptr_t(aStrB));
 | 
						|
  if (!B2) {
 | 
						|
    free(A2);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  int32_t result;
 | 
						|
  wchar_t* a = A2;
 | 
						|
  wchar_t* b = B2;
 | 
						|
 | 
						|
  do {
 | 
						|
    VersionPartW va, vb;
 | 
						|
 | 
						|
    a = ParseVP(a, va);
 | 
						|
    b = ParseVP(b, vb);
 | 
						|
 | 
						|
    result = CompareVP(va, vb);
 | 
						|
    if (result) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  } while (a || b);
 | 
						|
 | 
						|
  free(A2);
 | 
						|
  free(B2);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
int32_t CompareVersions(const char* aStrA, const char* aStrB) {
 | 
						|
  char* A2 = strdup(aStrA);
 | 
						|
  if (!A2) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  char* B2 = strdup(aStrB);
 | 
						|
  if (!B2) {
 | 
						|
    free(A2);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  int32_t result;
 | 
						|
  char* a = A2;
 | 
						|
  char* b = B2;
 | 
						|
 | 
						|
  do {
 | 
						|
    VersionPart va, vb;
 | 
						|
 | 
						|
    a = ParseVP(a, va);
 | 
						|
    b = ParseVP(b, vb);
 | 
						|
 | 
						|
    result = CompareVP(va, vb);
 | 
						|
    if (result) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  } while (a || b);
 | 
						|
 | 
						|
  free(A2);
 | 
						|
  free(B2);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla
 |