- Timestamp:
- Nov 24, 2009 12:09:03 PM (15 years ago)
- Location:
- trunk/src/VBox/Runtime
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/string/strversion.cpp
r24662 r24893 37 37 38 38 #include <iprt/assert.h> 39 #include <iprt/ctype.h> /* needed for RT_C_IS_DIGIT */40 39 #include <iprt/err.h> 41 40 #include <iprt/mem.h> 42 41 43 42 43 /******************************************************************************* 44 * Defined Constants * 45 *******************************************************************************/ 46 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9') 47 48 49 /******************************************************************************* 50 * Internal Functions * 51 *******************************************************************************/ 52 static 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 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 44 118 45 119 /** 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". 47 124 * 48 125 * @returns iprt status code. … … 55 132 * @retval VERR_NO_DIGITS 56 133 * 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. 74 139 */ 75 RTDECL(int) RTStrVersionToUInt32(const char *pszVer, uint32_t *pu32)140 int RTStrVersionCompare(const char *pszVer1, const char *pszVer2, uint8_t *pui8Res) 76 141 { 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 { 113 214 rc = VERR_NO_DIGITS; 114 215 } 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 */ 122 219 return rc; 123 220 } 124 221 RT_EXPORT_SYMBOL(RTStrVersionCompare); -
trunk/src/VBox/Runtime/testcase/tstStrToVer.cpp
r24658 r24893 1 1 /* $Id$ */ 2 2 /** @file 3 * IPRT Testcase - String To Version Number Conversion.3 * IPRT Testcase - Version String Comparison. 4 4 */ 5 5 … … 30 30 31 31 32 /******************************************************************************* 33 * Header Files * 34 *******************************************************************************/ 32 #include <iprt/initterm.h> 35 33 #include <iprt/string.h> 36 34 #include <iprt/stream.h> 37 #include <iprt/err.h>38 35 39 36 40 struct TstU 3237 struct TstU8 41 38 { 42 const char *psz; 39 const char *pszVer1; 40 const char *pszVer2; 43 41 int rc; 44 uint 32_tResult;42 uint8_t Result; 45 43 }; 46 44 … … 50 48 { \ 51 49 Type Result; \ 52 int rc = Fun(Test.psz , &Result); \50 int rc = Fun(Test.pszVer1, Test.pszVer2, &Result); \ 53 51 if (Result != Test.Result) \ 54 52 { \ 55 RTPrintf("failure: '%s' -> " Fmt " expected " Fmt ". (%s/%u)\n", Test.psz, Result, Test.Result, #Fun, iTest); \53 RTPrintf("failure: '%s' <-> '%s' -> " Fmt ", expected " Fmt ". (%s/%u)\n", Test.pszVer1, Test.pszVer2, Result, Test.Result, #Fun, iTest); \ 56 54 cErrors++; \ 57 55 } \ 58 56 else if (rc != Test.rc) \ 59 57 { \ 60 RTPrintf("failure: '%s' -> rc=%Rrc expected %Rrc. (%s/%u)\n", Test.psz, rc, Test.rc, #Fun, iTest); \58 RTPrintf("failure: '%s' <-> '%s' -> rc=%Rrc, expected %Rrc. (%s/%u)\n", Test.pszVer1, Test.pszVer2, rc, Test.rc, #Fun, iTest); \ 61 59 cErrors++; \ 62 60 } \ 63 61 } while (0) 64 65 62 66 63 #define RUN_TESTS(aTests, Type, Fmt, Fun) \ … … 73 70 } while (0) 74 71 75 76 72 int main() 77 73 { 78 /** @todo r=bird: IPRT init is missing. Use the RTTest framework (see any79 * tstRT*.cpp file for examples of this). */ 74 RTR3Init(); 75 80 76 int cErrors = 0; 77 static const struct TstU8 aTstU8[] = 78 { 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 }, 81 107 82 static const struct TstU32 aTstU32[] = 83 { 84 { "asdf", VERR_NO_DIGITS, 0 }, 85 { "asdf234", VERR_NO_DIGITS, 0 }, 86 { "123", VINF_SUCCESS, 123 }, 87 { "45.63", VINF_SUCCESS, 4563 }, 88 { "68.54.123", VINF_SUCCESS, 6854123 }, 89 { "1.0.3-asdf", VINF_SUCCESS, 103 }, 90 { "aasdf-1", VINF_SUCCESS, 1 }, 91 { "qwer-123.34", VINF_SUCCESS, 12334 }, 92 { "aasdf45-r4545-5", VINF_SUCCESS, 5 }, 93 { "foo41-r2431-6.9.8", VINF_SUCCESS, 698 }, 94 { "bar43-r3517-7.1.2-beta", VINF_SUCCESS, 712 }, 95 { "bar43-r3517-7.1.2.53412344556-beta", VWRN_NUMBER_TOO_BIG, 0 }, 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 }, 96 112 }; 97 RUN_TESTS(aTstU 32, uint32_t, "%#ld", RTStrVersionToUInt32);113 RUN_TESTS(aTstU8, uint8_t, "%#d", RTStrVersionCompare); 98 114 99 115 /*
Note:
See TracChangeset
for help on using the changeset viewer.