1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/ossrv/stdcpp/tsrc/Stdcpp_test/stdcxx/testengine/src/cmdopt.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,771 @@
1.4 +/***************************************************************************
1.5 + *
1.6 + * $Id: cmdopt.cpp 348342 2005-11-23 02:03:26Z sebor $
1.7 + *
1.8 + ************************************************************************
1.9 + *
1.10 + * Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave
1.11 + * Software division. Licensed under the Apache License, Version 2.0 (the
1.12 + * "License"); you may not use this file except in compliance with the
1.13 + * License. You may obtain a copy of the License at
1.14 + * http://www.apache.org/licenses/LICENSE-2.0. Unless required by
1.15 + * applicable law or agreed to in writing, software distributed under
1.16 + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
1.17 + * CONDITIONS OF ANY KIND, either express or implied. See the License
1.18 + * for the specific language governing permissions and limitations under
1.19 + * the License.
1.20 + *
1.21 + **************************************************************************/
1.22 +
1.23 +// expand _TEST_EXPORT macros
1.24 +#define _RWSTD_TEST_SRC
1.25 +
1.26 +#include <cmdopt.h>
1.27 +
1.28 +#include <assert.h> // for assert
1.29 +#include <errno.h> // for errno
1.30 +#include <stdarg.h> // for va_arg, ...
1.31 +#include <stdio.h> // for fprintf
1.32 +#include <stdlib.h> // for atexit, free, malloc
1.33 +#include <string.h> // for memcpy, strcpy, strcmp, ...
1.34 +
1.35 +#ifdef __ARMCC__
1.36 +#pragma diag_suppress 61
1.37 +#pragma diag_suppress 63
1.38 +#endif
1.39 +
1.40 +#ifndef EINVAL
1.41 +# define EINVAL 22 /* e.g., HP-UX, Linux, Solaris */
1.42 +#endif // EINVAL
1.43 +
1.44 +/**************************************************************************/
1.45 +
1.46 +typedef int (optcallback_t)(int, char*[]);
1.47 +
1.48 +struct cmdopts_t
1.49 +{
1.50 + char loptbuf_ [32]; // buffer for long option name
1.51 + optcallback_t *callback_; // function to call to process option
1.52 +
1.53 + // counter to increment for each occurrence of an option
1.54 + // or to set to the numeric argument (when specified)
1.55 + int *pcntr_;
1.56 +
1.57 + char *lopt_; // long option name
1.58 + char sopt_; // short option name
1.59 +
1.60 + size_t maxcalls_; // how many times option can be invoked
1.61 + size_t ncalls_; // how many times it has been invoked
1.62 +
1.63 + unsigned arg_ : 1; // option takes an argument?
1.64 + unsigned inv_ : 1; // callback invocation inverted
1.65 + unsigned envseen_ : 1; // environment option already processed
1.66 +};
1.67 +
1.68 +
1.69 +// total number of registered options
1.70 +static size_t ncmdopts;
1.71 +
1.72 +// number of default (always defined) options
1.73 +static size_t ndefopts;
1.74 +static cmdopts_t cmdoptbuf [32];
1.75 +static cmdopts_t *cmdopts = cmdoptbuf;
1.76 +static size_t optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
1.77 +
1.78 +/**************************************************************************/
1.79 +
1.80 +static int
1.81 +rw_print_help (int argc, char *argv[])
1.82 +{
1.83 + if (1 == argc && argv && 0 == argv [0]) {
1.84 + static const char helpstr[] = {
1.85 + "Without an argument, prints this help to stdout and exits with\n"
1.86 + "a status of 0 without further processing.\n"
1.87 + "With the optional argument prints help on the option with that\n"
1.88 + "name, if one exists, and exits with a status of 0. If an option\n"
1.89 + "with the specified name does not exist, prints an error message\n"
1.90 + "to stderr and exits with a status of 1.\n"
1.91 + "The leading underscores in the name of an option are optional.\n"
1.92 + };
1.93 +
1.94 + argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
1.95 +
1.96 + return 0;
1.97 + }
1.98 +
1.99 + // the option name to get help on, if any
1.100 + const char* opthelp = 1 < argc ? argv [1] : 0;
1.101 +
1.102 + // remove the optional one or two leading underscores
1.103 + if (opthelp && '-' == opthelp [0] && opthelp [1])
1.104 + opthelp += 1 + ('-' == opthelp [1]);
1.105 +
1.106 + if (0 == opthelp)
1.107 + printf ("OPTIONS\n");
1.108 +
1.109 + // set to a non-zero when the specified option is found
1.110 + int option_found = 0;
1.111 +
1.112 + for (size_t i = 0; i != ncmdopts; ++i) {
1.113 +
1.114 + // get a pointer to the name of the long option, if any
1.115 + const char* const lopt =
1.116 + cmdopts [i].lopt_ ? cmdopts [i].lopt_ : cmdopts [i].loptbuf_;
1.117 +
1.118 + if (opthelp && *opthelp) {
1.119 +
1.120 + if ( cmdopts [i].sopt_ == opthelp [0] && '\0' == opthelp [1]
1.121 + || *lopt && 0 == strcmp (lopt + 1, opthelp)) {
1.122 +
1.123 + // remember that we found the option whose (short
1.124 + // or long) name we're to give help on; after printing
1.125 + // the help text on the option keep looping in case
1.126 + // there is another option and callback with the same
1.127 + // name (unlikely but possible)
1.128 + option_found = 1;
1.129 + }
1.130 + else {
1.131 + // the option doesn't match, continue searching
1.132 + continue;
1.133 + }
1.134 + }
1.135 +
1.136 + printf (" ");
1.137 +
1.138 + if (cmdopts [i].sopt_) {
1.139 + printf ("-%c", cmdopts [i].sopt_);
1.140 +
1.141 + if (lopt)
1.142 + printf (" | ");
1.143 + }
1.144 +
1.145 + const char *pfx = "";
1.146 + const char *sfx = pfx;
1.147 +
1.148 + if (lopt) {
1.149 + printf ("-%s", lopt);
1.150 + if ( cmdopts [i].arg_
1.151 + && '=' != lopt [strlen (lopt) - 1]) {
1.152 + pfx = " [ ";
1.153 + sfx = " ]";
1.154 + }
1.155 + }
1.156 +
1.157 + printf ("%s%s%s", pfx, cmdopts [i].arg_ ? "<arg>" : "", sfx);
1.158 +
1.159 + if (_RWSTD_SIZE_MAX == cmdopts [i].maxcalls_)
1.160 + printf (" (each occurrence evaluated)\n");
1.161 + else if (1 < cmdopts [i].maxcalls_)
1.162 + printf (" (at most %u occurrences evaluated)\n",
1.163 + unsigned (cmdopts [i].maxcalls_));
1.164 + else
1.165 + printf (" (only the first occurrence evaluated)\n");
1.166 +
1.167 + // invoke callback with the "--help" option
1.168 + if (cmdopts [i].callback_) {
1.169 +
1.170 + char* help [2] = { 0, 0 };
1.171 +
1.172 + cmdopts [i].callback_ (1, help);
1.173 +
1.174 + for (const char *line = help [0]; line; ) {
1.175 +
1.176 + const char* const nl = strchr (line, '\n');
1.177 + const int len = nl ? int (nl - line) : int (strlen (line));
1.178 +
1.179 + printf (" %.*s\n", len, line);
1.180 +
1.181 + line = nl;
1.182 + if (nl)
1.183 + ++line;
1.184 + }
1.185 + }
1.186 + }
1.187 +
1.188 + if (opthelp && !option_found) {
1.189 + fprintf (stderr, "Unknown option \"%s\".\n", opthelp);
1.190 + exit (1);
1.191 + }
1.192 +
1.193 + exit (0);
1.194 +
1.195 + return 0;
1.196 +}
1.197 +
1.198 +/**************************************************************************/
1.199 +
1.200 +static int
1.201 +rw_set_ignenv (int argc, char *argv[])
1.202 +{
1.203 + if (1 == argc && argv && 0 == argv [0]) {
1.204 + static const char helpstr[] = {
1.205 + "Prevents options specified in the RWSTD_TESTOPTS environment\n"
1.206 + "variable from taking effect.\n"
1.207 + "Unless this option is specified, the RWSTD_TESTOPTS environment\n"
1.208 + "variable is processed as if its value were specified on the \n"
1.209 + "command line.\n"
1.210 + "For example, setting the value of the variable to the string\n"
1.211 + "\"--verbose --no-wchar\" and invoking this program with no\n"
1.212 + "command line arguments will have the same effect as invoking\n"
1.213 + "it with the two arguments on the command line.\n"
1.214 + };
1.215 +
1.216 + argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
1.217 +
1.218 + return 0;
1.219 + }
1.220 +
1.221 + return 0;
1.222 +}
1.223 +
1.224 +extern "C" {
1.225 +
1.226 +static void
1.227 +rw_clear_opts ()
1.228 +{
1.229 + // reset all options, deallocating dynamically allocated storage
1.230 +
1.231 + for (size_t i = 0; i != ncmdopts; ++i) {
1.232 +
1.233 + // free any storage allocated for the option name
1.234 + free (cmdopts [i].lopt_);
1.235 + }
1.236 +
1.237 + if (cmdopts != cmdoptbuf) {
1.238 + // free the storage allocated for all the options
1.239 + free (cmdopts);
1.240 + }
1.241 +
1.242 + // reset the options pointer to point at the statically
1.243 + // allocated buffer and the count back to 0
1.244 + ncmdopts = 0;
1.245 + cmdopts = cmdoptbuf;
1.246 + optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
1.247 +}
1.248 +
1.249 +}
1.250 +
1.251 +static void
1.252 +rw_set_myopts ()
1.253 +{
1.254 + static int cleanup_handler_registered;
1.255 +
1.256 + if (0 == cleanup_handler_registered) {
1.257 + atexit (rw_clear_opts);
1.258 + cleanup_handler_registered = 1;
1.259 + }
1.260 +
1.261 + if (0 != ncmdopts)
1.262 + return;
1.263 +
1.264 + static int recursive;
1.265 +
1.266 + if (recursive)
1.267 + return;
1.268 +
1.269 + ++recursive;
1.270 +
1.271 + rw_setopts ("|-help: " // argument optional
1.272 + "|-ignenv ",
1.273 + rw_print_help,
1.274 + rw_set_ignenv);
1.275 +
1.276 + ndefopts = ncmdopts;
1.277 +
1.278 + recursive = 0;
1.279 +}
1.280 +
1.281 +/**************************************************************************/
1.282 +
1.283 +//////////////////////////////////////////////////////////////////////
1.284 +// syntax of the option description string:
1.285 +//
1.286 +// opts ::= opt [ ':' | '=' | '#' ] [ @N | @* | '!' ] [ opts ]
1.287 +// opt ::= <sopt> [ '|' <lopt>]
1.288 +// ::= '|' <lopt>
1.289 +// sopt ::= char
1.290 +// lopt ::= char char*
1.291 +// char ::= A-Z a-z _ 0-9
1.292 +
1.293 +_TEST_EXPORT int
1.294 +rw_vsetopts (const char *opts, va_list va)
1.295 +{
1.296 + if (0 == opts) {
1.297 +
1.298 + rw_clear_opts ();
1.299 + return 0;
1.300 + }
1.301 +
1.302 + rw_set_myopts ();
1.303 +
1.304 + const char *next = opts;
1.305 +
1.306 + for ( ; ; ++ncmdopts) {
1.307 +
1.308 + while (' ' == *next)
1.309 + ++next;
1.310 +
1.311 + if ('\0' == *next) {
1.312 + break;
1.313 + }
1.314 +
1.315 + if (ncmdopts == optbufsize) {
1.316 +
1.317 + const size_t newbufsize = 2 * ncmdopts + 1;
1.318 +
1.319 + cmdopts_t* const newopts =
1.320 + (cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t));
1.321 +
1.322 + if (0 == newopts) {
1.323 + fprintf (stderr, "%s%d: failed to allocate memory\n",
1.324 + __FILE__, __LINE__);
1.325 + abort ();
1.326 + }
1.327 +
1.328 + memcpy (newopts, cmdopts, ncmdopts * sizeof (cmdopts_t));
1.329 +
1.330 + if (cmdopts != cmdoptbuf)
1.331 + free (cmdopts);
1.332 +
1.333 + cmdopts = newopts;
1.334 + optbufsize = newbufsize;
1.335 + }
1.336 +
1.337 + // clear the next option info
1.338 + memset (cmdopts + ncmdopts, 0, sizeof *cmdopts);
1.339 +
1.340 + if ('|' != *next)
1.341 + cmdopts [ncmdopts].sopt_ = *next++;
1.342 +
1.343 + if ('|' == *next) {
1.344 + const char* end = strpbrk (++next, "|@:=*!# ");
1.345 + if (0 == end)
1.346 + end = next + strlen (next);
1.347 +
1.348 + // copy the option name up to but not including the delimiter
1.349 + // (except when the delimiter is the equals sign ('='), which
1.350 + // becomes the last character of the option name
1.351 + const size_t optlen = size_t (end - next) + ('=' == *end);
1.352 +
1.353 + char *lopt = 0;
1.354 +
1.355 + if (optlen < sizeof cmdopts [ncmdopts].loptbuf_)
1.356 + lopt = cmdopts [ncmdopts].loptbuf_;
1.357 + else {
1.358 + lopt = (char*)malloc (optlen + 1);
1.359 + cmdopts [ncmdopts].lopt_ = lopt;
1.360 + }
1.361 +
1.362 + memcpy (lopt, next, optlen);
1.363 + lopt [optlen] = '\0';
1.364 +
1.365 + next = end;
1.366 + }
1.367 +
1.368 + // only the first occurrence of each command line option
1.369 + // causes an invocation of the callback, all subsequent
1.370 + // ones will be ignored by default
1.371 + cmdopts [ncmdopts].maxcalls_ = 1;
1.372 +
1.373 + int arg_is_callback = true;
1.374 +
1.375 + if ('#' == *next) {
1.376 + // insead of a pointer to a callback, the argument
1.377 + // is a pointer to an int counter that is to be
1.378 + // incremented for each occurrence of the option
1.379 + // during processing; when the option is immediately
1.380 + // followed by the equals sign ('=') and a numeric
1.381 + // argument the value of the argument will be stored
1.382 + arg_is_callback = false;
1.383 + ++next;
1.384 +
1.385 + // an unlimited number of occurrences of the option
1.386 + // are allowed and will be counted
1.387 + cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
1.388 + }
1.389 + else if (':' == *next || '=' == *next) {
1.390 + // ':' : argument optional
1.391 + // '=' : argument required
1.392 + cmdopts [ncmdopts].arg_ = true;
1.393 + ++next;
1.394 + }
1.395 +
1.396 + if ('@' == *next) {
1.397 +
1.398 + ++next;
1.399 +
1.400 + // at most how many occurrences of an option can be processed?
1.401 + if ('*' == *next) {
1.402 + // unlimited
1.403 + cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
1.404 + ++next;
1.405 + }
1.406 + else {
1.407 + // at most this many
1.408 + char *end;
1.409 + cmdopts [ncmdopts].maxcalls_ = strtoul (next, &end, 10);
1.410 + next = end;
1.411 + }
1.412 + }
1.413 + else if ('!' == *next) {
1.414 + cmdopts [ncmdopts].inv_ = true;
1.415 + ++next;
1.416 + }
1.417 +
1.418 + if (arg_is_callback) {
1.419 + // retrieve the callback and verify it's not null
1.420 + // (null callback is permitted in the special case when
1.421 + // the short option is '-', i.e., when setting up or
1.422 + // resetting an "unknown option" handler)
1.423 + cmdopts [ncmdopts].callback_ = va_arg (va, optcallback_t*);
1.424 + }
1.425 + else {
1.426 + // retrieve the address of the int counter where to keep
1.427 + // track of the number of occurrences of the option, or
1.428 + // where to store the value of the numeric argument of
1.429 + // the option
1.430 + cmdopts [ncmdopts].pcntr_ = va_arg (va, int*);
1.431 + }
1.432 +
1.433 + if ( '-' != cmdopts [ncmdopts].sopt_
1.434 + && 0 == cmdopts [ncmdopts].callback_
1.435 + && 0 == cmdopts [ncmdopts].pcntr_) {
1.436 +
1.437 + // get a pointer to the long option name
1.438 + const char* const lopt = cmdopts [ncmdopts].lopt_
1.439 + ? cmdopts [ncmdopts].lopt_ : cmdopts [ncmdopts].loptbuf_;
1.440 +
1.441 + if (*lopt)
1.442 + fprintf (stderr, "null handler for option -%s\n", lopt);
1.443 + else
1.444 + fprintf (stderr, "null handler for option -%c\n",
1.445 + cmdopts [ncmdopts].sopt_);
1.446 +
1.447 + abort ();
1.448 + }
1.449 + }
1.450 +
1.451 + return int (ncmdopts - ndefopts);
1.452 +}
1.453 +
1.454 +/**************************************************************************/
1.455 +
1.456 +_TEST_EXPORT int
1.457 +rw_setopts (const char *opts, ...)
1.458 +{
1.459 + va_list va;
1.460 + va_start (va, opts);
1.461 + const int result = rw_vsetopts (opts, va);
1.462 + va_end (va);
1.463 + return result;
1.464 +}
1.465 +
1.466 +/**************************************************************************/
1.467 +
1.468 +_TEST_EXPORT int
1.469 +rw_runopts (int argc, char *argv[])
1.470 +{
1.471 + rw_set_myopts ();
1.472 +
1.473 + static int recursive = false;
1.474 +
1.475 + // ignore options set in the environment?
1.476 + int ignenv = recursive;
1.477 +
1.478 + // return status
1.479 + int status = 0;
1.480 +
1.481 + // number of options processed
1.482 + int nopts = 0;
1.483 +
1.484 + // index of registered option whose callback should be invoked
1.485 + // for command line options that do not match any other
1.486 + size_t not_found_inx = _RWSTD_SIZE_MAX;
1.487 +
1.488 + // iterate over the command line arguments until a callback
1.489 + // returns a non-zero value or until all options have been
1.490 + // successfully processed
1.491 + for (int i = 0; i < argc && argv [i] && 0 == status; ++i) {
1.492 +
1.493 + if (0 == strcmp ("--ignore-environment", argv [i])) {
1.494 + // ignore options set in the environment
1.495 + ignenv = true;
1.496 + continue;
1.497 + }
1.498 +
1.499 + if (0 == strcmp ("--", argv [i])) {
1.500 + // "--" terminates options, everything
1.501 + // after it is treated as an argument
1.502 + break;
1.503 + }
1.504 +
1.505 + // the name of the option without the leading dash
1.506 + const char* const optname = argv [i] + 1;
1.507 +
1.508 + // look for the first equals sign
1.509 + const char* const eq = strchr (optname, '=');
1.510 +
1.511 + // compute the length of the option including the equals sign (if any)
1.512 + const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname);
1.513 +
1.514 + int found = false;
1.515 +
1.516 + // look up each command line option (i.e., a string that starts
1.517 + // with a dash ('-')) and invoke the callback associated with it
1.518 + for (size_t j = 0; j != ncmdopts; ++j) {
1.519 +
1.520 + if ('-' == cmdopts [j].sopt_)
1.521 + not_found_inx = j;
1.522 +
1.523 + if ('-' == argv [i][0]) {
1.524 +
1.525 + const size_t cmplen =
1.526 + eq && cmdopts [j].pcntr_ ? optlen - 1 : optlen;
1.527 +
1.528 + // get a pointer to the (possibly empty) name
1.529 + // of the long option
1.530 + const char* const lopt = cmdopts [j].lopt_ ?
1.531 + cmdopts [j].lopt_ : cmdopts [j].loptbuf_;
1.532 +
1.533 + // try to match the long option first, and only if it
1.534 + // doesn't match try the short single-character option
1.535 + if ( cmplen == strlen (lopt)
1.536 + && 0 == memcmp (optname, lopt, cmplen)
1.537 + || cmdopts [j].sopt_
1.538 + && optname [0] == cmdopts [j].sopt_
1.539 + && (1 == optlen || cmdopts [j].arg_)) {
1.540 +
1.541 + // matching option has been found
1.542 + found = true;
1.543 +
1.544 + // ignore the option if invoked recursively (by processing
1.545 + // options set in the environment) and the option has
1.546 + // already been seen (this prevents duplicate processing
1.547 + // of options that are set both on the command line and
1.548 + // in the environment and allows option with an argument
1.549 + // set on the command line to override those set in the
1.550 + // environment)
1.551 +
1.552 + if (cmdopts [j].ncalls_ && recursive)
1.553 + continue;
1.554 +
1.555 + // if the option has been evaluated the maximum number
1.556 + // of times, avoid evaluating it and continue processing
1.557 + if (cmdopts [j].maxcalls_ <= cmdopts [j].ncalls_)
1.558 + continue;
1.559 +
1.560 + if (cmdopts [j].callback_) {
1.561 + if (!cmdopts [j].inv_) {
1.562 + // when the command line argument matched
1.563 + // the option, invoke the callback function
1.564 + status = cmdopts [j].callback_ (argc - i, argv + i);
1.565 + }
1.566 + }
1.567 + else if (eq) {
1.568 + assert (0 != cmdopts [j].pcntr_);
1.569 +
1.570 + // obtain the numeric argument
1.571 + char *end = 0;
1.572 + const long optval = strtol (eq + 1, &end, 0);
1.573 +
1.574 + if (end && '\0' != *end) {
1.575 + fprintf (stderr, "expected numeric argument: %s\n",
1.576 + optname);
1.577 + ignenv = true;
1.578 + errno = EINVAL;
1.579 + status = 1;
1.580 + }
1.581 +
1.582 +#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
1.583 +
1.584 + else if ( optval < _RWSTD_INT_MIN
1.585 + || _RWSTD_INT_MAX < optval) {
1.586 + fprintf (stderr, "numeric argument %ld out of range"
1.587 + " [%d, %d]: %s\n", optval,
1.588 + _RWSTD_INT_MIN, _RWSTD_INT_MAX, optname);
1.589 + ignenv = true;
1.590 + errno = EINVAL;
1.591 + status = 1;
1.592 + }
1.593 +
1.594 +#endif // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
1.595 +
1.596 + else {
1.597 + *cmdopts [j].pcntr_ = optval;
1.598 + }
1.599 + }
1.600 + else {
1.601 + assert (0 != cmdopts [j].pcntr_);
1.602 + ++*cmdopts [j].pcntr_;
1.603 + }
1.604 +
1.605 + ++cmdopts [j].ncalls_;
1.606 +
1.607 + if (recursive)
1.608 + cmdopts [j].envseen_ = true;
1.609 +
1.610 + ++nopts;
1.611 +
1.612 + if (status) {
1.613 + // when the status returned from the last callback
1.614 + // is non-0 stop further processing (including
1.615 + // any options set in the environment) and return
1.616 + // status to the caller
1.617 + ignenv = true;
1.618 + break;
1.619 + }
1.620 + }
1.621 + }
1.622 + }
1.623 +
1.624 + if (!found && '-' == argv [i][0]) {
1.625 +
1.626 + // invoke the appropriate error handler for an option
1.627 + // that was not found
1.628 + if (_RWSTD_SIZE_MAX != not_found_inx) {
1.629 +
1.630 + // invoke the error handler set up through rw_setopts()
1.631 + // and let the handler decide whether to go on processing
1.632 + // other options or whether to abort
1.633 + status = cmdopts [not_found_inx].callback_ (argc - i, argv + i);
1.634 + if (status) {
1.635 + // no further processing done
1.636 + ignenv = true;
1.637 + break;
1.638 + }
1.639 + }
1.640 + else {
1.641 + // print an error message to stderr when no error
1.642 + // handler has been set up
1.643 + fprintf (stderr, "unknown option: %s\n", argv [i]);
1.644 + ignenv = true;
1.645 + errno = EINVAL;
1.646 + status = 1;
1.647 + break;
1.648 + }
1.649 + }
1.650 + }
1.651 +
1.652 + if (!ignenv) {
1.653 + // process options from the environment
1.654 + const char* const envar = getenv ("RWSTD_TESTOPTS");
1.655 + if (envar) {
1.656 + recursive = true;
1.657 + rw_runopts (envar);
1.658 + recursive = false;
1.659 + }
1.660 + }
1.661 +
1.662 + // invoke any inverted callbacks or bump their user-specified counters,
1.663 + // and reset internal counters indicating if/how many times each option
1.664 + // has been processed
1.665 + for (size_t j = 0; j != ncmdopts; ++j) {
1.666 +
1.667 + if (cmdopts [j].inv_ && 0 == cmdopts [j].ncalls_ && 0 == status) {
1.668 +
1.669 + if (cmdopts [j].callback_)
1.670 + status = cmdopts [j].callback_ (0, 0);
1.671 + else {
1.672 + assert (0 != cmdopts [j].pcntr_);
1.673 + ++*cmdopts [j].pcntr_;
1.674 + }
1.675 + }
1.676 +
1.677 + cmdopts [j].ncalls_ = 0;
1.678 + cmdopts [j].envseen_ = false;
1.679 + }
1.680 +
1.681 + return status;
1.682 +}
1.683 +
1.684 +/**************************************************************************/
1.685 +
1.686 +_TEST_EXPORT int
1.687 +rw_runopts (const char *str)
1.688 +{
1.689 + assert (0 != str);
1.690 +
1.691 + rw_set_myopts ();
1.692 +
1.693 + char buf [80]; // fixed size buffer to copy `str' into
1.694 + char *pbuf = buf; // a modifiable copy of `str'
1.695 +
1.696 + size_t len = strlen (str);
1.697 + if (len >= sizeof buf) {
1.698 + // allocate if necessary
1.699 + pbuf = (char*)malloc (len + 1);
1.700 + if (!pbuf)
1.701 + return -1;
1.702 + }
1.703 +
1.704 + // copy `str' to modifiable buffer
1.705 + memcpy (pbuf, str, len + 1);
1.706 +
1.707 + char *tmp_argv_buf [32] = { 0 }; // fixed size argv buffer
1.708 + char **argv = tmp_argv_buf; // array of arguments
1.709 +
1.710 + // initial size of argument array (will grow as necessary)
1.711 + size_t argv_size = sizeof tmp_argv_buf / sizeof *tmp_argv_buf;
1.712 + size_t argc = 0; // number of arguments in array
1.713 +
1.714 + int in_quotes = 0; // quoted argument being processed
1.715 +
1.716 + for (char *s = pbuf; *s; ++s) {
1.717 + if ('"' == *s) {
1.718 + in_quotes = !in_quotes;
1.719 + continue;
1.720 + }
1.721 +
1.722 + if (in_quotes)
1.723 + continue;
1.724 +
1.725 + // split up unquoted space-separated arguments
1.726 + if (argc == 0 || ' ' == *s) {
1.727 + if (argc > 0)
1.728 + *s = 0;
1.729 +
1.730 + // skip over leading spaces
1.731 + if (argc > 0 || ' ' == *s)
1.732 + while (' ' == *++s);
1.733 +
1.734 + if (*s) {
1.735 + if (argc == argv_size) {
1.736 + // grow `argv' as necessary
1.737 + char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2);
1.738 + if (!tmp) {
1.739 + if (argv != tmp_argv_buf)
1.740 + free (argv);
1.741 + return -1;
1.742 + }
1.743 +
1.744 + // copy existing elementes and zero out any new entries
1.745 + memcpy (tmp, argv, sizeof *tmp * argv_size);
1.746 + memset (tmp + argv_size, 0, sizeof *tmp * argv_size);
1.747 +
1.748 + // free existing buffer if necessary
1.749 + if (argv != tmp_argv_buf)
1.750 + free (argv);
1.751 +
1.752 + // reassign buffer and increase size
1.753 + argv = tmp;
1.754 + argv_size *= 2;
1.755 + }
1.756 +
1.757 + // add argument to array
1.758 + argv [argc++] = s;
1.759 + }
1.760 + }
1.761 + }
1.762 +
1.763 + // process `argc' options pointed to by `argv'
1.764 + const int status = rw_runopts (int (argc), argv);
1.765 +
1.766 + // free buffers if necessary
1.767 + if (argv != tmp_argv_buf)
1.768 + free (argv);
1.769 +
1.770 + if (pbuf != buf)
1.771 + free (pbuf);
1.772 +
1.773 + return status;
1.774 +}