VirtualBox

Changeset 24893 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Nov 24, 2009 12:09:03 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
55167
Message:

IPRT: Improved RTStrVersionCompare() + testcase.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/string/strversion.cpp

    r24662 r24893  
    3737
    3838#include <iprt/assert.h>
    39 #include <iprt/ctype.h> /* needed for RT_C_IS_DIGIT */
    4039#include <iprt/err.h>
    4140#include <iprt/mem.h>
    4241
    4342
     43/*******************************************************************************
     44*   Defined Constants                                                          *
     45*******************************************************************************/
     46#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
     47
     48
     49/*******************************************************************************
     50*   Internal Functions                                                         *
     51*******************************************************************************/
     52static uint16_t RTStrVersionGetBlockCount(const char *pszVer)
     53{
     54    uint16_t l = 0;
     55    const char *pszCur = pszVer;
     56    while (pszCur = RTStrStr(pszCur, "."))
     57    {
     58        if (pszCur == NULL)
     59            break;
     60        l++;
     61        pszCur++;
     62    }
     63    /* Adjust block count to also count in the very first block */
     64    if (*pszVer != '\0')
     65        l++;
     66    return l;
     67}
     68
     69
     70static int RTStrVersionGetUInt32(const char *pszVer, uint16_t u16Block, uint32_t *pu32)
     71{
     72    /* First make a copy of the version string so that we can modify it */
     73    char *pszString;
     74    int rc = RTStrDupEx(&pszString, pszVer);
     75    if (RT_FAILURE(rc))
     76        return rc;
     77
     78    /* Go to the beginning of the block we want to parse */
     79    char *pszCur = pszString;
     80    for (uint16_t i = 0; i < u16Block; i++)
     81    {
     82        pszCur = RTStrStr(pszCur, ".");
     83        if (pszCur == NULL)
     84            break;
     85        if (*pszCur != '\0')
     86            pszCur++;
     87    }
     88
     89    if (pszCur != NULL && *pszCur != '\0')
     90    {
     91        /* Skip trailing non-digits at the start of the block */
     92        while (pszCur && *pszCur != '\0')
     93        {
     94            if (ISDIGIT(*pszCur))
     95                break;
     96            pszCur++;
     97        }
     98
     99        /* Mark ending of the block */
     100        char *pszEnd = RTStrStr(pszCur, ".");
     101        if (NULL != pszEnd)
     102            *pszEnd = '\0';
     103
     104        /* Convert to number */
     105        rc = RTStrToUInt32Ex(pszCur, NULL /* ppszNext */, 10 /* Base */, pu32);
     106        /* Skip trailing warnings */
     107        if (   rc == VWRN_TRAILING_CHARS
     108            || rc == VWRN_TRAILING_SPACES)
     109            rc = VINF_SUCCESS;
     110    }
     111    else
     112        rc = VERR_NOT_FOUND;
     113
     114    RTStrFree(pszString);
     115    return rc;
     116}
     117
    44118
    45119/**
    46  * Converts a string representation of a version number to an unsigned number.
     120 * Compares two version strings and returns the result. The version string has
     121 * to be made of at least one number section, each section delimited by a ".",
     122 * e.g. "123.45.67". Trailing zeros at the beginning and non-digits in a section
     123 * will be skipped, so "12.foo006" becomes "12.6".
    47124 *
    48125 * @returns iprt status code.
     
    55132 * @retval  VERR_NO_DIGITS
    56133 *
    57  * @param   pszValue    Pointer to the string value.
    58  * @param   pu32        Where to store the converted number.
    59  *
    60  * @todo    r=bird: The returned value isn't really suitable for comparing two
    61  *          version strings.  Try see which result you get when converting
    62  *          "3.0.14" and "3.1.0" and comparing the values.  The way to fix this
    63  *          deficiency would be to convert the individual parts and dividing the
    64  *          return value into sections: bits 31:24 FirstNumber; 23:16 Second;
    65  *          15:8 Third; 7:0 Forth.  It would probably be a good idea to use a
    66  *          64-bit return value instead of a 32-bit one, so there is room for
    67  *          revision number when found.
    68  *
    69  *          Actually, because of the above, the kind of API I had in mind was
    70  *          int RTStrVersionCompare(const char *pszVer1, const char *pszVer2).
    71  *          It wouldn't try convert input to numbers, just do a parallel parse.
    72  *          This would allow easy handling beta/alpha/++ indicators and any
    73  *          number of dots and dashes.
     134 * @todo    Deal with prefixes and suffixes!
     135 * @param   pszVer1     First version string to compare.
     136 * @param   pszVer2     First version string to compare.*
     137 * @param   pui8Res     Pointer uint8_t value where to store the comparison result:
     138 *                      0 if equal, 1 if pszVer1 is greater, 2 if pszVer2 is greater.
    74139 */
    75 RTDECL(int) RTStrVersionToUInt32(const char *pszVer, uint32_t *pu32)
     140int RTStrVersionCompare(const char *pszVer1, const char *pszVer2, uint8_t *pui8Res)
    76141{
    77     const char *psz = pszVer;
    78     AssertPtr(pu32);
    79     AssertPtr(psz);
    80 
    81     char *pszNew = (char*)RTMemAllocZ((strlen(pszVer) + 1) * sizeof(char));
    82     if (pszNew == NULL)
    83         return VERR_NO_MEMORY;
    84 
    85     unsigned    i            = 0;
    86     bool        fLastInvalid = false;
    87     while (    psz
    88            && *psz != '\0')
    89     {
    90         if (fLastInvalid)
    91         {
    92             if (   *psz == '-'
    93                 || *psz == '_')
    94                 fLastInvalid = false;
    95         }
    96         else
    97         {
    98             if (RT_C_IS_DIGIT(*psz))
    99                 pszNew[i++] = *psz;
    100             else if (   *psz != '.'
    101                      && i == 0)
    102                 fLastInvalid = true;
    103         }
    104         psz++;
    105     }
    106     pszNew[i] = '\0';
    107 
    108     /* Convert final number string to number */
    109     int rc;
    110     if (fLastInvalid)
    111     {
    112         *pu32 = 0;
     142    AssertPtr(pszVer1);
     143    AssertPtr(pszVer2);
     144
     145    uint16_t len1 = RTStrVersionGetBlockCount(pszVer1);
     146    uint16_t len2 = RTStrVersionGetBlockCount(pszVer2);
     147
     148    int rc = 0;
     149    if (len1 > 0 && len2 > 0)
     150    {
     151        /* Figure out which version string is longer and set the corresponding
     152         * pointers */
     153        uint16_t range;
     154        uint16_t padding;
     155        const char *pszShorter, *pszLonger;
     156        if (len1 >= len2)
     157        {
     158            range = len1;
     159            padding = len1 - len2;
     160            pszLonger = pszVer1;
     161            pszShorter = pszVer2;
     162        }
     163        else if (len2 > len1)
     164        {
     165            range = len2;
     166            padding = len2 - len1;
     167            pszLonger = pszVer2;
     168            pszShorter = pszVer1;
     169        }
     170
     171        /* Now process each section (delimited by a ".") */
     172        AssertPtr(pszShorter);
     173        AssertPtr(pszLonger);
     174        AssertPtr(pui8Res);
     175        *pui8Res = 0;
     176        uint32_t val1, val2;
     177        for (uint16_t i = 0;    i < range
     178                             && *pui8Res == 0
     179                             && RT_SUCCESS(rc)
     180                           ; i++)
     181        {
     182            rc = RTStrVersionGetUInt32(pszLonger, i, &val1);
     183            if (RT_SUCCESS(rc))
     184            {
     185                if (i >= range - padding)
     186                {
     187                    /* If we're in the padding range, there are no numbers left
     188                     * to compare with anymore, so just assume "0" then */
     189                    val2 = 0;
     190                }
     191                else
     192                {
     193                    rc = RTStrVersionGetUInt32(pszShorter, i, &val2);
     194                }
     195            }
     196
     197            if (RT_SUCCESS(rc))
     198            {
     199                if (val1 > val2)
     200                {
     201                    *pui8Res = (pszLonger == pszVer1) ? 1 : 2;
     202                    break;
     203                }
     204                else if (val2 > val1)
     205                {
     206                    *pui8Res = (pszShorter == pszVer1) ? 1 : 2;
     207                    break;
     208                }
     209            }
     210        }
     211    }
     212    else
     213    {
    113214        rc = VERR_NO_DIGITS;
    114215    }
    115     else
    116     {
    117         rc = RTStrToUInt32Ex(pszNew, NULL /*pszNext*/, 10 /*uBase*/, pu32);
    118         if (rc != VINF_SUCCESS)
    119             *pu32 = 0;
    120     }
    121     RTStrFree(pszNew);
     216
     217    if (RT_FAILURE(rc))
     218        *pui8Res = 0; /* Zero out value */
    122219    return rc;
    123220}
    124 
     221RT_EXPORT_SYMBOL(RTStrVersionCompare);
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette