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 static int 220 can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type) 221 { 222 switch (type) { 223 case RTE_UINT8: 224 if (expected_result > UINT8_MAX) 225 return 0; 226 break; 227 case RTE_UINT16: 228 if (expected_result > UINT16_MAX) 229 return 0; 230 break; 231 case RTE_UINT32: 232 if (expected_result > UINT32_MAX) 233 return 0; 234 break; 235 case RTE_INT8: 236 if (expected_result > INT8_MAX) 237 return 0; 238 break; 239 case RTE_INT16: 240 if (expected_result > INT16_MAX) 241 return 0; 242 break; 243 case RTE_INT32: 244 if (expected_result > INT32_MAX) 245 return 0; 246 break; 247 case RTE_INT64: 248 if (expected_result > INT64_MAX) 249 return 0; 250 break; 251 default: 252 return 1; 253 } 254 return 1; 255 } 256 257 static int 258 can_parse_signed(int64_t expected_result, enum cmdline_numtype type) 259 { 260 switch (type) { 261 case RTE_UINT8: 262 if (expected_result > UINT8_MAX || expected_result < 0) 263 return 0; 264 break; 265 case RTE_UINT16: 266 if (expected_result > UINT16_MAX || expected_result < 0) 267 return 0; 268 break; 269 case RTE_UINT32: 270 if (expected_result > UINT32_MAX || expected_result < 0) 271 return 0; 272 break; 273 case RTE_UINT64: 274 if (expected_result < 0) 275 return 0; 276 break; 277 case RTE_INT8: 278 if (expected_result > INT8_MAX || expected_result < INT8_MIN) 279 return 0; 280 break; 281 case RTE_INT16: 282 if (expected_result > INT16_MAX || expected_result < INT16_MIN) 283 return 0; 284 break; 285 case RTE_INT32: 286 if (expected_result > INT32_MAX || expected_result < INT32_MIN) 287 return 0; 288 break; 289 default: 290 return 1; 291 } 292 return 1; 293 } 294 295 /* test invalid parameters */ 296 int 297 test_parse_num_invalid_param(void) 298 { 299 char buf[CMDLINE_TEST_BUFSIZE]; 300 uint32_t result; 301 cmdline_parse_token_num_t token; 302 int ret = 0; 303 304 /* set up a token */ 305 token.num_data.type = RTE_UINT32; 306 307 /* copy string to buffer */ 308 strlcpy(buf, num_valid_positive_strs[0].str, sizeof(buf)); 309 310 /* try all null */ 311 ret = cmdline_parse_num(NULL, NULL, NULL, 0); 312 if (ret != -1) { 313 printf("Error: parser accepted null parameters!\n"); 314 return -1; 315 } 316 317 /* try null token */ 318 ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result)); 319 if (ret != -1) { 320 printf("Error: parser accepted null token!\n"); 321 return -1; 322 } 323 324 /* try null buf */ 325 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL, 326 (void*)&result, sizeof(result)); 327 if (ret != -1) { 328 printf("Error: parser accepted null string!\n"); 329 return -1; 330 } 331 332 /* try null result */ 333 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf, 334 NULL, 0); 335 if (ret == -1) { 336 printf("Error: parser rejected null result!\n"); 337 return -1; 338 } 339 340 /* test help function */ 341 memset(&buf, 0, sizeof(buf)); 342 343 /* try all null */ 344 ret = cmdline_get_help_num(NULL, NULL, 0); 345 if (ret != -1) { 346 printf("Error: help function accepted null parameters!\n"); 347 return -1; 348 } 349 350 /* try null token */ 351 ret = cmdline_get_help_num(NULL, buf, sizeof(buf)); 352 if (ret != -1) { 353 printf("Error: help function accepted null token!\n"); 354 return -1; 355 } 356 357 /* coverage! */ 358 ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf)); 359 if (ret < 0) { 360 printf("Error: help function failed with valid parameters!\n"); 361 return -1; 362 } 363 364 return 0; 365 } 366 /* test valid parameters but invalid data */ 367 int 368 test_parse_num_invalid_data(void) 369 { 370 enum cmdline_numtype type; 371 int ret = 0; 372 unsigned i; 373 char buf[CMDLINE_TEST_BUFSIZE]; 374 uint64_t result; /* pick largest buffer */ 375 cmdline_parse_token_num_t token; 376 377 /* cycle through all possible parsed types */ 378 for (type = RTE_UINT8; type <= RTE_INT64; type++) { 379 token.num_data.type = type; 380 381 /* test full strings */ 382 for (i = 0; i < RTE_DIM(num_invalid_strs); i++) { 383 384 memset(&result, 0, sizeof(uint64_t)); 385 memset(&buf, 0, sizeof(buf)); 386 387 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, 388 num_invalid_strs[i], (void*)&result, sizeof(result)); 389 if (ret != -1) { 390 /* get some info about what we are trying to parse */ 391 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 392 buf, sizeof(buf)); 393 394 printf("Error: parsing %s as %s succeeded!\n", 395 num_invalid_strs[i], buf); 396 return -1; 397 } 398 } 399 } 400 return 0; 401 } 402 403 /* test valid parameters and data */ 404 int 405 test_parse_num_valid(void) 406 { 407 int ret = 0; 408 enum cmdline_numtype type; 409 unsigned i; 410 char buf[CMDLINE_TEST_BUFSIZE]; 411 uint64_t result; 412 cmdline_parse_token_num_t token; 413 414 /** valid strings **/ 415 416 /* cycle through all possible parsed types */ 417 for (type = RTE_UINT8; type <= RTE_INT64; type++) { 418 token.num_data.type = type; 419 420 /* test positive strings */ 421 for (i = 0; i < RTE_DIM(num_valid_positive_strs); i++) { 422 result = 0; 423 memset(&buf, 0, sizeof(buf)); 424 425 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 426 buf, sizeof(buf)); 427 428 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 429 num_valid_positive_strs[i].str, 430 (void*)&result, sizeof(result)); 431 432 /* if it should have passed but didn't, or if it should have failed but didn't */ 433 if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) { 434 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 435 num_valid_positive_strs[i].str, buf); 436 return -1; 437 } 438 /* check if result matches what it should have matched 439 * since unsigned numbers don't care about number of bits, we can just convert 440 * everything to uint64_t without any worries. */ 441 if (ret > 0 && num_valid_positive_strs[i].result != result) { 442 printf("Error: parsing %s as %s failed: result mismatch!\n", 443 num_valid_positive_strs[i].str, buf); 444 return -1; 445 } 446 } 447 448 /* test negative strings */ 449 for (i = 0; i < RTE_DIM(num_valid_negative_strs); i++) { 450 result = 0; 451 memset(&buf, 0, sizeof(buf)); 452 453 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 454 buf, sizeof(buf)); 455 456 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 457 num_valid_negative_strs[i].str, 458 (void*)&result, sizeof(result)); 459 460 /* if it should have passed but didn't, or if it should have failed but didn't */ 461 if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) { 462 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 463 num_valid_negative_strs[i].str, buf); 464 return -1; 465 } 466 /* check if result matches what it should have matched 467 * the result is signed in this case, so we have to account for that */ 468 if (ret > 0) { 469 /* detect negative */ 470 switch (type) { 471 case RTE_INT8: 472 result = (int8_t) result; 473 break; 474 case RTE_INT16: 475 result = (int16_t) result; 476 break; 477 case RTE_INT32: 478 result = (int32_t) result; 479 break; 480 default: 481 break; 482 } 483 if (num_valid_negative_strs[i].result == (int64_t) result) 484 continue; 485 printf("Error: parsing %s as %s failed: result mismatch!\n", 486 num_valid_negative_strs[i].str, buf); 487 return -1; 488 } 489 } 490 } 491 492 /** garbage strings **/ 493 494 /* cycle through all possible parsed types */ 495 for (type = RTE_UINT8; type <= RTE_INT64; type++) { 496 token.num_data.type = type; 497 498 /* test positive garbage strings */ 499 for (i = 0; i < RTE_DIM(num_garbage_positive_strs); i++) { 500 result = 0; 501 memset(&buf, 0, sizeof(buf)); 502 503 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 504 buf, sizeof(buf)); 505 506 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 507 num_garbage_positive_strs[i].str, 508 (void*)&result, sizeof(result)); 509 510 /* if it should have passed but didn't, or if it should have failed but didn't */ 511 if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) { 512 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 513 num_garbage_positive_strs[i].str, buf); 514 return -1; 515 } 516 /* check if result matches what it should have matched 517 * since unsigned numbers don't care about number of bits, we can just convert 518 * everything to uint64_t without any worries. */ 519 if (ret > 0 && num_garbage_positive_strs[i].result != result) { 520 printf("Error: parsing %s as %s failed: result mismatch!\n", 521 num_garbage_positive_strs[i].str, buf); 522 return -1; 523 } 524 } 525 526 /* test negative strings */ 527 for (i = 0; i < RTE_DIM(num_garbage_negative_strs); i++) { 528 result = 0; 529 memset(&buf, 0, sizeof(buf)); 530 531 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 532 buf, sizeof(buf)); 533 534 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 535 num_garbage_negative_strs[i].str, 536 (void*)&result, sizeof(result)); 537 538 /* if it should have passed but didn't, or if it should have failed but didn't */ 539 if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) { 540 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 541 num_garbage_negative_strs[i].str, buf); 542 return -1; 543 } 544 /* check if result matches what it should have matched 545 * the result is signed in this case, so we have to account for that */ 546 if (ret > 0) { 547 /* detect negative */ 548 switch (type) { 549 case RTE_INT8: 550 if (result & (INT8_MAX + 1)) 551 result |= 0xFFFFFFFFFFFFFF00ULL; 552 break; 553 case RTE_INT16: 554 if (result & (INT16_MAX + 1)) 555 result |= 0xFFFFFFFFFFFF0000ULL; 556 break; 557 case RTE_INT32: 558 if (result & (INT32_MAX + 1ULL)) 559 result |= 0xFFFFFFFF00000000ULL; 560 break; 561 default: 562 break; 563 } 564 if (num_garbage_negative_strs[i].result == (int64_t) result) 565 continue; 566 printf("Error: parsing %s as %s failed: result mismatch!\n", 567 num_garbage_negative_strs[i].str, buf); 568 return -1; 569 } 570 } 571 } 572 573 memset(&buf, 0, sizeof(buf)); 574 575 /* coverage! */ 576 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 577 buf, sizeof(buf)); 578 579 return 0; 580 } 581