Changeset 99246 in vbox for trunk/src/VBox/Runtime
- Timestamp:
- Mar 31, 2023 8:48:57 AM (22 months ago)
- Location:
- trunk/src/VBox/Runtime
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/misc/getopt.cpp
r98103 r99246 49 49 #include <iprt/message.h> 50 50 #include <iprt/string.h> 51 #include <iprt/uni.h> 51 52 #include <iprt/uuid.h> 52 53 … … 55 56 * Defined Constants And Macros * 56 57 *********************************************************************************************************************************/ 57 #ifdef IN_RT_STATIC 58 # define RT StrICmp RTStrICmpAscii59 # define RT StrNICmp RTStrNICmpAscii58 #ifdef IN_RT_STATIC /* We don't need full unicode case insensitive if we ASSUME basic latin only. */ 59 # define RTUniCpToLower(a_uc) ((RTUNICP)RT_C_TO_LOWER(a_uc)) 60 # define RTUniCpToUpper(a_uc) ((RTUNICP)RT_C_TO_UPPER(a_uc)) 60 61 #endif 61 62 … … 112 113 unsigned char ch; 113 114 while ((ch = *psz++) != '\0') 114 Assert(ch <= 0x7f); /* ASSUMPTION that we can use RTStrICmpAscii and RTStrNICmpAscii. */ 115 Assert(ch <= 0x7f); /* ASSUMPTION that we can use also RTStrICmpAscii and RTStrNICmpAscii for static builds 116 and that we don't need to use RTStrGetCp on pszLong strings. */ 115 117 } 116 118 } … … 160 162 161 163 #endif /* IPRT_GETOPT_WITHOUT_NETWORK_ADDRESSES */ 164 165 166 /** 167 * Checks if the given unicode codepoint is an alternative dash or not. 168 * 169 * ASSUMES caller checks for the ascii hypen-minus. 170 * 171 * @returns true / false. 172 * @param uc The codepoint to check. 173 */ 174 DECLINLINE(bool) rtGetOptIsAlternativeDash(RTUNICP uc) 175 { 176 return uc == 0x2011 /* non-breaking hypen */ 177 || uc == 0x2010 /* hypen */ 178 || uc == 0x2012 /* figure dash */ 179 || uc == 0x2013 /* en dash */ 180 || uc == 0x2014 /* em dash */ 181 || uc == 0x2212 /* minus sign */ 182 || uc == 0xfe58 /* small em dash */ 183 || uc == 0xfe63 /* small em hypen-minus */ 184 || uc == 0xff0d /* fullwidth hypen-minus */ 185 ; 186 } 187 188 189 /** 190 * Checks if the given unicode codepoint is an any kind of dash. 191 * 192 * @returns true / false. 193 * @param uc The codepoint to check. 194 */ 195 DECLINLINE(bool) rtGetOptIsDash(RTUNICP uc) 196 { 197 return uc == (RTUNICP)'-' || rtGetOptIsAlternativeDash(uc); 198 } 199 200 201 /** 202 * Compares a user specific argument with a RTGETOPTDEF long string, taking 203 * RTGETOPT_FLAG_ICASE into account. 204 * 205 * This differs from strcmp/RTStrICmp in that it matches the minus-hyphen 206 * (U+002D) codepoint in a RTGETOPTDEF string with non-breaking hyphen (U+2011) 207 * and some others. This allows copy & paste from manuals, documentation and 208 * similar that needs to use the non-breaking variant for typographical reasons. 209 * 210 * @returns true if matches, false if not. 211 * @param pszUserArg Ther user specified argument string. 212 * @param pszOption The RTGETOPTDEF::pszLong string. 213 * @param fOptFlags The RTGETOPTDEF::fFlags. 214 * @param cchMax RT_STR_MAX if full string compare, otherwise 215 * number of @a pszOption characters to compare. 216 * @param poffUserArgNext Where to return offset of the first character 217 * after the match in @a pszUserArg. Optional. 218 */ 219 static bool rtGetOptLongStrEquals(const char *pszUserArg, const char *pszOption, size_t cchMax, uint32_t fOptFlags, 220 size_t *poffUserArgNext) 221 { 222 const char * const pszUserArgStart = pszUserArg; 223 while (cchMax-- > 0) 224 { 225 char const ch1 = *pszUserArg++; 226 char const ch2 = *pszOption++; 227 if (ch1 == ch2) 228 { 229 if (!ch1) 230 break; 231 } 232 /* If the long option contains a dash, consider alternative unicode dashes 233 like non-breaking and such. */ 234 else if (ch2 == '-') 235 { 236 if (!((unsigned char)ch1 & 0x80)) 237 return false; 238 239 pszUserArg--; 240 RTUNICP uc; 241 int rc = RTStrGetCpEx(&pszUserArg, &uc); 242 AssertRCReturn(rc, false); 243 244 if (!rtGetOptIsAlternativeDash(uc)) 245 return false; 246 } 247 /* Do case folding if configured for this option. */ 248 else if (!(fOptFlags & RTGETOPT_FLAG_ICASE)) 249 return false; 250 else 251 { 252 pszUserArg--; 253 RTUNICP uc; 254 int rc = RTStrGetCpEx(&pszUserArg, &uc); 255 AssertRCReturn(rc, false); 256 257 if ( RTUniCpToLower(uc) != (RTUNICP)RT_C_TO_LOWER(ch2) 258 && RTUniCpToUpper(uc) != (RTUNICP)RT_C_TO_UPPER(ch2)) 259 return false; 260 } 261 } 262 if (poffUserArgNext) 263 *poffUserArgNext = (size_t)(pszUserArg - pszUserArgStart); 264 return true; 265 } 266 267 268 /** 269 * Skips the optional dash for options with the RTGETOPT_FLAG_INDEX_DEF_DASH 270 * flag. 271 */ 272 static size_t rtGetOptSkipIndexDefDash(const char *pszOption, size_t cchLong) 273 { 274 const char *pszNext = &pszOption[cchLong]; 275 RTUNICP uc; 276 int rc = RTStrGetCpEx(&pszNext, &uc); 277 AssertRCReturn(rc, cchLong); 278 279 /* given "--long" we match "--long-1" but not "--long-". */ 280 if ( rtGetOptIsDash(uc) 281 && RT_C_IS_DIGIT(*pszNext)) 282 return (size_t)(pszNext - pszOption); 283 284 return cchLong; 285 } 286 162 287 163 288 /** … … 169 294 * @param cOptions Number of items in the array. 170 295 * @param fFlags Init flags. 171 */ 172 static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions, uint32_t fFlags) 296 * @param pcchMatch Where to return the length of the matching option 297 * section, including any RTGETOPT_FLAG_INDEX_DEF_DASH 298 * dash, but _not_ the actual index or value separator. 299 */ 300 static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions, uint32_t fFlags, 301 size_t *pcchMatch) 173 302 { 174 303 PCRTGETOPTDEF pOpt = paOptions; … … 191 320 */ 192 321 size_t cchLong = strlen(pOpt->pszLong); 193 if ( !strncmp(pszOption, pOpt->pszLong, cchLong) 194 || ( (fOptFlags & RTGETOPT_FLAG_ICASE) 195 && !RTStrNICmp(pszOption, pOpt->pszLong, cchLong))) 196 { 197 if ( (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_DASH) 198 && pszOption[cchLong] == '-' 199 && RT_C_IS_DIGIT(pszOption[cchLong + 1])) /* given "--long" we match "--long-1" but not "--long-". */ 200 cchLong++; 322 if (rtGetOptLongStrEquals(pszOption, pOpt->pszLong, cchLong, fOptFlags, &cchLong)) 323 { 324 if (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_DASH) 325 cchLong = rtGetOptSkipIndexDefDash(pszOption, cchLong); 326 size_t const cchMatch = cchLong; 201 327 if (fOptFlags & RTGETOPT_FLAG_INDEX) 202 328 while (RT_C_IS_DIGIT(pszOption[cchLong])) … … 205 331 || pszOption[cchLong] == ':' 206 332 || pszOption[cchLong] == '=') 333 { 334 *pcchMatch = cchMatch; 207 335 return pOpt; 336 } 208 337 } 209 338 } … … 215 344 */ 216 345 size_t cchLong = strlen(pOpt->pszLong); 217 if ( !strncmp(pszOption, pOpt->pszLong, cchLong) 218 || ( (fOptFlags & RTGETOPT_FLAG_ICASE) 219 && !RTStrNICmp(pszOption, pOpt->pszLong, cchLong))) 220 { 221 if ( (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_DASH) 222 && pszOption[cchLong] == '-' 223 && RT_C_IS_DIGIT(pszOption[cchLong + 1])) 224 cchLong++; 346 if (rtGetOptLongStrEquals(pszOption, pOpt->pszLong, cchLong, fOptFlags, &cchLong)) 347 { 348 if (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_DASH) 349 cchLong = rtGetOptSkipIndexDefDash(pszOption, cchLong); 350 size_t const cchMatch = cchLong; 225 351 while (RT_C_IS_DIGIT(pszOption[cchLong])) 226 352 cchLong++; 227 353 if (pszOption[cchLong] == '\0') 354 { 355 *pcchMatch = cchMatch; 228 356 return pOpt; 229 } 230 } 231 else if ( !strcmp(pszOption, pOpt->pszLong) 232 || ( (fOptFlags & RTGETOPT_FLAG_ICASE) 233 && !RTStrICmp(pszOption, pOpt->pszLong))) 357 } 358 } 359 } 360 else if (rtGetOptLongStrEquals(pszOption, pOpt->pszLong, RTSTR_MAX, fOptFlags, NULL)) 234 361 return pOpt; 235 362 } … … 239 366 if (!(fFlags & RTGETOPTINIT_FLAGS_NO_STD_OPTS)) 240 367 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStdOptions); i++) 241 if ( !strcmp(pszOption, g_aStdOptions[i].pszLong) 242 || ( g_aStdOptions[i].fFlags & RTGETOPT_FLAG_ICASE 243 && !RTStrICmp(pszOption, g_aStdOptions[i].pszLong))) 368 { 369 size_t cchMatch = 0; 370 if (rtGetOptLongStrEquals(pszOption, g_aStdOptions[i].pszLong, RTSTR_MAX, g_aStdOptions[i].fFlags, &cchMatch)) 371 { 372 *pcchMatch = cchMatch; 244 373 return &g_aStdOptions[i]; 374 } 375 } 245 376 246 377 return NULL; … … 306 437 307 438 case RTGETOPT_REQ_BOOL: 308 if ( !RTStrICmp(pszValue, "true") 309 || !RTStrICmp(pszValue, "t") 310 || !RTStrICmp(pszValue, "yes") 311 || !RTStrICmp(pszValue, "y") 312 || !RTStrICmp(pszValue, "enabled") 313 || !RTStrICmp(pszValue, "enable") 314 || !RTStrICmp(pszValue, "en") 315 || !RTStrICmp(pszValue, "e") 316 || !RTStrICmp(pszValue, "on") 317 || !RTStrCmp(pszValue, "1") 318 ) 439 { 440 static struct RTGETOPTUPPERLOWERBOOL 441 { 442 bool f; 443 const char *pszLower; 444 const char *pszUpper; 445 const char *pszMarkers; /**< characters with '+' are okay to stop after, '-' aren't.*/ 446 } const s_aKeywords[] = 447 { 448 { true, "true", "TRUE", "+--+" }, 449 { true, "yes", "YES", "+-+" }, 450 { true, "enabled", "ENABLED", "++---++" }, 451 { true, "on", "ON", "-+" }, 452 { true, "1", "1", "+" }, 453 { false, "false", "FALSE", "+---+" }, 454 { false, "no", "NO", "++" }, 455 { false, "disabled", "DISABLED", "+-+---++" }, 456 { false, "off", "OFF", "--+" }, 457 { false, "0", "0", "+" }, 458 }; 459 for (size_t i = 0; i < RT_ELEMENTS(s_aKeywords); i++) 460 for (size_t off = 0;; off++) 461 { 462 char const ch = pszValue[off]; 463 if (!ch) 464 { 465 if (off > 0 && s_aKeywords[i].pszMarkers[off - 1] == '+') 466 { 467 pValueUnion->f = s_aKeywords[i].f; 468 return VINF_SUCCESS; 469 } 470 break; 471 } 472 if ( s_aKeywords[i].pszLower[off] != ch 473 && s_aKeywords[i].pszUpper[off] != ch) 474 break; 475 } 476 pValueUnion->psz = pszValue; 477 return VERR_GETOPT_UNKNOWN_OPTION; 478 } 479 480 case RTGETOPT_REQ_BOOL_ONOFF: 481 if ( (pszValue[0] == 'o' || pszValue[0] == 'O') 482 && (pszValue[1] == 'n' || pszValue[1] == 'N') 483 && pszValue[2] == '\0') 319 484 pValueUnion->f = true; 320 else if ( !RTStrICmp(pszValue, "false") 321 || !RTStrICmp(pszValue, "f") 322 || !RTStrICmp(pszValue, "no") 323 || !RTStrICmp(pszValue, "n") 324 || !RTStrICmp(pszValue, "disabled") 325 || !RTStrICmp(pszValue, "disable") 326 || !RTStrICmp(pszValue, "dis") 327 || !RTStrICmp(pszValue, "d") 328 || !RTStrICmp(pszValue, "off") 329 || !RTStrCmp(pszValue, "0") 330 ) 331 pValueUnion->f = false; 332 else 333 { 334 pValueUnion->psz = pszValue; 335 return VERR_GETOPT_UNKNOWN_OPTION; 336 } 337 break; 338 339 case RTGETOPT_REQ_BOOL_ONOFF: 340 if (!RTStrICmp(pszValue, "on")) 341 pValueUnion->f = true; 342 else if (!RTStrICmp(pszValue, "off")) 485 else if ( (pszValue[0] == 'o' || pszValue[0] == 'O') 486 && (pszValue[1] == 'f' || pszValue[1] == 'F') 487 && (pszValue[2] == 'f' || pszValue[2] == 'F') 488 && pszValue[3] == '\0') 343 489 pValueUnion->f = false; 344 490 else … … 567 713 int iThis; 568 714 const char *pszArgThis; 715 size_t cchMatch; 569 716 PCRTGETOPTDEF pOpt; 570 717 … … 580 727 return VERR_GETOPT_UNKNOWN_OPTION; 581 728 } 582 p State->pszNextShort++;583 pszArgThis = pState->pszNextShort - 2;729 pszArgThis = pState->pszNextShort++; 730 cchMatch = 1; 584 731 iThis = pState->iNext; 585 732 fShort = true; … … 619 766 * get mixed up with short ones. 620 767 */ 621 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions, pState->fFlags); 622 if ( !pOpt 623 && pszArgThis[0] == '-' 624 && pszArgThis[1] != '-' 625 && pszArgThis[1] != '\0') 626 { 627 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions, pState->fFlags); 628 fShort = pOpt != NULL; 629 } 630 else 631 fShort = false; 768 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions, pState->fFlags, &cchMatch); 769 fShort = false; 770 if (!pOpt) 771 { 772 const char *pszNext = pszArgThis; 773 RTUNICP uc; 774 int vrc = RTStrGetCpEx(&pszNext, &uc); 775 AssertRCReturn(vrc, vrc); 776 char chNext; 777 if ( rtGetOptIsDash(uc) 778 && (chNext = *pszNext) != '-' 779 && chNext != '\0' 780 && !((unsigned char)chNext & 0x80)) 781 { 782 pOpt = rtGetOptSearchShort(chNext, pState->paOptions, pState->cOptions, pState->fFlags); 783 if (pOpt) 784 { 785 cchMatch = (size_t)(&pszNext[1] - pszArgThis); 786 fShort = true; 787 } 788 } 789 } 632 790 633 791 /* Look for dash-dash. */ 634 if (!pOpt && !strcmp(pszArgThis, "--"))792 if (!pOpt && rtGetOptLongStrEquals(pszArgThis, "--", RTSTR_MAX, 0, NULL)) 635 793 { 636 794 rtGetOptMoveArgvEntries(&pState->argv[iThis], &pState->argv[iThis + pState->cNonOptions]); … … 644 802 if (pOpt) 645 803 rtGetOptMoveArgvEntries(&pState->argv[iThis], &pState->argv[iThis + pState->cNonOptions]); 646 else if ( *pszArgThis == '-')804 else if (rtGetOptIsDash(RTStrGetCp(pszArgThis))) 647 805 { 648 806 pValueUnion->psz = pszArgThis; … … 685 843 if (fShort) 686 844 { 687 if (pszArgThis[ 2] == '\0')845 if (pszArgThis[cchMatch] == '\0') 688 846 { 689 847 if (iThis + 1 >= pState->argc) … … 694 852 } 695 853 else /* same argument. */ 696 pszValue = &pszArgThis[ 2 + (pszArgThis[2] == ':' || pszArgThis[2] == '=')];854 pszValue = &pszArgThis[cchMatch + (pszArgThis[cchMatch] == ':' || pszArgThis[cchMatch] == '=')]; 697 855 if (pState->pszNextShort) 698 856 { … … 703 861 else 704 862 { 705 size_t cchLong = strlen(pOpt->pszLong);706 863 if (fOptFlags & RTGETOPT_FLAG_INDEX) 707 864 { 708 if ( pszArgThis[cch Long] != '\0'865 if ( pszArgThis[cchMatch] != '\0' 709 866 || (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_MASK)) 710 867 { 711 if ( (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_DASH) 712 && pszArgThis[cchLong] == '-') 713 cchLong++; 714 715 uint32_t uIndex; 716 char *pszRet = NULL; 717 int rc = RTStrToUInt32Ex(&pszArgThis[cchLong], &pszRet, 10, &uIndex); 868 const char *pszNext = &pszArgThis[cchMatch]; 869 uint32_t uIndex; 870 int rc = RTStrToUInt32Ex(pszNext, (char **)&pszNext, 10, &uIndex); 718 871 if ( rc == VERR_NO_DIGITS 719 872 && (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_MASK)) 720 873 { 721 874 uIndex = ((fOptFlags & RTGETOPT_FLAG_INDEX_DEF_MASK) >> RTGETOPT_FLAG_INDEX_DEF_SHIFT) - 1; 722 rc = psz Ret[0] == '\0' ? VINF_SUCCESS : VWRN_TRAILING_CHARS;875 rc = pszNext[0] == '\0' ? VINF_SUCCESS : VWRN_TRAILING_CHARS; 723 876 } 724 877 if (rc == VWRN_TRAILING_CHARS) 725 878 { 726 if ( psz Ret[0] != ':'727 && psz Ret[0] != '=')879 if ( pszNext[0] != ':' 880 && pszNext[0] != '=') 728 881 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; 729 882 pState->uIndex = uIndex; 730 pszValue = psz Ret + 1;883 pszValue = pszNext + 1; 731 884 } 732 885 else if (rc == VINF_SUCCESS) … … 747 900 else 748 901 { 749 if (pszArgThis[cch Long] == '\0')902 if (pszArgThis[cchMatch] == '\0') 750 903 { 751 904 if (iThis + 1 + pState->cNonOptions >= pState->argc) … … 756 909 } 757 910 else /* same argument. */ 758 pszValue = &pszArgThis[cch Long+ 1];911 pszValue = &pszArgThis[cchMatch + 1]; 759 912 } 760 913 } … … 773 926 * state variables for the start and end cases. 774 927 */ 775 if (pszArgThis[ 2])928 if (pszArgThis[cchMatch]) 776 929 { 777 930 if (!pState->pszNextShort) 778 931 { 779 932 /* start */ 780 pState->pszNextShort = &pszArgThis[ 2];933 pState->pszNextShort = &pszArgThis[cchMatch]; 781 934 pState->iNext--; 782 935 } … … 791 944 else if (fOptFlags & RTGETOPT_FLAG_INDEX) 792 945 { 793 size_t cchLong = strlen(pOpt->pszLong);794 946 uint32_t uIndex; 795 if (pszArgThis[cchLong] != '\0') 796 { 797 if ( (fOptFlags & RTGETOPT_FLAG_INDEX_DEF_DASH) 798 && pszArgThis[cchLong] == '-') 799 cchLong++; 800 if (RTStrToUInt32Full(&pszArgThis[cchLong], 10, &uIndex) == VINF_SUCCESS) 947 if (pszArgThis[cchMatch] != '\0') 948 { 949 if (RTStrToUInt32Full(&pszArgThis[cchMatch], 10, &uIndex) == VINF_SUCCESS) 801 950 pState->uIndex = uIndex; 802 951 else -
trunk/src/VBox/Runtime/testcase/tstRTGetOpt.cpp
r98103 r99246 222 222 "--optpair64", "0x128", 223 223 224 /* various utf-8 dashes: */ 225 #define DASH_HYPHEN "\xE2\x80\x90" // U+2010 226 #define DASH_NON_BREAKING "\xE2\x80\x91" // U+2011 227 #define DASH_FIGURE "\xE2\x80\x92" // U+2012 228 #define DASH_EN "\xE2\x80\x93" // U+2013 229 #define DASH_EM "\xE2\x80\x94" // U+2014 230 #define DASH_MINUS_SIGN "\xE2\x88\x92" // U+2212 231 #define DASH_SMALL_EM "\xEF\xB9\x98" // U+fe58 232 #define DASH_SMALL_HYPEN_MINUS "\xEF\xB9\xA3" // U+fe63 233 #define DASH_FULLWIDTH "\xEF\xBC\x8D" // U+ff0d 234 DASH_HYPHEN DASH_NON_BREAKING "quiet", 235 DASH_FIGURE DASH_EN "boolean" DASH_EM "dash" DASH_MINUS_SIGN "idx" DASH_FULLWIDTH "2", "off", 236 DASH_NON_BREAKING "qV", 237 DASH_SMALL_EM DASH_SMALL_HYPEN_MINUS "boolean1index42", "on", 238 DASH_NON_BREAKING DASH_FULLWIDTH "indexnovalue2", 239 224 240 /* done */ 225 241 NULL … … 514 530 CHECK(Val.PairU64.uFirst == 0x128); 515 531 CHECK(Val.PairU64.uSecond == UINT64_MAX); 532 533 /* various utf-8 dashes: */ 534 CHECK_GETOPT(RTGetOpt(&GetState, &Val), 384, 1); 535 CHECK_pDef(s_aOpts2, 4); 536 537 CHECK_GETOPT(RTGetOpt(&GetState, &Val), 415, 2); 538 CHECK(!Val.f); 539 CHECK(GetState.uIndex == 2); 540 541 CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'q', 0); 542 CHECK_pDef(s_aOpts2, 3); 543 CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'V', 1); 544 545 CHECK_GETOPT(RTGetOpt(&GetState, &Val), 414, 2); 546 CHECK(Val.f); 547 CHECK(GetState.uIndex == 42); 548 549 CHECK_GETOPT(RTGetOpt(&GetState, &Val), 403, 1); 550 CHECK(GetState.uIndex == 2); 516 551 517 552 /* the end */
Note:
See TracChangeset
for help on using the changeset viewer.