/*++
/* NAME
/* argv 3
/* SUMMARY
/* string array utilities
/* SYNOPSIS
/* #include <argv.h>
/*
/* typedef int (*ARGV_COMPAR_FN)(const void *, const void *);
/*
/* ARGV *argv_alloc(len)
/* ssize_t len;
/*
/* ARGV *argv_qsort(argvp, compar)
/* ARGV *argvp;
/* ARGV_COMPAR_FN compar;
/*
/* void argv_uniq(argvp, compar)
/* ARGV *argvp;
/* ARGV_COMPAR_FN compar;
/*
/* ARGV *argv_free(argvp)
/* ARGV *argvp;
/*
/* void argv_add(argvp, arg, ..., ARGV_END)
/* ARGV *argvp;
/* char *arg;
/*
/* void argv_addn(argvp, arg, arg_len, ..., ARGV_END)
/* ARGV *argvp;
/* char *arg;
/* ssize_t arg_len;
/*
/* ARGV *argv_addv(argvp, argv)
/* ARGV *argvp;
/* const char **argv;
/*
/* void argv_terminate(argvp);
/* ARGV *argvp;
/*
/* void argv_truncate(argvp, len);
/* ARGV *argvp;
/* ssize_t len;
/*
/* void argv_insert_one(argvp, pos, arg)
/* ARGV *argvp;
/* ssize_t pos;
/* const char *arg;
/*
/* void argv_replace_one(argvp, pos, arg)
/* ARGV *argvp;
/* ssize_t pos;
/* const char *arg;
/*
/* void argv_delete(argvp, pos, how_many)
/* ARGV *argvp;
/* ssize_t pos;
/* ssize_t how_many;
/*
/* char *argv_join(buf, argvp, delim)
/* VSTRING *buf;
/* ARGV *argvp;
/* int delim;
/*
/* void ARGV_FAKE_BEGIN(argv, arg)
/* const char *arg;
/*
/* void ARGV_FAKE2_BEGIN(argv, arg1, arg2)
/* const char *arg1;
/* const char *arg2;
/*
/* void ARGV_FAKE_END
/* DESCRIPTION
/* The functions in this module manipulate arrays of string
/* pointers. An ARGV structure contains the following members:
/* .IP len
/* The length of the \fIargv\fR array member.
/* .IP argc
/* The number of \fIargv\fR elements used.
/* .IP argv
/* An array of pointers to null-terminated strings.
/* .PP
/* argv_alloc() returns an empty string array of the requested
/* length. The result is ready for use by argv_add(). The array
/* is null terminated.
/*
/* argv_qsort() sorts the elements of argvp in place, and
/* returns its first argument. If the compar argument specifies
/* a null pointer, then argv_qsort() will use byte-by-byte
/* comparison.
/*
/* argv_uniq() reduces adjacent same-value elements to one
/* element, and returns its first argument. If the compar
/* argument specifies a null pointer, then argv_uniq() will
/* use byte-by-byte comparison.
/*
/* argv_add() copies zero or more strings and adds them to the
/* specified string array. The array is null terminated.
/* Terminate the argument list with a null pointer. The manifest
/* constant ARGV_END provides a convenient notation for this.
/*
/* argv_addn() is like argv_add(), but each string is followed
/* by a string length argument.
/*
/* argv_addv() optionally creates an ARGV when the first argument
/* is a null pointer, and appends a null-terminated list of
/* strings. The result is null terminated.
/*
/* argv_free() releases storage for a string array, and conveniently
/* returns a null pointer.
/*
/* argv_terminate() null-terminates its string array argument.
/*
/* argv_truncate() truncates its argument to the specified
/* number of entries, but does not reallocate memory. The
/* result is null-terminated.
/*
/* argv_insert_one() inserts one string at the specified array
/* position.
/*
/* argv_replace_one() replaces one string at the specified
/* position. The old string is destroyed after the update is
/* made.
/*
/* argv_delete() deletes the specified number of elements
/* starting at the specified array position. The result is
/* null-terminated.
/*
/* argv_join() joins all elements in an array using the
/* specified delimiter value, and appends the result to the
/* specified buffer.
/*
/* ARGV_FAKE_BEGIN/END are an optimization for the case where
/* a single string needs to be passed into an ARGV-based
/* interface. ARGV_FAKE_BEGIN() opens a statement block and
/* allocates a stack-based ARGV structure named after the first
/* argument, that encapsulates the second argument. This
/* implementation allocates no heap memory and creates no copy
/* of the second argument. ARGV_FAKE_END closes the statement
/* block and thereby releases storage.
/*
/* ARGV_FAKE2_BEGIN/END provide the same functionality for a pair
/* of strings.
/* SEE ALSO
/* msg(3) diagnostics interface
/* DIAGNOSTICS
/* Fatal errors: memory allocation problem.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System libraries. */
#include <sys_defs.h>
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
#include <stdarg.h>
#include <string.h>
/* Application-specific. */
#include "mymalloc.h"
#include "msg.h"
#include "vstring.h"
#include "argv.h"
#ifdef TEST
extern NORETURN PRINTFLIKE(1 , 2 ) test_msg_panic(const char *,...);
#define msg_panic test_msg_panic
#endif
/* argv_free - destroy string array */
ARGV *argv_free(ARGV *argvp)
{
char **cpp;
for (cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++)
myfree(*cpp);
myfree((void *) argvp->argv);
myfree((void *) argvp);
return (0 );
}
/* argv_alloc - initialize string array */
ARGV *argv_alloc(ssize_t len)
{
ARGV *argvp;
ssize_t sane_len;
/*
* Make sure that always argvp - > argc < argvp - > len .
*/
argvp = (ARGV *) mymalloc(sizeof (*argvp));
argvp->len = 0 ;
sane_len = (len < 2 ? 2 : len);
argvp->argv = (char **) mymalloc((sane_len + 1 ) * sizeof (char *));
argvp->len = sane_len;
argvp->argc = 0 ;
argvp->argv[0 ] = 0 ;
return (argvp);
}
static int argv_cmp(const void *e1, const void *e2)
{
const char *s1 = *(const char **) e1;
const char *s2 = *(const char **) e2;
return strcmp(s1, s2);
}
/* argv_qsort - sort array in place */
ARGV *argv_qsort(ARGV *argvp, ARGV_COMPAR_FN compar)
{
qsort(argvp->argv, argvp->argc, sizeof (argvp->argv[0 ]),
compar ? compar : argv_cmp);
return (argvp);
}
/* argv_sort - binary compatibility */
ARGV *argv_sort(ARGV *argvp)
{
qsort(argvp->argv, argvp->argc, sizeof (argvp->argv[0 ]), argv_cmp);
return (argvp);
}
/* argv_uniq - deduplicate adjacent array elements */
ARGV *argv_uniq(ARGV *argvp, ARGV_COMPAR_FN compar)
{
char **cpp;
char **prev;
if (compar == 0 )
compar = argv_cmp;
for (prev = 0 , cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++) {
if (prev != 0 && compar(prev, cpp) == 0 ) {
argv_delete(argvp, cpp - argvp->argv, 1 );
cpp = prev;
} else {
prev = cpp;
}
}
return (argvp);
}
/* argv_extend - extend array */
static void argv_extend(ARGV *argvp)
{
ssize_t new_len;
new_len = argvp->len * 2 ;
argvp->argv = (char **)
myrealloc((void *) argvp->argv, (new_len + 1 ) * sizeof (char *));
argvp->len = new_len;
}
/* argv_add - add string to vector */
void argv_add(ARGV *argvp,...)
{
char *arg;
va_list ap;
/*
* Make sure that always argvp - > argc < argvp - > len .
*/
#define ARGV_SPACE_LEFT(a) ((a)->len - (a)->argc - 1 )
va_start(ap, argvp);
while ((arg = va_arg(ap, char *)) != 0 ) {
if (ARGV_SPACE_LEFT(argvp) <= 0 )
argv_extend(argvp);
argvp->argv[argvp->argc++] = mystrdup(arg);
}
va_end(ap);
argvp->argv[argvp->argc] = 0 ;
}
/* argv_addn - add string to vector */
void argv_addn(ARGV *argvp,...)
{
char *arg;
ssize_t len;
va_list ap;
/*
* Make sure that always argvp - > argc < argvp - > len .
*/
va_start(ap, argvp);
while ((arg = va_arg(ap, char *)) != 0 ) {
if ((len = va_arg(ap, ssize_t)) < 0 )
msg_panic("argv_addn: bad string length %ld" , (long ) len);
if (ARGV_SPACE_LEFT(argvp) <= 0 )
argv_extend(argvp);
argvp->argv[argvp->argc++] = mystrndup(arg, len);
}
va_end(ap);
argvp->argv[argvp->argc] = 0 ;
}
/* argv_addv - optionally create ARGV, append string vector */
ARGV *argv_addv(ARGV *argvp, const char *const * argv)
{
const char *const * cpp;
if (argvp == 0 ) {
for (cpp = argv; *cpp; cpp++)
/* void */ ;
argvp = argv_alloc(cpp - argv);
}
for (cpp = argv; *cpp; cpp++)
argv_add(argvp, *cpp, (char *) 0 );
argvp->argv[argvp->argc] = 0 ;
return (argvp);
}
/* argv_terminate - terminate string array */
void argv_terminate(ARGV *argvp)
{
/*
* Trust that argvp - > argc < argvp - > len .
*/
argvp->argv[argvp->argc] = 0 ;
}
/* argv_truncate - truncate string array */
void argv_truncate(ARGV *argvp, ssize_t len)
{
char **cpp;
/*
* Sanity check .
*/
if (len < 0 )
msg_panic("argv_truncate: bad length %ld" , (long ) len);
if (len < argvp->argc) {
for (cpp = argvp->argv + len; cpp < argvp->argv + argvp->argc; cpp++)
myfree(*cpp);
argvp->argc = len;
argvp->argv[argvp->argc] = 0 ;
}
}
/* argv_insert_one - insert one string into array */
void argv_insert_one(ARGV *argvp, ssize_t where, const char *arg)
{
ssize_t pos;
/*
* Sanity check .
*/
if (where < 0 || where > argvp->argc)
msg_panic("argv_insert_one bad position: %ld" , (long ) where);
if (ARGV_SPACE_LEFT(argvp) <= 0 )
argv_extend(argvp);
for (pos = argvp->argc; pos >= where; pos--)
argvp->argv[pos + 1 ] = argvp->argv[pos];
argvp->argv[where] = mystrdup(arg);
argvp->argc += 1 ;
}
/* argv_replace_one - replace one string in array */
void argv_replace_one(ARGV *argvp, ssize_t where, const char *arg)
{
char *temp;
/*
* Sanity check .
*/
if (where < 0 || where >= argvp->argc)
msg_panic("argv_replace_one bad position: %ld" , (long ) where);
temp = argvp->argv[where];
argvp->argv[where] = mystrdup(arg);
myfree(temp);
}
/* argv_delete - remove string(s) from array */
void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many)
{
ssize_t pos;
/*
* Sanity check .
*/
if (first < 0 || how_many < 0 || first + how_many > argvp->argc)
msg_panic("argv_delete bad range: (start=%ld count=%ld)" ,
(long ) first, (long ) how_many);
for (pos = first; pos < first + how_many; pos++)
myfree(argvp->argv[pos]);
for (pos = first; pos <= argvp->argc - how_many; pos++)
argvp->argv[pos] = argvp->argv[pos + how_many];
argvp->argc -= how_many;
}
/* argv_join - concatenate array elements with delimiter */
char *argv_join(VSTRING *buf, ARGV *argv, int delim)
{
char **cpp;
for (cpp = argv->argv; *cpp; cpp++) {
vstring_strcat(buf, *cpp);
if (cpp[1 ])
VSTRING_ADDCH(buf, delim);
}
return (vstring_str(buf));
}
#ifdef TEST
/*
* System library .
*/
#include <setjmp.h>
/*
* Utility library .
*/
#include <msg_vstream.h>
#include <stringops.h>
#define ARRAY_LEN (10 )
typedef struct TEST_CASE {
const char *label; /* identifies test case */
const char *inputs[ARRAY_LEN]; /* input strings */
int terminate; /* terminate result */
ARGV *(*populate_fn) (const struct TEST_CASE *, ARGV *);
const char *exp_panic_msg; /* expected panic */
int exp_argc; /* expected array length */
const char *exp_argv[ARRAY_LEN]; /* expected array content */
int join_delim; /* argv_join() delimiter */
} TEST_CASE;
#define TERMINATE_ARRAY (1 )
#define PASS (0 )
#define FAIL (1 )
VSTRING *test_panic_str;
jmp_buf test_panic_jbuf;
/* test_msg_panic - does not return, and does not terminate */
void test_msg_panic(const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
test_panic_str = vstring_alloc(100 );
vstring_vsprintf(test_panic_str, fmt, ap);
va_end(ap);
longjmp(test_panic_jbuf, 1 );
}
/* test_argv_populate - populate result, optionally terminate */
static ARGV *test_argv_populate(const TEST_CASE *tp, ARGV *argvp)
{
const char *const * cpp;
for (cpp = tp->inputs; *cpp; cpp++)
argv_add(argvp, *cpp, (char *) 0 );
if (tp->terminate)
argv_terminate(argvp);
return (argvp);
}
/* test_argv_sort - populate and sort result */
static ARGV *test_argv_sort(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_qsort(argvp, (ARGV_COMPAR_FN) 0 );
return (argvp);
}
/* test_argv_sort_uniq - populate, sort, uniq result */
static ARGV *test_argv_sort_uniq(const TEST_CASE *tp, ARGV *argvp)
{
/*
* This also tests argv_delete ( ) .
*/
test_argv_sort(tp, argvp);
argv_uniq(argvp, (ARGV_COMPAR_FN) 0 );
return (argvp);
}
/* test_argv_good_truncate - populate and truncate to good size */
static ARGV *test_argv_good_truncate(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_truncate(argvp, tp->exp_argc);
return (argvp);
}
/* test_argv_bad_truncate - populate and truncate to bad size */
static ARGV *test_argv_bad_truncate(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_truncate(argvp, -1 );
return (argvp);
}
/* test_argv_good_insert - populate and insert at good position */
static ARGV *test_argv_good_insert(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_insert_one(argvp, 1 , "new" );
return (argvp);
}
/* test_argv_bad_insert1 - populate and insert at bad position */
static ARGV *test_argv_bad_insert1(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_insert_one(argvp, -1 , "new" );
return (argvp);
}
/* test_argv_bad_insert2 - populate and insert at bad position */
static ARGV *test_argv_bad_insert2(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_insert_one(argvp, 100 , "new" );
return (argvp);
}
/* test_argv_good_replace - populate and replace at good position */
static ARGV *test_argv_good_replace(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_replace_one(argvp, 1 , "new" );
return (argvp);
}
/* test_argv_bad_replace1 - populate and replace at bad position */
static ARGV *test_argv_bad_replace1(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_replace_one(argvp, -1 , "new" );
return (argvp);
}
/* test_argv_bad_replace2 - populate and replace at bad position */
static ARGV *test_argv_bad_replace2(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_replace_one(argvp, 100 , "new" );
return (argvp);
}
/* test_argv_bad_delete1 - populate and delete at bad position */
static ARGV *test_argv_bad_delete1(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_delete(argvp, -1 , 1 );
return (argvp);
}
/* test_argv_bad_delete2 - populate and delete at bad position */
static ARGV *test_argv_bad_delete2(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_delete(argvp, 0 , -1 );
return (argvp);
}
/* test_argv_bad_delete3 - populate and delete at bad position */
static ARGV *test_argv_bad_delete3(const TEST_CASE *tp, ARGV *argvp)
{
test_argv_populate(tp, argvp);
argv_delete(argvp, 100 , 1 );
return (argvp);
}
/* test_argv_join - populate, join, and overwrite */
static ARGV *test_argv_join(const TEST_CASE *tp, ARGV *argvp)
{
VSTRING *buf = vstring_alloc(100 );
/*
* Impedance mismatch : argv_join ( ) produces output to VSTRING , but the
* test fixture wants output to ARGV .
*/
test_argv_populate(tp, argvp);
argv_join(buf, argvp, tp->join_delim);
argv_delete(argvp, 0 , argvp->argc);
argv_add(argvp, vstring_str(buf), ARGV_END);
vstring_free(buf);
return (argvp);
}
/* test_argv_addv_appends - populate result */
static ARGV *test_argv_addv_appends(const TEST_CASE *tp, ARGV *argvp)
{
argvp = argv_addv(argvp, tp->inputs);
return (argvp);
}
/* test_argv_addv_creates_appends - populate result */
static ARGV *test_argv_addv_creates(const TEST_CASE *tp, ARGV *argvp)
{
argv_free(argvp);
argvp = argv_addv((ARGV *) 0 , tp->inputs);
return (argvp);
}
/* test_argv_verify - verify result */
static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp)
{
int idx;
if (tp->exp_panic_msg != 0 ) {
if (test_panic_str == 0 ) {
msg_warn("test case '%s': got no panic, want: '%s'" ,
tp->label, tp->exp_panic_msg);
return (FAIL);
}
if (strcmp(vstring_str(test_panic_str), tp->exp_panic_msg) != 0 ) {
msg_warn("test case '%s': got '%s', want: '%s'" ,
tp->label, vstring_str(test_panic_str), tp->exp_panic_msg);
return (FAIL);
}
return (PASS);
}
if (test_panic_str != 0 ) {
msg_warn("test case '%s': got '%s', want: no panic" ,
tp->label, vstring_str(test_panic_str));
return (FAIL);
}
if (argvp->argc != tp->exp_argc) {
msg_warn("test case '%s': got argc: %ld, want: %d" ,
tp->label, (long ) argvp->argc, tp->exp_argc);
return (FAIL);
}
if (argvp->argv[argvp->argc] != 0 && tp->terminate) {
msg_warn("test case '%s': got unterminated, want: terminated" , tp->label);
return (FAIL);
}
for (idx = 0 ; idx < argvp->argc; idx++) {
if (strcmp(argvp->argv[idx], tp->exp_argv[idx]) != 0 ) {
msg_warn("test case '%s': index %d: got '%s', want: '%s'" ,
tp->label, idx, argvp->argv[idx], tp->exp_argv[idx]);
return (FAIL);
}
}
return (PASS);
}
/*
* The test cases . TODO : argv_addn with good and bad string length .
*/
static const TEST_CASE test_cases[] = {
{"multiple strings, unterminated array" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_populate,
0 , 3 , {"foo" , "baz" , "bar" , 0 }
},
{"multiple strings, terminated array" ,
{"foo" , "baz" , "bar" , 0 }, TERMINATE_ARRAY, test_argv_populate,
0 , 3 , {"foo" , "baz" , "bar" , 0 }
},
{"distinct strings, sorted array" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_sort,
0 , 3 , {"bar" , "baz" , "foo" , 0 }
},
{"duplicate strings, sorted array" ,
{"foo" , "baz" , "baz" , "bar" , 0 }, 0 , test_argv_sort,
0 , 4 , {"bar" , "baz" , "baz" , "foo" , 0 }
},
{"duplicate strings, sorted, uniqued-middle elements" ,
{"foo" , "baz" , "baz" , "bar" , 0 }, 0 , test_argv_sort_uniq,
0 , 3 , {"bar" , "baz" , "foo" , 0 }
},
{"duplicate strings, sorted, uniqued-first elements" ,
{"foo" , "bar" , "baz" , "bar" , 0 }, 0 , test_argv_sort_uniq,
0 , 3 , {"bar" , "baz" , "foo" , 0 }
},
{"duplicate strings, sorted, uniqued-last elements" ,
{"foo" , "foo" , "baz" , "bar" , 0 }, 0 , test_argv_sort_uniq,
0 , 3 , {"bar" , "baz" , "foo" , 0 }
},
{"multiple strings, truncate array by one" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_good_truncate,
0 , 2 , {"foo" , "baz" , 0 }
},
{"multiple strings, truncate whole array" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_good_truncate,
0 , 0 , {"foo" , "baz" , 0 }
},
{"multiple strings, bad truncate" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_truncate,
"argv_truncate: bad length -1"
},
{"multiple strings, insert one at good position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_good_insert,
0 , 4 , {"foo" , "new" , "baz" , "bar" , 0 }
},
{"multiple strings, insert one at bad position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_insert1,
"argv_insert_one bad position: -1"
},
{"multiple strings, insert one at bad position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_insert2,
"argv_insert_one bad position: 100"
},
{"multiple strings, replace one at good position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_good_replace,
0 , 3 , {"foo" , "new" , "bar" , 0 }
},
{"multiple strings, replace one at bad position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_replace1,
"argv_replace_one bad position: -1"
},
{"multiple strings, replace one at bad position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_replace2,
"argv_replace_one bad position: 100"
},
{"multiple strings, delete one at negative position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_delete1,
"argv_delete bad range: (start=-1 count=1)"
},
{"multiple strings, delete with bad range end" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_delete2,
"argv_delete bad range: (start=0 count=-1)"
},
{"multiple strings, delete at too large position" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_bad_delete3,
"argv_delete bad range: (start=100 count=1)"
},
{"argv_join, multiple strings" ,
{"foo" , "baz" , "bar" , 0 }, 0 , test_argv_join,
0 , 1 , {"foo:baz:bar" , 0 }, ':'
},
{"argv_join, one string" ,
{"foo" , 0 }, 0 , test_argv_join,
0 , 1 , {"foo" , 0 }, ':'
},
{"argv_join, empty" ,
{0 }, 0 , test_argv_join,
0 , 1 , {"" , 0 }, ':'
},
{"argv_addv appends to ARGV" ,
{"foo" , "baz" , "bar" , 0 }, /* ignored */ 0, test_argv_addv_appends,
0 , 3 , {"foo" , "baz" , "bar" , 0 }
},
{"argv_addv creates ARGV" ,
{"foo" , "baz" , "bar" , 0 }, /* ignored */ 0, test_argv_addv_creates,
0 , 3 , {"foo" , "baz" , "bar" , 0 }
},
0 ,
};
int main(int argc, char **argv)
{
const TEST_CASE *tp;
int pass = 0 ;
int fail = 0 ;
msg_vstream_init(sane_basename((VSTRING *) 0 , argv[0 ]), VSTREAM_ERR);
for (tp = test_cases; tp->label != 0 ; tp++) {
int test_failed;
ARGV *argvp;
argvp = argv_alloc(1 );
if (setjmp(test_panic_jbuf) == 0 )
argvp = tp->populate_fn(tp, argvp);
test_failed = test_argv_verify(tp, argvp);
if (test_failed) {
msg_info("%s: FAIL" , tp->label);
fail++;
} else {
msg_info("%s: PASS" , tp->label);
pass++;
}
argv_free(argvp);
if (test_panic_str) {
vstring_free(test_panic_str);
test_panic_str = 0 ;
}
}
msg_info("PASS=%d FAIL=%d" , pass, fail);
exit (fail != 0 );
}
#endif
Messung V0.5 in Prozent C=83 H=93 G=87
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland