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