xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/argv.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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