1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <inttypes.h> 8 9 #include <rte_string_fns.h> 10 11 #include <cmdline_parse.h> 12 #include <cmdline_parse_num.h> 13 14 #include "test_cmdline.h" 15 16 struct num_unsigned_str { 17 const char * str; 18 uint64_t result; 19 }; 20 21 struct num_signed_str { 22 const char * str; 23 int64_t result; 24 }; 25 26 const struct num_unsigned_str num_valid_positive_strs[] = { 27 /* decimal positive */ 28 {"0", 0 }, 29 {"127", INT8_MAX }, 30 {"128", INT8_MAX + 1 }, 31 {"255", UINT8_MAX }, 32 {"256", UINT8_MAX + 1 }, 33 {"32767", INT16_MAX }, 34 {"32768", INT16_MAX + 1 }, 35 {"65535", UINT16_MAX }, 36 {"65536", UINT16_MAX + 1 }, 37 {"2147483647", INT32_MAX }, 38 {"2147483648", INT32_MAX + 1U }, 39 {"4294967295", UINT32_MAX }, 40 {"4294967296", UINT32_MAX + 1ULL }, 41 {"9223372036854775807", INT64_MAX }, 42 {"9223372036854775808", INT64_MAX + 1ULL}, 43 {"18446744073709551615", UINT64_MAX }, 44 /* hexadecimal (no leading zeroes) */ 45 {"0x0", 0 }, 46 {"0x7F", INT8_MAX }, 47 {"0x80", INT8_MAX + 1 }, 48 {"0xFF", UINT8_MAX }, 49 {"0x100", UINT8_MAX + 1 }, 50 {"0x7FFF", INT16_MAX }, 51 {"0x8000", INT16_MAX + 1 }, 52 {"0xFFFF", UINT16_MAX }, 53 {"0x10000", UINT16_MAX + 1 }, 54 {"0x7FFFFFFF", INT32_MAX }, 55 {"0x80000000", INT32_MAX + 1U }, 56 {"0xFFFFFFFF", UINT32_MAX }, 57 {"0x100000000", UINT32_MAX + 1ULL }, 58 {"0x7FFFFFFFFFFFFFFF", INT64_MAX }, 59 {"0x8000000000000000", INT64_MAX + 1ULL}, 60 {"0xFFFFFFFFFFFFFFFF", UINT64_MAX }, 61 /* hexadecimal (with leading zeroes) */ 62 {"0x00", 0 }, 63 {"0x7F", INT8_MAX }, 64 {"0x80", INT8_MAX + 1 }, 65 {"0xFF", UINT8_MAX }, 66 {"0x0100", UINT8_MAX + 1 }, 67 {"0x7FFF", INT16_MAX }, 68 {"0x8000", INT16_MAX + 1 }, 69 {"0xFFFF", UINT16_MAX }, 70 {"0x00010000", UINT16_MAX + 1 }, 71 {"0x7FFFFFFF", INT32_MAX }, 72 {"0x80000000", INT32_MAX + 1U }, 73 {"0xFFFFFFFF", UINT32_MAX }, 74 {"0x0000000100000000", UINT32_MAX + 1ULL }, 75 {"0x7FFFFFFFFFFFFFFF", INT64_MAX }, 76 {"0x8000000000000000", INT64_MAX + 1ULL}, 77 {"0xFFFFFFFFFFFFFFFF", UINT64_MAX }, 78 /* check all characters */ 79 {"0x1234567890ABCDEF", 0x1234567890ABCDEFULL }, 80 {"0x1234567890abcdef", 0x1234567890ABCDEFULL }, 81 /* binary (no leading zeroes) */ 82 {"0b0", 0 }, 83 {"0b1111111", INT8_MAX }, 84 {"0b10000000", INT8_MAX + 1 }, 85 {"0b11111111", UINT8_MAX }, 86 {"0b100000000", UINT8_MAX + 1 }, 87 {"0b111111111111111", INT16_MAX }, 88 {"0b1000000000000000", INT16_MAX + 1 }, 89 {"0b1111111111111111", UINT16_MAX }, 90 {"0b10000000000000000", UINT16_MAX + 1 }, 91 {"0b1111111111111111111111111111111", INT32_MAX }, 92 {"0b10000000000000000000000000000000", INT32_MAX + 1U }, 93 {"0b11111111111111111111111111111111", UINT32_MAX }, 94 {"0b100000000000000000000000000000000", UINT32_MAX + 1ULL }, 95 {"0b111111111111111111111111111111111111111111111111111111111111111", 96 INT64_MAX }, 97 {"0b1000000000000000000000000000000000000000000000000000000000000000", 98 INT64_MAX + 1ULL}, 99 {"0b1111111111111111111111111111111111111111111111111111111111111111", 100 UINT64_MAX }, 101 /* binary (with leading zeroes) */ 102 {"0b01111111", INT8_MAX }, 103 {"0b0000000100000000", UINT8_MAX + 1 }, 104 {"0b0111111111111111", INT16_MAX }, 105 {"0b00000000000000010000000000000000", UINT16_MAX + 1 }, 106 {"0b01111111111111111111111111111111", INT32_MAX }, 107 {"0b0000000000000000000000000000000100000000000000000000000000000000", 108 UINT32_MAX + 1ULL }, 109 {"0b0111111111111111111111111111111111111111111111111111111111111111", 110 INT64_MAX }, 111 /* octal */ 112 {"00", 0 }, 113 {"0177", INT8_MAX }, 114 {"0200", INT8_MAX + 1 }, 115 {"0377", UINT8_MAX }, 116 {"0400", UINT8_MAX + 1 }, 117 {"077777", INT16_MAX }, 118 {"0100000", INT16_MAX + 1 }, 119 {"0177777", UINT16_MAX }, 120 {"0200000", UINT16_MAX + 1 }, 121 {"017777777777", INT32_MAX }, 122 {"020000000000", INT32_MAX + 1U }, 123 {"037777777777", UINT32_MAX }, 124 {"040000000000", UINT32_MAX + 1ULL }, 125 {"0777777777777777777777", INT64_MAX }, 126 {"01000000000000000000000", INT64_MAX + 1ULL}, 127 {"01777777777777777777777", UINT64_MAX }, 128 /* check all numbers */ 129 {"012345670", 012345670 }, 130 {"076543210", 076543210 }, 131 }; 132 133 const struct num_signed_str num_valid_negative_strs[] = { 134 /* deciman negative */ 135 {"-128", INT8_MIN }, 136 {"-129", INT8_MIN - 1 }, 137 {"-32768", INT16_MIN }, 138 {"-32769", INT16_MIN - 1 }, 139 {"-2147483648", INT32_MIN }, 140 {"-2147483649", INT32_MIN - 1LL }, 141 {"-9223372036854775808", INT64_MIN }, 142 }; 143 144 const struct num_unsigned_str num_garbage_positive_strs[] = { 145 /* valid strings with garbage on the end, should still be valid */ 146 /* decimal */ 147 {"9223372036854775807\0garbage", INT64_MAX }, 148 {"9223372036854775807\tgarbage", INT64_MAX }, 149 {"9223372036854775807\rgarbage", INT64_MAX }, 150 {"9223372036854775807\ngarbage", INT64_MAX }, 151 {"9223372036854775807#garbage", INT64_MAX }, 152 {"9223372036854775807 garbage", INT64_MAX }, 153 /* hex */ 154 {"0x7FFFFFFFFFFFFFFF\0garbage", INT64_MAX }, 155 {"0x7FFFFFFFFFFFFFFF\tgarbage", INT64_MAX }, 156 {"0x7FFFFFFFFFFFFFFF\rgarbage", INT64_MAX }, 157 {"0x7FFFFFFFFFFFFFFF\ngarbage", INT64_MAX }, 158 {"0x7FFFFFFFFFFFFFFF#garbage", INT64_MAX }, 159 {"0x7FFFFFFFFFFFFFFF garbage", INT64_MAX }, 160 /* binary */ 161 {"0b1111111111111111111111111111111\0garbage", INT32_MAX }, 162 {"0b1111111111111111111111111111111\rgarbage", INT32_MAX }, 163 {"0b1111111111111111111111111111111\tgarbage", INT32_MAX }, 164 {"0b1111111111111111111111111111111\ngarbage", INT32_MAX }, 165 {"0b1111111111111111111111111111111#garbage", INT32_MAX }, 166 {"0b1111111111111111111111111111111 garbage", INT32_MAX }, 167 /* octal */ 168 {"01777777777777777777777\0garbage", UINT64_MAX }, 169 {"01777777777777777777777\rgarbage", UINT64_MAX }, 170 {"01777777777777777777777\tgarbage", UINT64_MAX }, 171 {"01777777777777777777777\ngarbage", UINT64_MAX }, 172 {"01777777777777777777777#garbage", UINT64_MAX }, 173 {"01777777777777777777777 garbage", UINT64_MAX }, 174 }; 175 176 const struct num_signed_str num_garbage_negative_strs[] = { 177 /* valid strings with garbage on the end, should still be valid */ 178 {"-9223372036854775808\0garbage", INT64_MIN }, 179 {"-9223372036854775808\rgarbage", INT64_MIN }, 180 {"-9223372036854775808\tgarbage", INT64_MIN }, 181 {"-9223372036854775808\ngarbage", INT64_MIN }, 182 {"-9223372036854775808#garbage", INT64_MIN }, 183 {"-9223372036854775808 garbage", INT64_MIN }, 184 }; 185 186 const char * num_invalid_strs[] = { 187 "18446744073709551616", /* out of range unsigned */ 188 "-9223372036854775809", /* out of range negative signed */ 189 "0x10000000000000000", /* out of range hex */ 190 /* out of range binary */ 191 "0b10000000000000000000000000000000000000000000000000000000000000000", 192 "020000000000000000000000", /* out of range octal */ 193 /* wrong chars */ 194 "0123456239", 195 "0x1234580AGE", 196 "0b0111010101g001", 197 "0b01110101017001", 198 /* false negative numbers */ 199 "-12345F623", 200 "-0x1234580A", 201 "-0b0111010101", 202 /* too long (128+ chars) */ 203 "0b1111000011110000111100001111000011110000111100001111000011110000" 204 "1111000011110000111100001111000011110000111100001111000011110000", 205 "1E3", 206 "0A", 207 "-B", 208 "+4", 209 "1.23G", 210 "", 211 " ", 212 "#", 213 "\r", 214 "\t", 215 "\n", 216 "\0", 217 }; 218 219 #define NUM_POSITIVE_STRS_SIZE \ 220 (sizeof(num_valid_positive_strs) / sizeof(num_valid_positive_strs[0])) 221 #define NUM_NEGATIVE_STRS_SIZE \ 222 (sizeof(num_valid_negative_strs) / sizeof(num_valid_negative_strs[0])) 223 #define NUM_POSITIVE_GARBAGE_STRS_SIZE \ 224 (sizeof(num_garbage_positive_strs) / sizeof(num_garbage_positive_strs[0])) 225 #define NUM_NEGATIVE_GARBAGE_STRS_SIZE \ 226 (sizeof(num_garbage_negative_strs) / sizeof(num_garbage_negative_strs[0])) 227 #define NUM_INVALID_STRS_SIZE \ 228 (sizeof(num_invalid_strs) / sizeof(num_invalid_strs[0])) 229 230 231 232 static int 233 can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type) 234 { 235 switch (type) { 236 case UINT8: 237 if (expected_result > UINT8_MAX) 238 return 0; 239 break; 240 case UINT16: 241 if (expected_result > UINT16_MAX) 242 return 0; 243 break; 244 case UINT32: 245 if (expected_result > UINT32_MAX) 246 return 0; 247 break; 248 case INT8: 249 if (expected_result > INT8_MAX) 250 return 0; 251 break; 252 case INT16: 253 if (expected_result > INT16_MAX) 254 return 0; 255 break; 256 case INT32: 257 if (expected_result > INT32_MAX) 258 return 0; 259 break; 260 case INT64: 261 if (expected_result > INT64_MAX) 262 return 0; 263 break; 264 default: 265 return 1; 266 } 267 return 1; 268 } 269 270 static int 271 can_parse_signed(int64_t expected_result, enum cmdline_numtype type) 272 { 273 switch (type) { 274 case UINT8: 275 if (expected_result > UINT8_MAX || expected_result < 0) 276 return 0; 277 break; 278 case UINT16: 279 if (expected_result > UINT16_MAX || expected_result < 0) 280 return 0; 281 break; 282 case UINT32: 283 if (expected_result > UINT32_MAX || expected_result < 0) 284 return 0; 285 break; 286 case UINT64: 287 if (expected_result < 0) 288 return 0; 289 break; 290 case INT8: 291 if (expected_result > INT8_MAX || expected_result < INT8_MIN) 292 return 0; 293 break; 294 case INT16: 295 if (expected_result > INT16_MAX || expected_result < INT16_MIN) 296 return 0; 297 break; 298 case INT32: 299 if (expected_result > INT32_MAX || expected_result < INT32_MIN) 300 return 0; 301 break; 302 default: 303 return 1; 304 } 305 return 1; 306 } 307 308 /* test invalid parameters */ 309 int 310 test_parse_num_invalid_param(void) 311 { 312 char buf[CMDLINE_TEST_BUFSIZE]; 313 uint32_t result; 314 cmdline_parse_token_num_t token; 315 int ret = 0; 316 317 /* set up a token */ 318 token.num_data.type = UINT32; 319 320 /* copy string to buffer */ 321 snprintf(buf, sizeof(buf), "%s", 322 num_valid_positive_strs[0].str); 323 324 /* try all null */ 325 ret = cmdline_parse_num(NULL, NULL, NULL, 0); 326 if (ret != -1) { 327 printf("Error: parser accepted null parameters!\n"); 328 return -1; 329 } 330 331 /* try null token */ 332 ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result)); 333 if (ret != -1) { 334 printf("Error: parser accepted null token!\n"); 335 return -1; 336 } 337 338 /* try null buf */ 339 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL, 340 (void*)&result, sizeof(result)); 341 if (ret != -1) { 342 printf("Error: parser accepted null string!\n"); 343 return -1; 344 } 345 346 /* try null result */ 347 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf, 348 NULL, 0); 349 if (ret == -1) { 350 printf("Error: parser rejected null result!\n"); 351 return -1; 352 } 353 354 /* test help function */ 355 memset(&buf, 0, sizeof(buf)); 356 357 /* try all null */ 358 ret = cmdline_get_help_num(NULL, NULL, 0); 359 if (ret != -1) { 360 printf("Error: help function accepted null parameters!\n"); 361 return -1; 362 } 363 364 /* try null token */ 365 ret = cmdline_get_help_num(NULL, buf, sizeof(buf)); 366 if (ret != -1) { 367 printf("Error: help function accepted null token!\n"); 368 return -1; 369 } 370 371 /* coverage! */ 372 ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf)); 373 if (ret < 0) { 374 printf("Error: help function failed with valid parameters!\n"); 375 return -1; 376 } 377 378 return 0; 379 } 380 /* test valid parameters but invalid data */ 381 int 382 test_parse_num_invalid_data(void) 383 { 384 enum cmdline_numtype type; 385 int ret = 0; 386 unsigned i; 387 char buf[CMDLINE_TEST_BUFSIZE]; 388 uint64_t result; /* pick largest buffer */ 389 cmdline_parse_token_num_t token; 390 391 /* cycle through all possible parsed types */ 392 for (type = UINT8; type <= INT64; type++) { 393 token.num_data.type = type; 394 395 /* test full strings */ 396 for (i = 0; i < NUM_INVALID_STRS_SIZE; i++) { 397 398 memset(&result, 0, sizeof(uint64_t)); 399 memset(&buf, 0, sizeof(buf)); 400 401 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, 402 num_invalid_strs[i], (void*)&result, sizeof(result)); 403 if (ret != -1) { 404 /* get some info about what we are trying to parse */ 405 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 406 buf, sizeof(buf)); 407 408 printf("Error: parsing %s as %s succeeded!\n", 409 num_invalid_strs[i], buf); 410 return -1; 411 } 412 } 413 } 414 return 0; 415 } 416 417 /* test valid parameters and data */ 418 int 419 test_parse_num_valid(void) 420 { 421 int ret = 0; 422 enum cmdline_numtype type; 423 unsigned i; 424 char buf[CMDLINE_TEST_BUFSIZE]; 425 uint64_t result; 426 cmdline_parse_token_num_t token; 427 428 /** valid strings **/ 429 430 /* cycle through all possible parsed types */ 431 for (type = UINT8; type <= INT64; type++) { 432 token.num_data.type = type; 433 434 /* test positive strings */ 435 for (i = 0; i < NUM_POSITIVE_STRS_SIZE; i++) { 436 result = 0; 437 memset(&buf, 0, sizeof(buf)); 438 439 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 440 buf, sizeof(buf)); 441 442 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 443 num_valid_positive_strs[i].str, 444 (void*)&result, sizeof(result)); 445 446 /* if it should have passed but didn't, or if it should have failed but didn't */ 447 if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) { 448 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 449 num_valid_positive_strs[i].str, buf); 450 return -1; 451 } 452 /* check if result matches what it should have matched 453 * since unsigned numbers don't care about number of bits, we can just convert 454 * everything to uint64_t without any worries. */ 455 if (ret > 0 && num_valid_positive_strs[i].result != result) { 456 printf("Error: parsing %s as %s failed: result mismatch!\n", 457 num_valid_positive_strs[i].str, buf); 458 return -1; 459 } 460 } 461 462 /* test negative strings */ 463 for (i = 0; i < NUM_NEGATIVE_STRS_SIZE; i++) { 464 result = 0; 465 memset(&buf, 0, sizeof(buf)); 466 467 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 468 buf, sizeof(buf)); 469 470 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 471 num_valid_negative_strs[i].str, 472 (void*)&result, sizeof(result)); 473 474 /* if it should have passed but didn't, or if it should have failed but didn't */ 475 if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) { 476 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 477 num_valid_negative_strs[i].str, buf); 478 return -1; 479 } 480 /* check if result matches what it should have matched 481 * the result is signed in this case, so we have to account for that */ 482 if (ret > 0) { 483 /* detect negative */ 484 switch (type) { 485 case INT8: 486 result = (int8_t) result; 487 break; 488 case INT16: 489 result = (int16_t) result; 490 break; 491 case INT32: 492 result = (int32_t) result; 493 break; 494 default: 495 break; 496 } 497 if (num_valid_negative_strs[i].result == (int64_t) result) 498 continue; 499 printf("Error: parsing %s as %s failed: result mismatch!\n", 500 num_valid_negative_strs[i].str, buf); 501 return -1; 502 } 503 } 504 } 505 506 /** garbage strings **/ 507 508 /* cycle through all possible parsed types */ 509 for (type = UINT8; type <= INT64; type++) { 510 token.num_data.type = type; 511 512 /* test positive garbage strings */ 513 for (i = 0; i < NUM_POSITIVE_GARBAGE_STRS_SIZE; i++) { 514 result = 0; 515 memset(&buf, 0, sizeof(buf)); 516 517 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 518 buf, sizeof(buf)); 519 520 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 521 num_garbage_positive_strs[i].str, 522 (void*)&result, sizeof(result)); 523 524 /* if it should have passed but didn't, or if it should have failed but didn't */ 525 if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) { 526 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 527 num_garbage_positive_strs[i].str, buf); 528 return -1; 529 } 530 /* check if result matches what it should have matched 531 * since unsigned numbers don't care about number of bits, we can just convert 532 * everything to uint64_t without any worries. */ 533 if (ret > 0 && num_garbage_positive_strs[i].result != result) { 534 printf("Error: parsing %s as %s failed: result mismatch!\n", 535 num_garbage_positive_strs[i].str, buf); 536 return -1; 537 } 538 } 539 540 /* test negative strings */ 541 for (i = 0; i < NUM_NEGATIVE_GARBAGE_STRS_SIZE; i++) { 542 result = 0; 543 memset(&buf, 0, sizeof(buf)); 544 545 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 546 buf, sizeof(buf)); 547 548 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 549 num_garbage_negative_strs[i].str, 550 (void*)&result, sizeof(result)); 551 552 /* if it should have passed but didn't, or if it should have failed but didn't */ 553 if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) { 554 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 555 num_garbage_negative_strs[i].str, buf); 556 return -1; 557 } 558 /* check if result matches what it should have matched 559 * the result is signed in this case, so we have to account for that */ 560 if (ret > 0) { 561 /* detect negative */ 562 switch (type) { 563 case INT8: 564 if (result & (INT8_MAX + 1)) 565 result |= 0xFFFFFFFFFFFFFF00ULL; 566 break; 567 case INT16: 568 if (result & (INT16_MAX + 1)) 569 result |= 0xFFFFFFFFFFFF0000ULL; 570 break; 571 case INT32: 572 if (result & (INT32_MAX + 1ULL)) 573 result |= 0xFFFFFFFF00000000ULL; 574 break; 575 default: 576 break; 577 } 578 if (num_garbage_negative_strs[i].result == (int64_t) result) 579 continue; 580 printf("Error: parsing %s as %s failed: result mismatch!\n", 581 num_garbage_negative_strs[i].str, buf); 582 return -1; 583 } 584 } 585 } 586 587 memset(&buf, 0, sizeof(buf)); 588 589 /* coverage! */ 590 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 591 buf, sizeof(buf)); 592 593 return 0; 594 } 595