Changeset 25005 in vbox for trunk/src/VBox/Runtime
- Timestamp:
- Nov 26, 2009 2:42:59 PM (15 years ago)
- Location:
- trunk/src/VBox/Runtime
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/string/strversion.cpp
r24894 r25005 37 37 38 38 #include <iprt/assert.h> 39 #include <iprt/ctype.h> 39 40 #include <iprt/err.h> 40 #include <iprt/mem.h>41 41 42 42 43 43 /******************************************************************************* 44 * Defined Constants 44 * Defined Constants And Macros * 45 45 *******************************************************************************/ 46 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9') 46 #define RTSTRVER_IS_PUNCTUACTION(ch) \ 47 ( (ch) == '_' || (ch) == '-' || (ch) == '+' || RT_C_IS_PUNCT(ch) ) 47 48 48 49 49 /******************************************************************************* 50 * Internal Functions * 51 *******************************************************************************/ 52 static uint16_t RTStrVersionGetBlockCount(const char *pszVer) 50 /** 51 * Parses a out the next block from a version string. 52 * 53 * @returns true if numeric, false if not. 54 * @param ppszVer The string cursor, IN/OUT. 55 * @param pu32Value Where to return the value if numeric. 56 * @param pcchBlock Where to return the block length. 57 */ 58 static bool rtStrVersionParseBlock(const char **ppszVer, uint32_t *pu32Value, size_t *pcchBlock) 53 59 { 54 uint16_t l = 0; 55 const char *pszCur = pszVer; 56 while (pszCur = RTStrStr(pszCur, ".")) 60 const char *psz = *ppszVer; 61 62 /* Check for end-of-string. */ 63 if (!*psz) 57 64 { 58 if (pszCur == NULL) 59 break; 60 l++; 61 pszCur++; 65 *pu32Value = 0; 66 *pcchBlock = 0; 67 return false; 62 68 } 63 /* Adjust block count to also count in the very first block */ 64 if (*pszVer != '\0') 65 l++; 66 return l; 69 70 bool fNumeric = RT_C_IS_DIGIT(*psz); 71 if (fNumeric) 72 { 73 do 74 psz++; 75 while (*psz && RT_C_IS_DIGIT(*psz)); 76 77 char *pszNext; 78 int rc = RTStrToUInt32Ex(*ppszVer, &pszNext, 10, pu32Value); 79 AssertRC(rc); 80 Assert(pszNext == psz); 81 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG) 82 { 83 fNumeric = false; 84 *pu32Value = 0; 85 } 86 } 87 else 88 { 89 do 90 psz++; 91 while (*psz && !RT_C_IS_DIGIT(*psz) && !RTSTRVER_IS_PUNCTUACTION(*psz)); 92 *pu32Value = 0; 93 } 94 *pcchBlock = psz - *ppszVer; 95 96 /* skip punctuation */ 97 if (RTSTRVER_IS_PUNCTUACTION(*psz)) 98 psz++; 99 *ppszVer = psz; 100 101 return fNumeric; 67 102 } 68 103 69 104 70 static 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 118 119 /** 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". 124 * 125 * @returns iprt status code. 126 * Warnings are used to indicate convertion problems. 127 * @retval VWRN_NUMBER_TOO_BIG 128 * @retval VWRN_TRAILING_CHARS 129 * @retval VWRN_TRAILING_SPACES 130 * @retval VINF_SUCCESS 131 * @retval VERR_NO_MEMORY 132 * @retval VERR_NO_DIGITS 133 * 134 * @param pszVer1 First version string to compare. 135 * @param pszVer2 First version string to compare. 136 * @param pui8Res Pointer uint8_t value where to store the comparison result: 137 * 0 if equal, 1 if pszVer1 is greater, 2 if pszVer2 is greater. 138 */ 139 int RTStrVersionCompare(const char *pszVer1, const char *pszVer2, uint8_t *pui8Res) 105 RTDECL(int) RTStrVersionCompare(const char *pszVer1, const char *pszVer2) 140 106 { 141 107 AssertPtr(pszVer1); 142 108 AssertPtr(pszVer2); 143 109 144 uint16_t len1 = RTStrVersionGetBlockCount(pszVer1); 145 uint16_t len2 = RTStrVersionGetBlockCount(pszVer2); 110 /* 111 * Do a parallel parse of the strings. 112 */ 113 int iRes = 0; 114 while (*pszVer1 || *pszVer2) 115 { 116 const char *pszBlock1 = pszVer1; 117 size_t cchBlock1; 118 uint32_t uVal1; 119 bool fNumeric1 = rtStrVersionParseBlock(&pszVer1, &uVal1, &cchBlock1); 146 120 147 int rc = 0; 148 if (len1 > 0 && len2 > 0) 149 { 150 /* Figure out which version string is longer and set the corresponding 151 * pointers */ 152 uint16_t range; 153 uint16_t padding; 154 const char *pszShorter, *pszLonger; 155 if (len1 >= len2) 121 const char *pszBlock2 = pszVer2; 122 size_t cchBlock2; 123 uint32_t uVal2; 124 bool fNumeric2 = rtStrVersionParseBlock(&pszVer2, &uVal2, &cchBlock2); 125 126 if (fNumeric1 && fNumeric2) 156 127 { 157 range = len1; 158 padding = len1 - len2; 159 pszLonger = pszVer1; 160 pszShorter = pszVer2; 128 if (uVal1 != uVal2) 129 { 130 iRes = uVal1 > uVal2 ? 1 : 2; 131 break; 132 } 161 133 } 162 else if (len2 > len1) 134 else if ( !fNumeric1 && fNumeric2 && uVal2 == 0 && cchBlock1 == 0 135 || !fNumeric2 && fNumeric1 && uVal1 == 0 && cchBlock2 == 0 136 ) 163 137 { 164 range = len2; 165 padding = len2 - len1; 166 pszLonger = pszVer2; 167 pszShorter = pszVer1; 138 /* 1.0 == 1.0.0.0.0. */; 168 139 } 169 170 /* Now process each section (delimited by a ".") */ 171 AssertPtr(pszShorter); 172 AssertPtr(pszLonger); 173 AssertPtr(pui8Res); 174 *pui8Res = 0; 175 uint32_t val1, val2; 176 for (uint16_t i = 0; i < range 177 && *pui8Res == 0 178 && RT_SUCCESS(rc) 179 ; i++) 140 else 180 141 { 181 rc = RTStrVersionGetUInt32(pszLonger, i, &val1); 182 if (RT_SUCCESS(rc)) 142 int iDiff = RTStrNICmp(pszBlock1, pszBlock2, RT_MIN(cchBlock1, cchBlock2)); 143 if (!iDiff && cchBlock1 != cchBlock2) 144 iDiff = cchBlock1 < cchBlock2 ? -1 : 1; 145 if (iDiff) 183 146 { 184 if (i >= range - padding) 185 { 186 /* If we're in the padding range, there are no numbers left 187 * to compare with anymore, so just assume "0" then */ 188 val2 = 0; 189 } 190 else 191 { 192 rc = RTStrVersionGetUInt32(pszShorter, i, &val2); 193 } 194 } 195 196 if (RT_SUCCESS(rc)) 197 { 198 if (val1 > val2) 199 { 200 *pui8Res = (pszLonger == pszVer1) ? 1 : 2; 201 break; 202 } 203 else if (val2 > val1) 204 { 205 *pui8Res = (pszShorter == pszVer1) ? 1 : 2; 206 break; 207 } 147 iRes = iDiff > 0 ? 1 : 2; 148 break; 208 149 } 209 150 } 210 151 } 211 else 212 { 213 rc = VERR_NO_DIGITS; 214 } 215 216 if (RT_FAILURE(rc)) 217 *pui8Res = 0; /* Zero out value */ 218 return rc; 152 return iRes; 219 153 } 220 154 RT_EXPORT_SYMBOL(RTStrVersionCompare); -
trunk/src/VBox/Runtime/testcase/tstStrToVer.cpp
r24893 r25005 39 39 const char *pszVer1; 40 40 const char *pszVer2; 41 int rc;42 41 uint8_t Result; 43 42 }; … … 47 46 do \ 48 47 { \ 49 Type Result; \ 50 int rc = Fun(Test.pszVer1, Test.pszVer2, &Result); \ 48 Type Result = Fun(Test.pszVer1, Test.pszVer2); \ 51 49 if (Result != Test.Result) \ 52 50 { \ 53 51 RTPrintf("failure: '%s' <-> '%s' -> " Fmt ", expected " Fmt ". (%s/%u)\n", Test.pszVer1, Test.pszVer2, Result, Test.Result, #Fun, iTest); \ 54 cErrors++; \55 } \56 else if (rc != Test.rc) \57 { \58 RTPrintf("failure: '%s' <-> '%s' -> rc=%Rrc, expected %Rrc. (%s/%u)\n", Test.pszVer1, Test.pszVer2, rc, Test.rc, #Fun, iTest); \59 52 cErrors++; \ 60 53 } \ … … 77 70 static const struct TstU8 aTstU8[] = 78 71 { 79 { "", "", VERR_NO_DIGITS,0 },80 { "asdf", "", VERR_NO_DIGITS, 0 },81 { "asdf234", "1.4.5", VINF_SUCCESS,1 },82 { "12.foo006", "12.6", VINF_SUCCESS, 0 },83 { "1", "1", VINF_SUCCESS,0 },84 { "1", "100", VINF_SUCCESS,2 },85 { "100", "1", VINF_SUCCESS,1 },86 { "3", "4", VINF_SUCCESS,2 },87 { "1", "0.1", VINF_SUCCESS,1 },88 { "1", "0.0.0.0.10000", VINF_SUCCESS,1 },89 { "0100", "100", VINF_SUCCESS,0 },90 { "1.0.0", "1", VINF_SUCCESS,0 },91 { "1.0.0", "100.0.0", VINF_SUCCESS,2 },92 { "1", "1.0.3.0", VINF_SUCCESS,2 },93 { "1.4.5", "1.2.3", VINF_SUCCESS,1 },94 { "1.2.3", "1.4.5", VINF_SUCCESS,2 },95 { "1.2.3", "4.5.6", VINF_SUCCESS,2 },96 { "1.0.4", "1.0.3", VINF_SUCCESS,1 },97 { "0.1", "0.0.1", VINF_SUCCESS,1 },98 { "0.0.1", "0.1.1", VINF_SUCCESS,2 },99 { "3.1.0", "3.0.14", VINF_SUCCESS,1 },100 { "2.0.12", "3.0.14", VINF_SUCCESS,2 },101 { "3.1", "3.0.22", VINF_SUCCESS,1 },102 { "3.0.14", "3.1.0", VINF_SUCCESS,2 },103 { "45.63", "04.560.30", VINF_SUCCESS,1 },104 { "45.006", "45.6", VINF_SUCCESS,0 },105 { "23.206", "23.06", VINF_SUCCESS,1 },106 { "23.2", "23.060", VINF_SUCCESS,2 },72 { "", "", 0 }, 73 { "asdf", "", 1 }, /* "asdf" is bigger than "" */ 74 { "asdf234", "1.4.5", 1 }, 75 { "12.foo006", "12.6", 1 }, /* "12.foo006" is bigger than "12.6" */ 76 { "1", "1", 0 }, 77 { "1", "100", 2 }, 78 { "100", "1", 1 }, 79 { "3", "4", 2 }, 80 { "1", "0.1", 1 }, 81 { "1", "0.0.0.0.10000", 1 }, 82 { "0100", "100", 0 }, 83 { "1.0.0", "1", 0 }, 84 { "1.0.0", "100.0.0", 2 }, 85 { "1", "1.0.3.0", 2 }, 86 { "1.4.5", "1.2.3", 1 }, 87 { "1.2.3", "1.4.5", 2 }, 88 { "1.2.3", "4.5.6", 2 }, 89 { "1.0.4", "1.0.3", 1 }, 90 { "0.1", "0.0.1", 1 }, 91 { "0.0.1", "0.1.1", 2 }, 92 { "3.1.0", "3.0.14", 1 }, 93 { "2.0.12", "3.0.14", 2 }, 94 { "3.1", "3.0.22", 1 }, 95 { "3.0.14", "3.1.0", 2 }, 96 { "45.63", "04.560.30", 1 }, 97 { "45.006", "45.6", 0 }, 98 { "23.206", "23.06", 1 }, 99 { "23.2", "23.060", 2 }, 107 100 108 { "VirtualBox-2.0.8-Beta2", "VirtualBox-2.0.8_Beta3-r12345", VINF_SUCCESS, 0 }, 109 { "VirtualBox-2.2.4-Beta2", "VirtualBox-2.2.2", VINF_SUCCESS, 1 }, 110 { "VirtualBox-3.1.0", "VirtualBox-3.1.2_Beta1", VINF_SUCCESS, 2 }, 111 { "3.1.0_BETA-r12345", "3.1.2", VINF_SUCCESS, 2 }, 101 { "VirtualBox-2.0.8-Beta2", "VirtualBox-2.0.8_Beta3-r12345", 2 }, 102 { "VirtualBox-2.2.4-Beta2", "VirtualBox-2.2.2", 1 }, 103 { "VirtualBox-2.2.4-Beta3", "VirtualBox-2.2.2-Beta4", 1 }, 104 { "VirtualBox-3.1.8-Alpha1", "VirtualBox-3.1.8-Alpha1-r61454", 2 }, 105 { "VirtualBox-3.1.0", "VirtualBox-3.1.2_Beta1", 2 }, 106 { "3.1.0_BETA-r12345", "3.1.2", 2 }, 112 107 }; 113 RUN_TESTS(aTstU8, uint8_t, "%#d", RTStrVersionCompare);108 RUN_TESTS(aTstU8, int, "%#d", RTStrVersionCompare); 114 109 115 110 /*
Note:
See TracChangeset
for help on using the changeset viewer.