os/ossrv/stdcpp/tsrc/Stdcpp_test/stdcxx/testengine/src/cmdopt.cpp
changeset 0 bde4ae8d615e
     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 +}