eb3a3435bb6f3ad6a38085912929372669c498favboxsync/* $Id$ */
eb3a3435bb6f3ad6a38085912929372669c498favboxsync/** @file
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * IPRT - Version String Parsing.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync */
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
eb3a3435bb6f3ad6a38085912929372669c498favboxsync/*
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync * Copyright (C) 2009-2010 Oracle Corporation
eb3a3435bb6f3ad6a38085912929372669c498favboxsync *
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * available from http://www.virtualbox.org. This file is free software;
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * you can redistribute it and/or modify it under the terms of the GNU
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * General Public License (GPL) as published by the Free Software
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync *
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * The contents of this file may alternatively be used under the terms
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * of the Common Development and Distribution License Version 1.0
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * VirtualBox OSE distribution, in which case the provisions of the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * CDDL are applicable instead of those of the GPL.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync *
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * You may elect to license modified versions of this file under the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * terms and conditions of either the GPL or the CDDL or both.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync */
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
eb3a3435bb6f3ad6a38085912929372669c498favboxsync/*******************************************************************************
eb3a3435bb6f3ad6a38085912929372669c498favboxsync* Header Files *
eb3a3435bb6f3ad6a38085912929372669c498favboxsync*******************************************************************************/
eb3a3435bb6f3ad6a38085912929372669c498favboxsync#include <iprt/string.h>
eb3a3435bb6f3ad6a38085912929372669c498favboxsync#include "internal/iprt.h"
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
eb3a3435bb6f3ad6a38085912929372669c498favboxsync#include <iprt/assert.h>
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync#include <iprt/ctype.h>
eb3a3435bb6f3ad6a38085912929372669c498favboxsync#include <iprt/err.h>
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
4b783d68e6d569ab798901917ace422a4810edf0vboxsync/*******************************************************************************
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync* Defined Constants And Macros *
4b783d68e6d569ab798901917ace422a4810edf0vboxsync*******************************************************************************/
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync#define RTSTRVER_IS_PUNCTUACTION(ch) \
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync ( (ch) == '_' || (ch) == '-' || (ch) == '+' || RT_C_IS_PUNCT(ch) )
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/**
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * Parses a out the next block from a version string.
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync *
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * @returns true if numeric, false if not.
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * @param ppszVer The string cursor, IN/OUT.
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync * @param pi32Value Where to return the value if numeric.
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * @param pcchBlock Where to return the block length.
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync */
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsyncstatic bool rtStrVersionParseBlock(const char **ppszVer, int32_t *pi32Value, size_t *pcchBlock)
4b783d68e6d569ab798901917ace422a4810edf0vboxsync{
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync const char *psz = *ppszVer;
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync /*
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Check for end-of-string.
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (!*psz)
4b783d68e6d569ab798901917ace422a4810edf0vboxsync {
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync *pi32Value = 0;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync *pcchBlock = 0;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync return false;
4b783d68e6d569ab798901917ace422a4810edf0vboxsync }
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync /*
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Try convert the block to a number the simple way.
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync */
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync char ch;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync bool fNumeric = RT_C_IS_DIGIT(*psz);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (fNumeric)
4b783d68e6d569ab798901917ace422a4810edf0vboxsync {
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync do
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync ch = *++psz;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync while (ch && RT_C_IS_DIGIT(ch));
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync int rc = RTStrToInt32Ex(*ppszVer, NULL, 10, pi32Value);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
4b783d68e6d569ab798901917ace422a4810edf0vboxsync {
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync AssertRC(rc);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync fNumeric = false;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync *pi32Value = 0;
4b783d68e6d569ab798901917ace422a4810edf0vboxsync }
4b783d68e6d569ab798901917ace422a4810edf0vboxsync }
4b783d68e6d569ab798901917ace422a4810edf0vboxsync else
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync {
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync /*
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Find the end of the current string. Make a special case for SVN
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * revision numbers that immediately follows a release tag string.
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync do
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync ch = *++psz;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync while ( ch
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && !RT_C_IS_DIGIT(ch)
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && !RTSTRVER_IS_PUNCTUACTION(ch));
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync size_t cchBlock = psz - *ppszVer;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync if ( cchBlock > 1
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && psz[-1] == 'r'
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && RT_C_IS_DIGIT(*psz))
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync {
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync psz--;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync cchBlock--;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync }
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync /*
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Translate standard pre release terms to negative values.
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync */
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync static const struct
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync {
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync size_t cch;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync const char *psz;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync int32_t iValue;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync } s_aTerms[] =
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync {
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync { 2, "RC", -100000 },
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync { 3, "PRE", -200000 },
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync { 5, "GAMMA", -300000 },
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync { 4, "BETA", -400000 },
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync { 5, "ALPHA", -500000 }
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync };
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync int32_t iVal1 = 0;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync for (unsigned i = 0; i < RT_ELEMENTS(s_aTerms); i++)
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync if ( cchBlock == s_aTerms[i].cch
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && !RTStrNCmp(s_aTerms[i].psz, *ppszVer, cchBlock))
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync {
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync iVal1 = s_aTerms[i].iValue;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync break;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync if (iVal1 != 0)
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync {
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync /*
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Does the prelease term have a trailing number?
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Add it assuming BETA == BETA1.
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync if (RT_C_IS_DIGIT(*psz))
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync {
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync const char *psz2 = psz;
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync do
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync ch = *++psz;
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync while ( ch
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && RT_C_IS_DIGIT(ch)
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync && !RTSTRVER_IS_PUNCTUACTION(ch));
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync int rc = RTStrToInt32Ex(psz2, NULL, 10, pi32Value);
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync if (RT_SUCCESS(rc) && rc != VWRN_NUMBER_TOO_BIG && *pi32Value)
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync iVal1 += *pi32Value - 1;
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync else
469215c767ddc89620e8e5edf36b3cdf7be4dad8vboxsync {
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync AssertRC(rc);
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync psz = psz2;
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync fNumeric = true;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync *pi32Value = iVal1;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync *pcchBlock = psz - *ppszVer;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync /*
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync * Skip trailing punctuation.
f859334e89cff63a2472f1a95a28e77bc07667f1vboxsync */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (RTSTRVER_IS_PUNCTUACTION(*psz))
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync psz++;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync *ppszVer = psz;
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync return fNumeric;
4b783d68e6d569ab798901917ace422a4810edf0vboxsync}
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
eb3a3435bb6f3ad6a38085912929372669c498favboxsync
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsyncRTDECL(int) RTStrVersionCompare(const char *pszVer1, const char *pszVer2)
eb3a3435bb6f3ad6a38085912929372669c498favboxsync{
4b783d68e6d569ab798901917ace422a4810edf0vboxsync AssertPtr(pszVer1);
4b783d68e6d569ab798901917ace422a4810edf0vboxsync AssertPtr(pszVer2);
4b783d68e6d569ab798901917ace422a4810edf0vboxsync
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync /*
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * Do a parallel parse of the strings.
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync while (*pszVer1 || *pszVer2)
eb3a3435bb6f3ad6a38085912929372669c498favboxsync {
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync const char *pszBlock1 = pszVer1;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync size_t cchBlock1;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync int32_t iVal1;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync bool fNumeric1 = rtStrVersionParseBlock(&pszVer1, &iVal1, &cchBlock1);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync const char *pszBlock2 = pszVer2;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync size_t cchBlock2;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync int32_t iVal2;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync bool fNumeric2 = rtStrVersionParseBlock(&pszVer2, &iVal2, &cchBlock2);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (fNumeric1 && fNumeric2)
eb3a3435bb6f3ad6a38085912929372669c498favboxsync {
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync if (iVal1 != iVal2)
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync return iVal1 < iVal2 ? -1 : 1;
eb3a3435bb6f3ad6a38085912929372669c498favboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync else if ( fNumeric1 != fNumeric2
469215c767ddc89620e8e5edf36b3cdf7be4dad8vboxsync && ( fNumeric1
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync ? iVal1 == 0 && cchBlock2 == 0
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync : iVal2 == 0 && cchBlock1 == 0)
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync )
eb3a3435bb6f3ad6a38085912929372669c498favboxsync {
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync /*else: 1.0 == 1.0.0.0.0. */;
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync }
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync else if ( fNumeric1 != fNumeric2
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync && (fNumeric1 ? iVal1 : iVal2) < 0)
469215c767ddc89620e8e5edf36b3cdf7be4dad8vboxsync {
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync /* Pre-release indicators are smaller than all other strings. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync return fNumeric1 ? -1 : 1;
eb3a3435bb6f3ad6a38085912929372669c498favboxsync }
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync else
4b783d68e6d569ab798901917ace422a4810edf0vboxsync {
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync int iDiff = RTStrNICmp(pszBlock1, pszBlock2, RT_MIN(cchBlock1, cchBlock2));
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (!iDiff && cchBlock1 != cchBlock2)
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync iDiff = cchBlock1 < cchBlock2 ? -1 : 1;
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync if (iDiff)
467aa5ec73ac24ab34a04b46ea812955566910dcvboxsync return iDiff < 0 ? -1 : 1;
4b783d68e6d569ab798901917ace422a4810edf0vboxsync }
eb3a3435bb6f3ad6a38085912929372669c498favboxsync }
467aa5ec73ac24ab34a04b46ea812955566910dcvboxsync return 0;
eb3a3435bb6f3ad6a38085912929372669c498favboxsync}
4b783d68e6d569ab798901917ace422a4810edf0vboxsyncRT_EXPORT_SYMBOL(RTStrVersionCompare);