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