28#define _RWSTD_TEST_SRC
72static size_t _rw_ncmdopts;
75static size_t _rw_ndefopts;
77static cmdopts_t *_rw_cmdopts = _rw_cmdoptbuf;
78static size_t _rw_optbufsize =
sizeof _rw_cmdoptbuf /
sizeof *_rw_cmdoptbuf;
83_rw_print_help (
int argc,
char *argv[])
85 if (1 == argc && argv && 0 == argv [0]) {
86 static const char helpstr[] = {
87 "Without an argument, prints this help to stdout and exits with\n"
88 "a status of 0 without further processing.\n"
89 "With the optional argument prints help on the option with that\n"
90 "name, if one exists, and exits with a status of 0. If an option\n"
91 "with the specified name does not exist, prints an error message\n"
92 "to stderr and exits with a status of 1.\n"
93 "The leading underscores in the name of an option are optional.\n"
102 const char* opthelp = 1 < argc ? argv [1] : 0;
105 if (opthelp &&
'-' == opthelp [0] && opthelp [1])
106 opthelp += 1 + (
'-' == opthelp [1]);
112 int option_found = 0;
114 for (
size_t i = 0;
i != _rw_ncmdopts; ++
i) {
122 if (opthelp && *opthelp) {
124 if ( (opt->
sopt_ == opthelp [0] &&
'\0' == opthelp [1])
125 || (*lopt && 0 == strcmp (lopt + 1, opthelp))) {
143 if (
i && 0 == _rw_cmdopts [
i - 1].callback_ && opt->
callback_)
155 const char *pfx =
"";
156 const char *sfx = pfx;
160 if (opt->
arg_ &&
'=' != lopt [strlen (lopt) - 1]) {
166 const char*
const arg =
168 || opt->
maxval_ < _RWSTD_INT_MAX ?
"int" :
"arg";
181 if (_RWSTD_INT_MIN < opt->minval_)
186 if (opt->
maxval_ < _RWSTD_INT_MAX)
193 printf (
" (each occurrence evaluated)\n");
195 printf (
" (at most %u occurrences evaluated)\n",
198 printf (
" (only the first occurrence evaluated)\n");
203 char* help [2] = { 0, 0 };
207 for (
const char *
line = help [0];
line; ) {
209 const char*
const nl = strchr (
line,
'\n');
210 const int len = nl ? int (nl -
line) : int (strlen (
line));
221 if (opthelp && !option_found) {
222 fprintf (
stderr,
"Unknown option \"%s\".\n", opthelp);
234_rw_set_ignenv (
int argc,
char *argv[])
236 if (1 == argc && argv && 0 == argv [0]) {
237 static const char helpstr[] = {
238 "Prevents options specified in the RWSTD_TESTOPTS environment\n"
239 "variable from taking effect.\n"
240 "Unless this option is specified, the RWSTD_TESTOPTS environment\n"
241 "variable is processed as if its value were specified on the \n"
243 "For example, setting the value of the variable to the string\n"
244 "\"--verbose --no-wchar\" and invoking this program with no\n"
245 "command line arguments will have the same effect as invoking\n"
246 "it with the two arguments on the command line.\n"
264 for (
size_t i = 0;
i != _rw_ncmdopts; ++
i) {
267 free (_rw_cmdopts [
i].lopt_);
270 if (_rw_cmdopts != _rw_cmdoptbuf) {
278 _rw_cmdopts = _rw_cmdoptbuf;
279 _rw_optbufsize =
sizeof _rw_cmdoptbuf /
sizeof *_rw_cmdoptbuf;
287 static int cleanup_handler_registered;
289 if (0 == cleanup_handler_registered) {
291 cleanup_handler_registered = 1;
294 if (0 != _rw_ncmdopts)
297 static int recursive;
310 _rw_ndefopts = _rw_ncmdopts;
317#ifndef _RWSTD_NO_VA_LIST_ARRAY
319# define RW_VA_LIST_ARG_PTR va_list
320# define RW_VA_LIST_ARG_TO_PTR(va) va
321# define RW_VA_LIST_PTR_ARG_TO_VA_LIST(va) va
324# define RW_VA_LIST_ARG_PTR va_list*
325# define RW_VA_LIST_ARG_TO_PTR(va) &va
326# define RW_VA_LIST_PTR_ARG_TO_VA_LIST(pva) *pva
335 if (
'*' == *next ||
'+' == *next ||
'-' == *next || sep == *next
336 || isdigit (*next)) {
338 bool have_maxval =
false;
340 if (*next == sep &&
'-' != sep) {
348 const long val =
'*' == *next ?
350 : strtol (next, &
end, 10);
352#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
354 if (
val < _RWSTD_INT_MIN || _RWSTD_INT_MAX <
val) {
356 "lower bound %ld out of range [%d, %d]: "
357 "%s\n",
val, _RWSTD_INT_MIN, _RWSTD_INT_MAX,
363 next =
end ?
end : next + 1;
366 _rw_cmdopts [_rw_ncmdopts].
minval_ = 0;
367 _rw_cmdopts [_rw_ncmdopts].
maxval_ = int (
val);
370 _rw_cmdopts [_rw_ncmdopts].
minval_ = int (
val);
372 if (sep == *next && !have_maxval) {
377 || (
val < 0 &&
'-' == *next)
378 || isdigit (*next)) {
383 const long maxval =
'*' == *next ?
385 : strtol (next, &
end, 10);
387#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
389 if (maxval < _RWSTD_INT_MIN || _RWSTD_INT_MAX < maxval) {
392 "upper bound %ld out of range [%ld, %d]: "
393 "%s\n", maxval,
val, _RWSTD_INT_MAX,
399 if (
'-' == sep && maxval <
val) {
402 "invalid range [%ld, %ld]: %s\n",
407 next =
end ?
end : next + 1;
408 _rw_cmdopts [_rw_ncmdopts].
maxval_ = int (maxval);
413 "syntax error in range specification: %s\n",
418 else if (*next && !isspace (*next)) {
421 "syntax error numeric argument: %s\n",
425 else if (!have_maxval) {
427 _rw_cmdopts [_rw_ncmdopts].
maxval_ = _RWSTD_INT_MAX;
430 else if (
':' == sep) {
432 _rw_cmdopts [_rw_ncmdopts].
minval_ = 0;
433 _rw_cmdopts [_rw_ncmdopts].
maxval_ = 0;
437 _rw_cmdopts [_rw_ncmdopts].
minval_ = _RWSTD_INT_MIN;
438 _rw_cmdopts [_rw_ncmdopts].
maxval_ = _RWSTD_INT_MAX;
443 _rw_cmdopts [_rw_ncmdopts].
maxcount_ = _RWSTD_SIZE_MAX;
460 const char *next = opts;
462 for ( ; ; ++_rw_ncmdopts) {
464 while (isspace (*next))
471 if (_rw_ncmdopts == _rw_optbufsize) {
473 const size_t newbufsize = 2 * _rw_ncmdopts + 1;
479 fprintf (
stderr,
"%s%d: failed to allocate memory\n",
484 memcpy (newopts, _rw_cmdopts, _rw_ncmdopts *
sizeof (
cmdopts_t));
486 if (_rw_cmdopts != _rw_cmdoptbuf)
489 _rw_cmdopts = newopts;
490 _rw_optbufsize = newbufsize;
494 memset (_rw_cmdopts + _rw_ncmdopts, 0,
sizeof *_rw_cmdopts);
497 cmdopts_t*
const lastopt = _rw_cmdopts + _rw_ncmdopts;
499 lastopt->
minval_ = _RWSTD_INT_MIN;
500 lastopt->
maxval_ = _RWSTD_INT_MAX;
503 lastopt->
sopt_ = *next++;
506 const char*
end = strpbrk (++next,
"|@:=*!#~ ");
508 end = next + strlen (next);
513 const size_t optlen = size_t (
end - next) + (
'=' == *
end);
517 if (optlen < sizeof lastopt->loptbuf_)
520 lopt = (
char*)malloc (optlen + 1);
523 lastopt->
lopt_ = lopt;
526 memcpy (lopt, next, optlen);
527 lopt [optlen] =
'\0';
537 int arg_is_callback =
true;
557 arg_is_callback =
false;
560 else if (
'~' == *next) {
577 arg_is_callback =
false;
579 else if (
':' == *next ||
'=' == *next) {
582 lastopt->
arg_ =
true;
608 else if (
'!' == *next) {
609 lastopt->
inv_ =
true;
613 if (arg_is_callback) {
628 if (
'-' != lastopt->
sopt_
630 && 0 == lastopt->
pcntr_) {
633 const char*
const lopt = lastopt->
lopt_
637 fprintf (
stderr,
"null handler for option -%s\n", lopt);
639 fprintf (
stderr,
"null handler for option -%c\n",
646 return int (_rw_ncmdopts - _rw_ndefopts);
665_rw_getarg (
cmdopts_t *optspec,
const char *opt,
const char *
arg)
672 const long optval = strtol (
arg, &end, 0);
676 if (end &&
'\0' != *end) {
677 fprintf (
stderr,
"expected numeric argument: %s\n", opt);
682#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
684 else if (optval < _RWSTD_INT_MIN || _RWSTD_INT_MAX < optval) {
685 fprintf (
stderr,
"numeric argument %ld out of range"
686 " [%d, %d]: %s\n", optval,
687 _RWSTD_INT_MIN, _RWSTD_INT_MAX, opt);
694 else if ( optval <
long (optspec->
minval_)
695 || long (optspec->
maxval_) < optval) {
698 fprintf (
stderr,
"numeric argument %ld out of range"
699 " [%d, %d]: %s\n", optval,
704 else if (optspec->
pcntr_) {
707 *optspec->
pcntr_ = int (optval);
716_rw_runopt_recursive =
false;
721_rw_runopt (
cmdopts_t *optspec,
int argc,
char *argv[])
733 if (optspec->
count_ && _rw_runopt_recursive)
743 char* equals = strchr (argv [0],
'=');
748 if (!optspec->
inv_) {
752 const char*
const lopt = optspec->
lopt_ ?
756 && ( _RWSTD_INT_MIN < optspec->minval_
757 || optspec->
maxval_ < _RWSTD_INT_MAX)) {
760 if (strchr (lopt,
'=')) {
765 status = _rw_getarg (optspec, argv [0], equals + 1);
768 else if (1 < argc &&
'-' != argv [1][0]) {
770 const char *
arg = argv [1];
774 if (
'\\' ==
arg [0] &&
'-' ==
arg [1])
777 status = _rw_getarg (optspec, argv [0],
arg);
782 status = optspec->
callback_ (argc, argv);
790 status = _rw_getarg (optspec, argv [0], equals + 1);
801 if (_rw_runopt_recursive)
815_rw_match_tristate (
const cmdopts_t *opt,
const char *optname)
820 static const char*
const prefix[] = {
821 "+enable",
"+use",
"+with",
822 "-disable",
"-no",
"-without",
828 if (
'-' == optname [0] &&
'-' != optname [2]) {
832 const size_t optlen = strlen (optname);
835 const char*
const pfx =
prefix [
i] + 1;
836 const size_t pfxlen = strlen (pfx);
838 if (pfxlen < optlen && 0 == memcmp (optname, pfx, pfxlen)) {
839 const char*
const name = optname + pfxlen;
842 const char*
const lopt =
845 if (0 == strcmp (lopt,
name)) {
846 tristate =
'+' ==
prefix [
i][0] ? 1 : -1;
862_rw_set_bits (
int val)
866 for (
unsigned uval =
val; uval; uval >>= 1) {
880 int ignenv = _rw_runopt_recursive;
895 for (
int i = 0;
i < argc && argv [
i] && 0 == status; ++
i) {
897 if (0 == strcmp (
"--ignore-environment", argv [
i])) {
903 if (0 == strcmp (
"--", argv [
i])) {
910 const char*
const optname = argv [
i] + 1;
913 const int opt_argc = argc -
i;
914 char**
const opt_argv = argv +
i;
917 const char*
const eq = strchr (optname,
'=');
920 const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname);
926 for (
size_t j = 0;
j != _rw_ncmdopts; ++
j) {
931 if (
'-' == opt->
sopt_)
934 if (
'-' == argv [
i][0]) {
936 const size_t cmplen =
937 eq && opt->
pcntr_ ? optlen - 1 : optlen;
941 const char*
const lopt = opt->
lopt_ ?
948 const int tristate = _rw_match_tristate (opt, optname);
953 const int word_bits = _RWSTD_CHAR_BIT *
sizeof (int);
958 const int word_inx = opt->
minval_ / word_bits;
959 const int bit_inx = opt->
minval_ % word_bits;
961 const int bit_val = _RWSTD_INT_MAX == opt->
maxval_
966 _rw_set_bits (bit_val) << bit_inx;
969 opt->
pcntr_ [word_inx] &= ~bit_mask;
972 opt->
pcntr_ [word_inx] = ~(bit_val << bit_inx);
974 opt->
pcntr_ [word_inx] |= bit_val << bit_inx;
978 opt->
pcntr_ [word_inx] = tristate < 0 ? -1 : +1;
994 if ( ( cmplen == strlen (lopt)
995 && 0 == memcmp (optname, lopt, cmplen))
997 && optname [0] == opt->
sopt_
998 && (1 == optlen || opt->
arg_))) {
1004 status = _rw_runopt (opt, opt_argc, opt_argv);
1022 if (!
found &&
'-' == argv [
i][0]) {
1026 if (0 != not_found_opt) {
1031 status = not_found_opt->
callback_ (opt_argc, opt_argv);
1041 fprintf (
stderr,
"unknown option: %s\n", argv [
i]);
1052 const char*
const envar = getenv (
"RWSTD_TESTOPTS");
1054 _rw_runopt_recursive =
true;
1056 _rw_runopt_recursive =
false;
1063 for (
size_t j = 0;
j != _rw_ncmdopts; ++
j) {
1068 if (opt->
inv_ && 0 == opt->
count_ && 0 == status) {
1097 size_t len = strlen (
str);
1100 pbuf = (
char*)malloc (
len + 1);
1106 memcpy (pbuf,
str,
len + 1);
1108 char *tmp_argv_buf [32] = { 0 };
1109 char **argv = tmp_argv_buf;
1112 size_t argv_size =
sizeof tmp_argv_buf /
sizeof *tmp_argv_buf;
1117 for (
char *
s = pbuf; *
s; ++
s) {
1119 in_quotes = !in_quotes;
1127 if (argc == 0 || isspace (*
s)) {
1132 if (argc > 0 || isspace (*
s))
1133 while (isspace (*++
s));
1136 if (argc == argv_size) {
1138 char **
tmp = (
char**)malloc (
sizeof *
tmp * argv_size * 2);
1140 if (argv != tmp_argv_buf)
1146 memcpy (
tmp, argv,
sizeof *
tmp * argv_size);
1147 memset (
tmp + argv_size, 0,
sizeof *
tmp * argv_size);
1150 if (argv != tmp_argv_buf)
1165 const int status =
rw_runopts (
int (argc), argv);
1168 if (argv != tmp_argv_buf)
IMPORT_C SInt32 printf(const Char *,...)
IMPORT_C SInt32 atexit(Void(*function)(Void))
#define va_start(va, name)
#define _RWSTD_CONST_CAST(T, x)
optcallback_t * callback_
long prefix(const entry_type &entry)
#define RW_VA_LIST_PTR_ARG_TO_VA_LIST(va)
_TEST_EXPORT int rw_vsetopts(const char *opts, va_list va)
_TEST_EXPORT int rw_setopts(const char *opts,...)
#define RW_VA_LIST_ARG_TO_PTR(va)
int() optcallback_t(int, char *[])
#define RW_VA_LIST_ARG_PTR
_TEST_EXPORT int rw_runopts(int argc, char *argv[])