diff -r 000000000000 -r e4d67989cc36 stdcpp/tsrc/Stdcpp_test/stdcxx/testengine/src/cmdopt.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stdcpp/tsrc/Stdcpp_test/stdcxx/testengine/src/cmdopt.cpp Tue Feb 02 02:01:42 2010 +0200 @@ -0,0 +1,766 @@ +/*************************************************************************** + * + * $Id: cmdopt.cpp 348342 2005-11-23 02:03:26Z sebor $ + * + ************************************************************************ + * + * Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave + * Software division. Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. Unless required by + * applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License + * for the specific language governing permissions and limitations under + * the License. + * + **************************************************************************/ + +// expand _TEST_EXPORT macros +#define _RWSTD_TEST_SRC + +#include + +#include // for assert +#include // for errno +#include // for va_arg, ... +#include // for fprintf +#include // for atexit, free, malloc +#include // for memcpy, strcpy, strcmp, ... + +#ifndef EINVAL +# define EINVAL 22 /* e.g., HP-UX, Linux, Solaris */ +#endif // EINVAL + +/**************************************************************************/ + +typedef int (optcallback_t)(int, char*[]); + +struct cmdopts_t +{ + char loptbuf_ [32]; // buffer for long option name + optcallback_t *callback_; // function to call to process option + + // counter to increment for each occurrence of an option + // or to set to the numeric argument (when specified) + int *pcntr_; + + char *lopt_; // long option name + char sopt_; // short option name + + size_t maxcalls_; // how many times option can be invoked + size_t ncalls_; // how many times it has been invoked + + unsigned arg_ : 1; // option takes an argument? + unsigned inv_ : 1; // callback invocation inverted + unsigned envseen_ : 1; // environment option already processed +}; + + +// total number of registered options +static size_t ncmdopts; + +// number of default (always defined) options +static size_t ndefopts; +static cmdopts_t cmdoptbuf [32]; +static cmdopts_t *cmdopts = cmdoptbuf; +static size_t optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf; + +/**************************************************************************/ + +static int +rw_print_help (int argc, char *argv[]) +{ + if (1 == argc && argv && 0 == argv [0]) { + static const char helpstr[] = { + "Without an argument, prints this help to stdout and exits with\n" + "a status of 0 without further processing.\n" + "With the optional argument prints help on the option with that\n" + "name, if one exists, and exits with a status of 0. If an option\n" + "with the specified name does not exist, prints an error message\n" + "to stderr and exits with a status of 1.\n" + "The leading underscores in the name of an option are optional.\n" + }; + + argv [0] = _RWSTD_CONST_CAST (char*, helpstr); + + return 0; + } + + // the option name to get help on, if any + const char* opthelp = 1 < argc ? argv [1] : 0; + + // remove the optional one or two leading underscores + if (opthelp && '-' == opthelp [0] && opthelp [1]) + opthelp += 1 + ('-' == opthelp [1]); + + if (0 == opthelp) + printf ("OPTIONS\n"); + + // set to a non-zero when the specified option is found + int option_found = 0; + + for (size_t i = 0; i != ncmdopts; ++i) { + + // get a pointer to the name of the long option, if any + const char* const lopt = + cmdopts [i].lopt_ ? cmdopts [i].lopt_ : cmdopts [i].loptbuf_; + + if (opthelp && *opthelp) { + + if ( cmdopts [i].sopt_ == opthelp [0] && '\0' == opthelp [1] + || *lopt && 0 == strcmp (lopt + 1, opthelp)) { + + // remember that we found the option whose (short + // or long) name we're to give help on; after printing + // the help text on the option keep looping in case + // there is another option and callback with the same + // name (unlikely but possible) + option_found = 1; + } + else { + // the option doesn't match, continue searching + continue; + } + } + + printf (" "); + + if (cmdopts [i].sopt_) { + printf ("-%c", cmdopts [i].sopt_); + + if (lopt) + printf (" | "); + } + + const char *pfx = ""; + const char *sfx = pfx; + + if (lopt) { + printf ("-%s", lopt); + if ( cmdopts [i].arg_ + && '=' != lopt [strlen (lopt) - 1]) { + pfx = " [ "; + sfx = " ]"; + } + } + + printf ("%s%s%s", pfx, cmdopts [i].arg_ ? "" : "", sfx); + + if (_RWSTD_SIZE_MAX == cmdopts [i].maxcalls_) + printf (" (each occurrence evaluated)\n"); + else if (1 < cmdopts [i].maxcalls_) + printf (" (at most %u occurrences evaluated)\n", + unsigned (cmdopts [i].maxcalls_)); + else + printf (" (only the first occurrence evaluated)\n"); + + // invoke callback with the "--help" option + if (cmdopts [i].callback_) { + + char* help [2] = { 0, 0 }; + + cmdopts [i].callback_ (1, help); + + for (const char *line = help [0]; line; ) { + + const char* const nl = strchr (line, '\n'); + const int len = nl ? int (nl - line) : int (strlen (line)); + + printf (" %.*s\n", len, line); + + line = nl; + if (nl) + ++line; + } + } + } + + if (opthelp && !option_found) { + fprintf (stderr, "Unknown option \"%s\".\n", opthelp); + exit (1); + } + + exit (0); + + return 0; +} + +/**************************************************************************/ + +static int +rw_set_ignenv (int argc, char *argv[]) +{ + if (1 == argc && argv && 0 == argv [0]) { + static const char helpstr[] = { + "Prevents options specified in the RWSTD_TESTOPTS environment\n" + "variable from taking effect.\n" + "Unless this option is specified, the RWSTD_TESTOPTS environment\n" + "variable is processed as if its value were specified on the \n" + "command line.\n" + "For example, setting the value of the variable to the string\n" + "\"--verbose --no-wchar\" and invoking this program with no\n" + "command line arguments will have the same effect as invoking\n" + "it with the two arguments on the command line.\n" + }; + + argv [0] = _RWSTD_CONST_CAST (char*, helpstr); + + return 0; + } + + return 0; +} + +extern "C" { + +static void +rw_clear_opts () +{ + // reset all options, deallocating dynamically allocated storage + + for (size_t i = 0; i != ncmdopts; ++i) { + + // free any storage allocated for the option name + free (cmdopts [i].lopt_); + } + + if (cmdopts != cmdoptbuf) { + // free the storage allocated for all the options + free (cmdopts); + } + + // reset the options pointer to point at the statically + // allocated buffer and the count back to 0 + ncmdopts = 0; + cmdopts = cmdoptbuf; + optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf; +} + +} + +static void +rw_set_myopts () +{ + static int cleanup_handler_registered; + + if (0 == cleanup_handler_registered) { + atexit (rw_clear_opts); + cleanup_handler_registered = 1; + } + + if (0 != ncmdopts) + return; + + static int recursive; + + if (recursive) + return; + + ++recursive; + + rw_setopts ("|-help: " // argument optional + "|-ignenv ", + rw_print_help, + rw_set_ignenv); + + ndefopts = ncmdopts; + + recursive = 0; +} + +/**************************************************************************/ + +////////////////////////////////////////////////////////////////////// +// syntax of the option description string: +// +// opts ::= opt [ ':' | '=' | '#' ] [ @N | @* | '!' ] [ opts ] +// opt ::= [ '|' ] +// ::= '|' +// sopt ::= char +// lopt ::= char char* +// char ::= A-Z a-z _ 0-9 + +_TEST_EXPORT int +rw_vsetopts (const char *opts, va_list va) +{ + if (0 == opts) { + + rw_clear_opts (); + return 0; + } + + rw_set_myopts (); + + const char *next = opts; + + for ( ; ; ++ncmdopts) { + + while (' ' == *next) + ++next; + + if ('\0' == *next) { + break; + } + + if (ncmdopts == optbufsize) { + + const size_t newbufsize = 2 * ncmdopts + 1; + + cmdopts_t* const newopts = + (cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t)); + + if (0 == newopts) { + fprintf (stderr, "%s%d: failed to allocate memory\n", + __FILE__, __LINE__); + abort (); + } + + memcpy (newopts, cmdopts, ncmdopts * sizeof (cmdopts_t)); + + if (cmdopts != cmdoptbuf) + free (cmdopts); + + cmdopts = newopts; + optbufsize = newbufsize; + } + + // clear the next option info + memset (cmdopts + ncmdopts, 0, sizeof *cmdopts); + + if ('|' != *next) + cmdopts [ncmdopts].sopt_ = *next++; + + if ('|' == *next) { + const char* end = strpbrk (++next, "|@:=*!# "); + if (0 == end) + end = next + strlen (next); + + // copy the option name up to but not including the delimiter + // (except when the delimiter is the equals sign ('='), which + // becomes the last character of the option name + const size_t optlen = size_t (end - next) + ('=' == *end); + + char *lopt = 0; + + if (optlen < sizeof cmdopts [ncmdopts].loptbuf_) + lopt = cmdopts [ncmdopts].loptbuf_; + else { + lopt = (char*)malloc (optlen + 1); + cmdopts [ncmdopts].lopt_ = lopt; + } + + memcpy (lopt, next, optlen); + lopt [optlen] = '\0'; + + next = end; + } + + // only the first occurrence of each command line option + // causes an invocation of the callback, all subsequent + // ones will be ignored by default + cmdopts [ncmdopts].maxcalls_ = 1; + + int arg_is_callback = true; + + if ('#' == *next) { + // insead of a pointer to a callback, the argument + // is a pointer to an int counter that is to be + // incremented for each occurrence of the option + // during processing; when the option is immediately + // followed by the equals sign ('=') and a numeric + // argument the value of the argument will be stored + arg_is_callback = false; + ++next; + + // an unlimited number of occurrences of the option + // are allowed and will be counted + cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX; + } + else if (':' == *next || '=' == *next) { + // ':' : argument optional + // '=' : argument required + cmdopts [ncmdopts].arg_ = true; + ++next; + } + + if ('@' == *next) { + + ++next; + + // at most how many occurrences of an option can be processed? + if ('*' == *next) { + // unlimited + cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX; + ++next; + } + else { + // at most this many + char *end; + cmdopts [ncmdopts].maxcalls_ = strtoul (next, &end, 10); + next = end; + } + } + else if ('!' == *next) { + cmdopts [ncmdopts].inv_ = true; + ++next; + } + + if (arg_is_callback) { + // retrieve the callback and verify it's not null + // (null callback is permitted in the special case when + // the short option is '-', i.e., when setting up or + // resetting an "unknown option" handler) + cmdopts [ncmdopts].callback_ = va_arg (va, optcallback_t*); + } + else { + // retrieve the address of the int counter where to keep + // track of the number of occurrences of the option, or + // where to store the value of the numeric argument of + // the option + cmdopts [ncmdopts].pcntr_ = va_arg (va, int*); + } + + if ( '-' != cmdopts [ncmdopts].sopt_ + && 0 == cmdopts [ncmdopts].callback_ + && 0 == cmdopts [ncmdopts].pcntr_) { + + // get a pointer to the long option name + const char* const lopt = cmdopts [ncmdopts].lopt_ + ? cmdopts [ncmdopts].lopt_ : cmdopts [ncmdopts].loptbuf_; + + if (*lopt) + fprintf (stderr, "null handler for option -%s\n", lopt); + else + fprintf (stderr, "null handler for option -%c\n", + cmdopts [ncmdopts].sopt_); + + abort (); + } + } + + return int (ncmdopts - ndefopts); +} + +/**************************************************************************/ + +_TEST_EXPORT int +rw_setopts (const char *opts, ...) +{ + va_list va; + va_start (va, opts); + const int result = rw_vsetopts (opts, va); + va_end (va); + return result; +} + +/**************************************************************************/ + +_TEST_EXPORT int +rw_runopts (int argc, char *argv[]) +{ + rw_set_myopts (); + + static int recursive = false; + + // ignore options set in the environment? + int ignenv = recursive; + + // return status + int status = 0; + + // number of options processed + int nopts = 0; + + // index of registered option whose callback should be invoked + // for command line options that do not match any other + size_t not_found_inx = _RWSTD_SIZE_MAX; + + // iterate over the command line arguments until a callback + // returns a non-zero value or until all options have been + // successfully processed + for (int i = 0; i < argc && argv [i] && 0 == status; ++i) { + + if (0 == strcmp ("--ignore-environment", argv [i])) { + // ignore options set in the environment + ignenv = true; + continue; + } + + if (0 == strcmp ("--", argv [i])) { + // "--" terminates options, everything + // after it is treated as an argument + break; + } + + // the name of the option without the leading dash + const char* const optname = argv [i] + 1; + + // look for the first equals sign + const char* const eq = strchr (optname, '='); + + // compute the length of the option including the equals sign (if any) + const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname); + + int found = false; + + // look up each command line option (i.e., a string that starts + // with a dash ('-')) and invoke the callback associated with it + for (size_t j = 0; j != ncmdopts; ++j) { + + if ('-' == cmdopts [j].sopt_) + not_found_inx = j; + + if ('-' == argv [i][0]) { + + const size_t cmplen = + eq && cmdopts [j].pcntr_ ? optlen - 1 : optlen; + + // get a pointer to the (possibly empty) name + // of the long option + const char* const lopt = cmdopts [j].lopt_ ? + cmdopts [j].lopt_ : cmdopts [j].loptbuf_; + + // try to match the long option first, and only if it + // doesn't match try the short single-character option + if ( cmplen == strlen (lopt) + && 0 == memcmp (optname, lopt, cmplen) + || cmdopts [j].sopt_ + && optname [0] == cmdopts [j].sopt_ + && (1 == optlen || cmdopts [j].arg_)) { + + // matching option has been found + found = true; + + // ignore the option if invoked recursively (by processing + // options set in the environment) and the option has + // already been seen (this prevents duplicate processing + // of options that are set both on the command line and + // in the environment and allows option with an argument + // set on the command line to override those set in the + // environment) + + if (cmdopts [j].ncalls_ && recursive) + continue; + + // if the option has been evaluated the maximum number + // of times, avoid evaluating it and continue processing + if (cmdopts [j].maxcalls_ <= cmdopts [j].ncalls_) + continue; + + if (cmdopts [j].callback_) { + if (!cmdopts [j].inv_) { + // when the command line argument matched + // the option, invoke the callback function + status = cmdopts [j].callback_ (argc - i, argv + i); + } + } + else if (eq) { + assert (0 != cmdopts [j].pcntr_); + + // obtain the numeric argument + char *end = 0; + const long optval = strtol (eq + 1, &end, 0); + + if (end && '\0' != *end) { + fprintf (stderr, "expected numeric argument: %s\n", + optname); + ignenv = true; + errno = EINVAL; + status = 1; + } + +#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE + + else if ( optval < _RWSTD_INT_MIN + || _RWSTD_INT_MAX < optval) { + fprintf (stderr, "numeric argument %ld out of range" + " [%d, %d]: %s\n", optval, + _RWSTD_INT_MIN, _RWSTD_INT_MAX, optname); + ignenv = true; + errno = EINVAL; + status = 1; + } + +#endif // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE + + else { + *cmdopts [j].pcntr_ = optval; + } + } + else { + assert (0 != cmdopts [j].pcntr_); + ++*cmdopts [j].pcntr_; + } + + ++cmdopts [j].ncalls_; + + if (recursive) + cmdopts [j].envseen_ = true; + + ++nopts; + + if (status) { + // when the status returned from the last callback + // is non-0 stop further processing (including + // any options set in the environment) and return + // status to the caller + ignenv = true; + break; + } + } + } + } + + if (!found && '-' == argv [i][0]) { + + // invoke the appropriate error handler for an option + // that was not found + if (_RWSTD_SIZE_MAX != not_found_inx) { + + // invoke the error handler set up through rw_setopts() + // and let the handler decide whether to go on processing + // other options or whether to abort + status = cmdopts [not_found_inx].callback_ (argc - i, argv + i); + if (status) { + // no further processing done + ignenv = true; + break; + } + } + else { + // print an error message to stderr when no error + // handler has been set up + fprintf (stderr, "unknown option: %s\n", argv [i]); + ignenv = true; + errno = EINVAL; + status = 1; + break; + } + } + } + + if (!ignenv) { + // process options from the environment + const char* const envar = getenv ("RWSTD_TESTOPTS"); + if (envar) { + recursive = true; + rw_runopts (envar); + recursive = false; + } + } + + // invoke any inverted callbacks or bump their user-specified counters, + // and reset internal counters indicating if/how many times each option + // has been processed + for (size_t j = 0; j != ncmdopts; ++j) { + + if (cmdopts [j].inv_ && 0 == cmdopts [j].ncalls_ && 0 == status) { + + if (cmdopts [j].callback_) + status = cmdopts [j].callback_ (0, 0); + else { + assert (0 != cmdopts [j].pcntr_); + ++*cmdopts [j].pcntr_; + } + } + + cmdopts [j].ncalls_ = 0; + cmdopts [j].envseen_ = false; + } + + return status; +} + +/**************************************************************************/ + +_TEST_EXPORT int +rw_runopts (const char *str) +{ + assert (0 != str); + + rw_set_myopts (); + + char buf [80]; // fixed size buffer to copy `str' into + char *pbuf = buf; // a modifiable copy of `str' + + size_t len = strlen (str); + if (len >= sizeof buf) { + // allocate if necessary + pbuf = (char*)malloc (len + 1); + if (!pbuf) + return -1; + } + + // copy `str' to modifiable buffer + memcpy (pbuf, str, len + 1); + + char *tmp_argv_buf [32] = { 0 }; // fixed size argv buffer + char **argv = tmp_argv_buf; // array of arguments + + // initial size of argument array (will grow as necessary) + size_t argv_size = sizeof tmp_argv_buf / sizeof *tmp_argv_buf; + size_t argc = 0; // number of arguments in array + + int in_quotes = 0; // quoted argument being processed + + for (char *s = pbuf; *s; ++s) { + if ('"' == *s) { + in_quotes = !in_quotes; + continue; + } + + if (in_quotes) + continue; + + // split up unquoted space-separated arguments + if (argc == 0 || ' ' == *s) { + if (argc > 0) + *s = 0; + + // skip over leading spaces + if (argc > 0 || ' ' == *s) + while (' ' == *++s); + + if (*s) { + if (argc == argv_size) { + // grow `argv' as necessary + char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2); + if (!tmp) { + if (argv != tmp_argv_buf) + free (argv); + return -1; + } + + // copy existing elementes and zero out any new entries + memcpy (tmp, argv, sizeof *tmp * argv_size); + memset (tmp + argv_size, 0, sizeof *tmp * argv_size); + + // free existing buffer if necessary + if (argv != tmp_argv_buf) + free (argv); + + // reassign buffer and increase size + argv = tmp; + argv_size *= 2; + } + + // add argument to array + argv [argc++] = s; + } + } + } + + // process `argc' options pointed to by `argv' + const int status = rw_runopts (int (argc), argv); + + // free buffers if necessary + if (argv != tmp_argv_buf) + free (argv); + + if (pbuf != buf) + free (pbuf); + + return status; +}