1 /* $NetBSD: argv.c,v 1.6 2026/05/09 18:49:22 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* argv 3 6 /* SUMMARY 7 /* string array utilities 8 /* SYNOPSIS 9 /* #include <argv.h> 10 /* 11 /* typedef int (*ARGV_COMPAR_FN)(const void *, const void *); 12 /* 13 /* ARGV *argv_alloc(len) 14 /* ssize_t len; 15 /* 16 /* ARGV *argv_qsort(argvp, compar) 17 /* ARGV *argvp; 18 /* ARGV_COMPAR_FN compar; 19 /* 20 /* void argv_uniq(argvp, compar) 21 /* ARGV *argvp; 22 /* ARGV_COMPAR_FN compar; 23 /* 24 /* ARGV *argv_free(argvp) 25 /* ARGV *argvp; 26 /* 27 /* void argv_add(argvp, arg, ..., ARGV_END) 28 /* ARGV *argvp; 29 /* char *arg; 30 /* 31 /* void argv_addn(argvp, arg, arg_len, ..., ARGV_END) 32 /* ARGV *argvp; 33 /* char *arg; 34 /* ssize_t arg_len; 35 /* 36 /* ARGV *argv_addv(argvp, argv) 37 /* ARGV *argvp; 38 /* const char **argv; 39 /* 40 /* void argv_terminate(argvp); 41 /* ARGV *argvp; 42 /* 43 /* void argv_truncate(argvp, len); 44 /* ARGV *argvp; 45 /* ssize_t len; 46 /* 47 /* void argv_insert_one(argvp, pos, arg) 48 /* ARGV *argvp; 49 /* ssize_t pos; 50 /* const char *arg; 51 /* 52 /* void argv_replace_one(argvp, pos, arg) 53 /* ARGV *argvp; 54 /* ssize_t pos; 55 /* const char *arg; 56 /* 57 /* void argv_delete(argvp, pos, how_many) 58 /* ARGV *argvp; 59 /* ssize_t pos; 60 /* ssize_t how_many; 61 /* 62 /* char *argv_join(buf, argvp, delim) 63 /* VSTRING *buf; 64 /* ARGV *argvp; 65 /* int delim; 66 /* 67 /* void ARGV_FAKE_BEGIN(argv, arg) 68 /* const char *arg; 69 /* 70 /* void ARGV_FAKE2_BEGIN(argv, arg1, arg2) 71 /* const char *arg1; 72 /* const char *arg2; 73 /* 74 /* void ARGV_FAKE_END 75 /* DESCRIPTION 76 /* The functions in this module manipulate arrays of string 77 /* pointers. An ARGV structure contains the following members: 78 /* .IP len 79 /* The length of the \fIargv\fR array member. 80 /* .IP argc 81 /* The number of \fIargv\fR elements used. 82 /* .IP argv 83 /* An array of pointers to null-terminated strings. 84 /* .PP 85 /* argv_alloc() returns an empty string array of the requested 86 /* length. The result is ready for use by argv_add(). The array 87 /* is null terminated. 88 /* 89 /* argv_qsort() sorts the elements of argvp in place, and 90 /* returns its first argument. If the compar argument specifies 91 /* a null pointer, then argv_qsort() will use byte-by-byte 92 /* comparison. 93 /* 94 /* argv_uniq() reduces adjacent same-value elements to one 95 /* element, and returns its first argument. If the compar 96 /* argument specifies a null pointer, then argv_uniq() will 97 /* use byte-by-byte comparison. 98 /* 99 /* argv_add() copies zero or more strings and adds them to the 100 /* specified string array. The array is null terminated. 101 /* Terminate the argument list with a null pointer. The manifest 102 /* constant ARGV_END provides a convenient notation for this. 103 /* 104 /* argv_addn() is like argv_add(), but each string is followed 105 /* by a string length argument. 106 /* 107 /* argv_addv() optionally creates an ARGV when the first argument 108 /* is a null pointer, and appends a null-terminated list of 109 /* strings. The result is null terminated. 110 /* 111 /* argv_free() releases storage for a string array, and conveniently 112 /* returns a null pointer. 113 /* 114 /* argv_terminate() null-terminates its string array argument. 115 /* 116 /* argv_truncate() truncates its argument to the specified 117 /* number of entries, but does not reallocate memory. The 118 /* result is null-terminated. 119 /* 120 /* argv_insert_one() inserts one string at the specified array 121 /* position. 122 /* 123 /* argv_replace_one() replaces one string at the specified 124 /* position. The old string is destroyed after the update is 125 /* made. 126 /* 127 /* argv_delete() deletes the specified number of elements 128 /* starting at the specified array position. The result is 129 /* null-terminated. 130 /* 131 /* argv_join() joins all elements in an array using the 132 /* specified delimiter value, and appends the result to the 133 /* specified buffer. 134 /* 135 /* ARGV_FAKE_BEGIN/END are an optimization for the case where 136 /* a single string needs to be passed into an ARGV-based 137 /* interface. ARGV_FAKE_BEGIN() opens a statement block and 138 /* allocates a stack-based ARGV structure named after the first 139 /* argument, that encapsulates the second argument. This 140 /* implementation allocates no heap memory and creates no copy 141 /* of the second argument. ARGV_FAKE_END closes the statement 142 /* block and thereby releases storage. 143 /* 144 /* ARGV_FAKE2_BEGIN/END provide the same functionality for a pair 145 /* of strings. 146 /* SEE ALSO 147 /* msg(3) diagnostics interface 148 /* DIAGNOSTICS 149 /* Fatal errors: memory allocation problem. 150 /* LICENSE 151 /* .ad 152 /* .fi 153 /* The Secure Mailer license must be distributed with this software. 154 /* AUTHOR(S) 155 /* Wietse Venema 156 /* IBM T.J. Watson Research 157 /* P.O. Box 704 158 /* Yorktown Heights, NY 10598, USA 159 /* 160 /* Wietse Venema 161 /* Google, Inc. 162 /* 111 8th Avenue 163 /* New York, NY 10011, USA 164 /* 165 /* Wietse Venema 166 /* porcupine.org 167 /*--*/ 168 169 /* System libraries. */ 170 171 #include <sys_defs.h> 172 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */ 173 #include <stdarg.h> 174 #include <string.h> 175 176 /* Application-specific. */ 177 178 #include "mymalloc.h" 179 #include "msg.h" 180 #include "vstring.h" 181 #include "argv.h" 182 183 #ifdef TEST 184 extern NORETURN PRINTFLIKE(1, 2) test_msg_panic(const char *,...); 185 186 #define msg_panic test_msg_panic 187 #endif 188 189 /* argv_free - destroy string array */ 190 191 ARGV *argv_free(ARGV *argvp) 192 { 193 char **cpp; 194 195 for (cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++) 196 myfree(*cpp); 197 myfree((void *) argvp->argv); 198 myfree((void *) argvp); 199 return (0); 200 } 201 202 /* argv_alloc - initialize string array */ 203 204 ARGV *argv_alloc(ssize_t len) 205 { 206 ARGV *argvp; 207 ssize_t sane_len; 208 209 /* 210 * Make sure that always argvp->argc < argvp->len. 211 */ 212 argvp = (ARGV *) mymalloc(sizeof(*argvp)); 213 argvp->len = 0; 214 sane_len = (len < 2 ? 2 : len); 215 /* 202604 Claude: avoid overflowing sane_len + 1 */ 216 if (sane_len > SSIZE_MAX - 1) 217 msg_panic("argv_alloc: array length overflow"); 218 argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *)); 219 argvp->len = sane_len; 220 argvp->argc = 0; 221 argvp->argv[0] = 0; 222 return (argvp); 223 } 224 225 static int argv_cmp(const void *e1, const void *e2) 226 { 227 const char *s1 = *(const char **) e1; 228 const char *s2 = *(const char **) e2; 229 230 return strcmp(s1, s2); 231 } 232 233 /* argv_qsort - sort array in place */ 234 235 ARGV *argv_qsort(ARGV *argvp, ARGV_COMPAR_FN compar) 236 { 237 qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), 238 compar ? compar : argv_cmp); 239 return (argvp); 240 } 241 242 /* argv_sort - binary compatibility */ 243 244 ARGV *argv_sort(ARGV *argvp) 245 { 246 qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), argv_cmp); 247 return (argvp); 248 } 249 250 /* argv_uniq - deduplicate adjacent array elements */ 251 252 ARGV *argv_uniq(ARGV *argvp, ARGV_COMPAR_FN compar) 253 { 254 char **cpp; 255 char **prev; 256 257 if (compar == 0) 258 compar = argv_cmp; 259 for (prev = 0, cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++) { 260 if (prev != 0 && compar(prev, cpp) == 0) { 261 argv_delete(argvp, cpp - argvp->argv, 1); 262 cpp = prev; 263 } else { 264 prev = cpp; 265 } 266 } 267 return (argvp); 268 } 269 270 /* argv_extend - extend array */ 271 272 static void argv_extend(ARGV *argvp) 273 { 274 ssize_t new_len; 275 276 /* 202604 Claude: avoid overflowing (new_len + 1) * sizeof(char *). */ 277 if (argvp->len > SSIZE_MAX / (2 * sizeof(char *)) - 1) 278 msg_panic("argv_extend: array length overflow"); 279 new_len = argvp->len * 2; 280 argvp->argv = (char **) 281 myrealloc((void *) argvp->argv, (new_len + 1) * sizeof(char *)); 282 argvp->len = new_len; 283 } 284 285 /* argv_add - add string to vector */ 286 287 void argv_add(ARGV *argvp,...) 288 { 289 char *arg; 290 va_list ap; 291 292 /* 293 * Make sure that always argvp->argc < argvp->len. 294 */ 295 #define ARGV_SPACE_LEFT(a) ((a)->len - (a)->argc - 1) 296 297 va_start(ap, argvp); 298 while ((arg = va_arg(ap, char *)) != 0) { 299 if (ARGV_SPACE_LEFT(argvp) <= 0) 300 argv_extend(argvp); 301 argvp->argv[argvp->argc++] = mystrdup(arg); 302 } 303 va_end(ap); 304 argvp->argv[argvp->argc] = 0; 305 } 306 307 /* argv_addn - add string to vector */ 308 309 void argv_addn(ARGV *argvp,...) 310 { 311 char *arg; 312 ssize_t len; 313 va_list ap; 314 315 /* 316 * Make sure that always argvp->argc < argvp->len. 317 */ 318 va_start(ap, argvp); 319 while ((arg = va_arg(ap, char *)) != 0) { 320 if ((len = va_arg(ap, ssize_t)) < 0) 321 msg_panic("argv_addn: bad string length %ld", (long) len); 322 if (ARGV_SPACE_LEFT(argvp) <= 0) 323 argv_extend(argvp); 324 argvp->argv[argvp->argc++] = mystrndup(arg, len); 325 } 326 va_end(ap); 327 argvp->argv[argvp->argc] = 0; 328 } 329 330 /* argv_addv - optionally create ARGV, append string vector */ 331 332 ARGV *argv_addv(ARGV *argvp, const char *const * argv) 333 { 334 const char *const * cpp; 335 336 if (argvp == 0) { 337 for (cpp = argv; *cpp; cpp++) 338 /* void */ ; 339 argvp = argv_alloc(cpp - argv); 340 } 341 for (cpp = argv; *cpp; cpp++) 342 argv_add(argvp, *cpp, (char *) 0); 343 argvp->argv[argvp->argc] = 0; 344 return (argvp); 345 } 346 347 /* argv_terminate - terminate string array */ 348 349 void argv_terminate(ARGV *argvp) 350 { 351 352 /* 353 * Trust that argvp->argc < argvp->len. 354 */ 355 argvp->argv[argvp->argc] = 0; 356 } 357 358 /* argv_truncate - truncate string array */ 359 360 void argv_truncate(ARGV *argvp, ssize_t len) 361 { 362 char **cpp; 363 364 /* 365 * Sanity check. 366 */ 367 if (len < 0) 368 msg_panic("argv_truncate: bad length %ld", (long) len); 369 370 if (len < argvp->argc) { 371 for (cpp = argvp->argv + len; cpp < argvp->argv + argvp->argc; cpp++) 372 myfree(*cpp); 373 argvp->argc = len; 374 argvp->argv[argvp->argc] = 0; 375 } 376 } 377 378 /* argv_insert_one - insert one string into array */ 379 380 void argv_insert_one(ARGV *argvp, ssize_t where, const char *arg) 381 { 382 ssize_t pos; 383 384 /* 385 * Sanity check. 386 */ 387 if (where < 0 || where > argvp->argc) 388 msg_panic("argv_insert_one bad position: %ld", (long) where); 389 390 if (ARGV_SPACE_LEFT(argvp) <= 0) 391 argv_extend(argvp); 392 for (pos = argvp->argc; pos >= where; pos--) 393 argvp->argv[pos + 1] = argvp->argv[pos]; 394 argvp->argv[where] = mystrdup(arg); 395 argvp->argc += 1; 396 } 397 398 /* argv_replace_one - replace one string in array */ 399 400 void argv_replace_one(ARGV *argvp, ssize_t where, const char *arg) 401 { 402 char *temp; 403 404 /* 405 * Sanity check. 406 */ 407 if (where < 0 || where >= argvp->argc) 408 msg_panic("argv_replace_one bad position: %ld", (long) where); 409 410 temp = argvp->argv[where]; 411 argvp->argv[where] = mystrdup(arg); 412 myfree(temp); 413 } 414 415 /* argv_delete - remove string(s) from array */ 416 417 void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many) 418 { 419 ssize_t pos; 420 421 /* 422 * Sanity check. 202604 Claude: avoid expression 'first + how_many'. 423 */ 424 if (first < 0 || how_many < 0 || first > argvp->argc 425 || how_many > argvp->argc - first) 426 msg_panic("argv_delete bad range: (start=%ld count=%ld)", 427 (long) first, (long) how_many); 428 429 for (pos = first; pos < first + how_many; pos++) 430 myfree(argvp->argv[pos]); 431 for (pos = first; pos <= argvp->argc - how_many; pos++) 432 argvp->argv[pos] = argvp->argv[pos + how_many]; 433 argvp->argc -= how_many; 434 } 435 436 /* argv_join - concatenate array elements with delimiter */ 437 438 char *argv_join(VSTRING *buf, ARGV *argv, int delim) 439 { 440 char **cpp; 441 442 for (cpp = argv->argv; *cpp; cpp++) { 443 vstring_strcat(buf, *cpp); 444 if (cpp[1]) 445 VSTRING_ADDCH(buf, delim); 446 } 447 return (vstring_str(buf)); 448 } 449 450 #ifdef TEST 451 452 /* 453 * System library. 454 */ 455 #include <setjmp.h> 456 457 /* 458 * Utility library. 459 */ 460 #include <msg_vstream.h> 461 #include <stringops.h> 462 463 #define ARRAY_LEN (10) 464 465 typedef struct TEST_CASE { 466 const char *label; /* identifies test case */ 467 const char *inputs[ARRAY_LEN]; /* input strings */ 468 int terminate; /* terminate result */ 469 ARGV *(*populate_fn) (const struct TEST_CASE *, ARGV *); 470 const char *exp_panic_msg; /* expected panic */ 471 int exp_argc; /* expected array length */ 472 const char *exp_argv[ARRAY_LEN]; /* expected array content */ 473 int join_delim; /* argv_join() delimiter */ 474 } TEST_CASE; 475 476 #define TERMINATE_ARRAY (1) 477 478 #define PASS (0) 479 #define FAIL (1) 480 481 VSTRING *test_panic_str; 482 jmp_buf test_panic_jbuf; 483 484 /* test_msg_panic - does not return, and does not terminate */ 485 486 void test_msg_panic(const char *fmt,...) 487 { 488 va_list ap; 489 490 va_start(ap, fmt); 491 test_panic_str = vstring_alloc(100); 492 vstring_vsprintf(test_panic_str, fmt, ap); 493 va_end(ap); 494 longjmp(test_panic_jbuf, 1); 495 } 496 497 /* test_argv_populate - populate result, optionally terminate */ 498 499 static ARGV *test_argv_populate(const TEST_CASE *tp, ARGV *argvp) 500 { 501 const char *const * cpp; 502 503 for (cpp = tp->inputs; *cpp; cpp++) 504 argv_add(argvp, *cpp, (char *) 0); 505 if (tp->terminate) 506 argv_terminate(argvp); 507 return (argvp); 508 } 509 510 /* test_argv_sort - populate and sort result */ 511 512 static ARGV *test_argv_sort(const TEST_CASE *tp, ARGV *argvp) 513 { 514 test_argv_populate(tp, argvp); 515 argv_qsort(argvp, (ARGV_COMPAR_FN) 0); 516 return (argvp); 517 } 518 519 /* test_argv_sort_uniq - populate, sort, uniq result */ 520 521 static ARGV *test_argv_sort_uniq(const TEST_CASE *tp, ARGV *argvp) 522 { 523 524 /* 525 * This also tests argv_delete(). 526 */ 527 test_argv_sort(tp, argvp); 528 argv_uniq(argvp, (ARGV_COMPAR_FN) 0); 529 return (argvp); 530 } 531 532 /* test_argv_good_truncate - populate and truncate to good size */ 533 534 static ARGV *test_argv_good_truncate(const TEST_CASE *tp, ARGV *argvp) 535 { 536 test_argv_populate(tp, argvp); 537 argv_truncate(argvp, tp->exp_argc); 538 return (argvp); 539 } 540 541 /* test_argv_bad_truncate - populate and truncate to bad size */ 542 543 static ARGV *test_argv_bad_truncate(const TEST_CASE *tp, ARGV *argvp) 544 { 545 test_argv_populate(tp, argvp); 546 argv_truncate(argvp, -1); 547 return (argvp); 548 } 549 550 /* test_argv_good_insert - populate and insert at good position */ 551 552 static ARGV *test_argv_good_insert(const TEST_CASE *tp, ARGV *argvp) 553 { 554 test_argv_populate(tp, argvp); 555 argv_insert_one(argvp, 1, "new"); 556 return (argvp); 557 } 558 559 /* test_argv_bad_insert1 - populate and insert at bad position */ 560 561 static ARGV *test_argv_bad_insert1(const TEST_CASE *tp, ARGV *argvp) 562 { 563 test_argv_populate(tp, argvp); 564 argv_insert_one(argvp, -1, "new"); 565 return (argvp); 566 } 567 568 /* test_argv_bad_insert2 - populate and insert at bad position */ 569 570 static ARGV *test_argv_bad_insert2(const TEST_CASE *tp, ARGV *argvp) 571 { 572 test_argv_populate(tp, argvp); 573 argv_insert_one(argvp, 100, "new"); 574 return (argvp); 575 } 576 577 /* test_argv_good_replace - populate and replace at good position */ 578 579 static ARGV *test_argv_good_replace(const TEST_CASE *tp, ARGV *argvp) 580 { 581 test_argv_populate(tp, argvp); 582 argv_replace_one(argvp, 1, "new"); 583 return (argvp); 584 } 585 586 /* test_argv_bad_replace1 - populate and replace at bad position */ 587 588 static ARGV *test_argv_bad_replace1(const TEST_CASE *tp, ARGV *argvp) 589 { 590 test_argv_populate(tp, argvp); 591 argv_replace_one(argvp, -1, "new"); 592 return (argvp); 593 } 594 595 /* test_argv_bad_replace2 - populate and replace at bad position */ 596 597 static ARGV *test_argv_bad_replace2(const TEST_CASE *tp, ARGV *argvp) 598 { 599 test_argv_populate(tp, argvp); 600 argv_replace_one(argvp, 100, "new"); 601 return (argvp); 602 } 603 604 /* test_argv_bad_delete1 - populate and delete at bad position */ 605 606 static ARGV *test_argv_bad_delete1(const TEST_CASE *tp, ARGV *argvp) 607 { 608 test_argv_populate(tp, argvp); 609 argv_delete(argvp, -1, 1); 610 return (argvp); 611 } 612 613 /* test_argv_bad_delete2 - populate and delete at bad position */ 614 615 static ARGV *test_argv_bad_delete2(const TEST_CASE *tp, ARGV *argvp) 616 { 617 test_argv_populate(tp, argvp); 618 argv_delete(argvp, 0, -1); 619 return (argvp); 620 } 621 622 /* test_argv_bad_delete3 - populate and delete at bad position */ 623 624 static ARGV *test_argv_bad_delete3(const TEST_CASE *tp, ARGV *argvp) 625 { 626 test_argv_populate(tp, argvp); 627 argv_delete(argvp, 100, 1); 628 return (argvp); 629 } 630 631 /* test_argv_join - populate, join, and overwrite */ 632 633 static ARGV *test_argv_join(const TEST_CASE *tp, ARGV *argvp) 634 { 635 VSTRING *buf = vstring_alloc(100); 636 637 /* 638 * Impedance mismatch: argv_join() produces output to VSTRING, but the 639 * test fixture wants output to ARGV. 640 */ 641 test_argv_populate(tp, argvp); 642 argv_join(buf, argvp, tp->join_delim); 643 argv_delete(argvp, 0, argvp->argc); 644 argv_add(argvp, vstring_str(buf), ARGV_END); 645 vstring_free(buf); 646 return (argvp); 647 } 648 649 /* test_argv_addv_appends - populate result */ 650 651 static ARGV *test_argv_addv_appends(const TEST_CASE *tp, ARGV *argvp) 652 { 653 argvp = argv_addv(argvp, tp->inputs); 654 return (argvp); 655 } 656 657 /* test_argv_addv_creates_appends - populate result */ 658 659 static ARGV *test_argv_addv_creates(const TEST_CASE *tp, ARGV *argvp) 660 { 661 argv_free(argvp); 662 argvp = argv_addv((ARGV *) 0, tp->inputs); 663 return (argvp); 664 } 665 666 /* test_argv_verify - verify result */ 667 668 static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp) 669 { 670 int idx; 671 672 if (tp->exp_panic_msg != 0) { 673 if (test_panic_str == 0) { 674 msg_warn("test case '%s': got no panic, want: '%s'", 675 tp->label, tp->exp_panic_msg); 676 return (FAIL); 677 } 678 if (strcmp(vstring_str(test_panic_str), tp->exp_panic_msg) != 0) { 679 msg_warn("test case '%s': got '%s', want: '%s'", 680 tp->label, vstring_str(test_panic_str), tp->exp_panic_msg); 681 return (FAIL); 682 } 683 return (PASS); 684 } 685 if (test_panic_str != 0) { 686 msg_warn("test case '%s': got '%s', want: no panic", 687 tp->label, vstring_str(test_panic_str)); 688 return (FAIL); 689 } 690 if (argvp->argc != tp->exp_argc) { 691 msg_warn("test case '%s': got argc: %ld, want: %d", 692 tp->label, (long) argvp->argc, tp->exp_argc); 693 return (FAIL); 694 } 695 if (argvp->argv[argvp->argc] != 0 && tp->terminate) { 696 msg_warn("test case '%s': got unterminated, want: terminated", tp->label); 697 return (FAIL); 698 } 699 for (idx = 0; idx < argvp->argc; idx++) { 700 if (strcmp(argvp->argv[idx], tp->exp_argv[idx]) != 0) { 701 msg_warn("test case '%s': index %d: got '%s', want: '%s'", 702 tp->label, idx, argvp->argv[idx], tp->exp_argv[idx]); 703 return (FAIL); 704 } 705 } 706 return (PASS); 707 } 708 709 /* 710 * The test cases. TODO: argv_addn with good and bad string length. 711 */ 712 static const TEST_CASE test_cases[] = { 713 {"multiple strings, unterminated array", 714 {"foo", "baz", "bar", 0}, 0, test_argv_populate, 715 0, 3, {"foo", "baz", "bar", 0} 716 }, 717 {"multiple strings, terminated array", 718 {"foo", "baz", "bar", 0}, TERMINATE_ARRAY, test_argv_populate, 719 0, 3, {"foo", "baz", "bar", 0} 720 }, 721 {"distinct strings, sorted array", 722 {"foo", "baz", "bar", 0}, 0, test_argv_sort, 723 0, 3, {"bar", "baz", "foo", 0} 724 }, 725 {"duplicate strings, sorted array", 726 {"foo", "baz", "baz", "bar", 0}, 0, test_argv_sort, 727 0, 4, {"bar", "baz", "baz", "foo", 0} 728 }, 729 {"duplicate strings, sorted, uniqued-middle elements", 730 {"foo", "baz", "baz", "bar", 0}, 0, test_argv_sort_uniq, 731 0, 3, {"bar", "baz", "foo", 0} 732 }, 733 {"duplicate strings, sorted, uniqued-first elements", 734 {"foo", "bar", "baz", "bar", 0}, 0, test_argv_sort_uniq, 735 0, 3, {"bar", "baz", "foo", 0} 736 }, 737 {"duplicate strings, sorted, uniqued-last elements", 738 {"foo", "foo", "baz", "bar", 0}, 0, test_argv_sort_uniq, 739 0, 3, {"bar", "baz", "foo", 0} 740 }, 741 {"multiple strings, truncate array by one", 742 {"foo", "baz", "bar", 0}, 0, test_argv_good_truncate, 743 0, 2, {"foo", "baz", 0} 744 }, 745 {"multiple strings, truncate whole array", 746 {"foo", "baz", "bar", 0}, 0, test_argv_good_truncate, 747 0, 0, {"foo", "baz", 0} 748 }, 749 {"multiple strings, bad truncate", 750 {"foo", "baz", "bar", 0}, 0, test_argv_bad_truncate, 751 "argv_truncate: bad length -1" 752 }, 753 {"multiple strings, insert one at good position", 754 {"foo", "baz", "bar", 0}, 0, test_argv_good_insert, 755 0, 4, {"foo", "new", "baz", "bar", 0} 756 }, 757 {"multiple strings, insert one at bad position", 758 {"foo", "baz", "bar", 0}, 0, test_argv_bad_insert1, 759 "argv_insert_one bad position: -1" 760 }, 761 {"multiple strings, insert one at bad position", 762 {"foo", "baz", "bar", 0}, 0, test_argv_bad_insert2, 763 "argv_insert_one bad position: 100" 764 }, 765 {"multiple strings, replace one at good position", 766 {"foo", "baz", "bar", 0}, 0, test_argv_good_replace, 767 0, 3, {"foo", "new", "bar", 0} 768 }, 769 {"multiple strings, replace one at bad position", 770 {"foo", "baz", "bar", 0}, 0, test_argv_bad_replace1, 771 "argv_replace_one bad position: -1" 772 }, 773 {"multiple strings, replace one at bad position", 774 {"foo", "baz", "bar", 0}, 0, test_argv_bad_replace2, 775 "argv_replace_one bad position: 100" 776 }, 777 {"multiple strings, delete one at negative position", 778 {"foo", "baz", "bar", 0}, 0, test_argv_bad_delete1, 779 "argv_delete bad range: (start=-1 count=1)" 780 }, 781 {"multiple strings, delete with bad range end", 782 {"foo", "baz", "bar", 0}, 0, test_argv_bad_delete2, 783 "argv_delete bad range: (start=0 count=-1)" 784 }, 785 {"multiple strings, delete at too large position", 786 {"foo", "baz", "bar", 0}, 0, test_argv_bad_delete3, 787 "argv_delete bad range: (start=100 count=1)" 788 }, 789 {"argv_join, multiple strings", 790 {"foo", "baz", "bar", 0}, 0, test_argv_join, 791 0, 1, {"foo:baz:bar", 0}, ':' 792 }, 793 {"argv_join, one string", 794 {"foo", 0}, 0, test_argv_join, 795 0, 1, {"foo", 0}, ':' 796 }, 797 {"argv_join, empty", 798 {0}, 0, test_argv_join, 799 0, 1, {"", 0}, ':' 800 }, 801 {"argv_addv appends to ARGV", 802 {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_appends, 803 0, 3, {"foo", "baz", "bar", 0} 804 }, 805 {"argv_addv creates ARGV", 806 {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_creates, 807 0, 3, {"foo", "baz", "bar", 0} 808 }, 809 0, 810 }; 811 812 int main(int argc, char **argv) 813 { 814 const TEST_CASE *tp; 815 int pass = 0; 816 int fail = 0; 817 818 msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR); 819 820 for (tp = test_cases; tp->label != 0; tp++) { 821 int test_failed; 822 ARGV *argvp; 823 824 argvp = argv_alloc(1); 825 if (setjmp(test_panic_jbuf) == 0) 826 argvp = tp->populate_fn(tp, argvp); 827 test_failed = test_argv_verify(tp, argvp); 828 if (test_failed) { 829 msg_info("%s: FAIL", tp->label); 830 fail++; 831 } else { 832 msg_info("%s: PASS", tp->label); 833 pass++; 834 } 835 argv_free(argvp); 836 if (test_panic_str) { 837 vstring_free(test_panic_str); 838 test_panic_str = 0; 839 } 840 } 841 msg_info("PASS=%d FAIL=%d", pass, fail); 842 exit(fail != 0); 843 } 844 845 #endif 846