os/ossrv/stdcpp/tsrc/Stdcpp_test/stdcxx/testengine/src/cmdopt.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /***************************************************************************
     2  *
     3  * $Id: cmdopt.cpp 348342 2005-11-23 02:03:26Z sebor $
     4  *
     5  ************************************************************************
     6  *
     7  * Copyright (c) 1994-2005 Quovadx,  Inc., acting through its  Rogue Wave
     8  * Software division. Licensed under the Apache License, Version 2.0 (the
     9  * "License");  you may  not use this file except  in compliance with the
    10  * License.    You    may   obtain   a   copy   of    the   License    at
    11  * http://www.apache.org/licenses/LICENSE-2.0.    Unless   required    by
    12  * applicable law  or agreed to  in writing,  software  distributed under
    13  * the License is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR
    14  * CONDITIONS OF  ANY KIND, either  express or implied.  See  the License
    15  * for the specific language governing permissions  and limitations under
    16  * the License.
    17  * 
    18  **************************************************************************/
    19 
    20 // expand _TEST_EXPORT macros
    21 #define _RWSTD_TEST_SRC
    22 
    23 #include <cmdopt.h>
    24 
    25 #include <assert.h>   // for assert
    26 #include <errno.h>    // for errno
    27 #include <stdarg.h>   // for va_arg, ...
    28 #include <stdio.h>    // for fprintf
    29 #include <stdlib.h>   // for atexit, free, malloc
    30 #include <string.h>   // for memcpy, strcpy, strcmp, ...
    31 
    32 #ifdef __ARMCC__
    33 #pragma diag_suppress 61
    34 #pragma diag_suppress 63
    35 #endif
    36 
    37 #ifndef EINVAL
    38 #  define EINVAL   22   /* e.g., HP-UX, Linux, Solaris */
    39 #endif   // EINVAL
    40 
    41 /**************************************************************************/
    42 
    43 typedef int (optcallback_t)(int, char*[]);
    44 
    45 struct cmdopts_t
    46 {
    47     char           loptbuf_ [32];   // buffer for long option name
    48     optcallback_t *callback_;       // function to call to process option
    49 
    50     // counter to increment for each occurrence of an option
    51     // or to set to the numeric argument (when specified)
    52     int           *pcntr_;
    53 
    54     char          *lopt_;           // long option name
    55     char           sopt_;           // short option name
    56 
    57     size_t         maxcalls_;       // how many times option can be invoked
    58     size_t         ncalls_;         // how many times it has been invoked
    59 
    60     unsigned       arg_ : 1;        // option takes an argument?
    61     unsigned       inv_ : 1;        // callback invocation inverted
    62     unsigned       envseen_ : 1;    // environment option already processed
    63 };
    64 
    65 
    66 // total number of registered options
    67 static size_t ncmdopts;
    68 
    69 // number of default (always defined) options
    70 static size_t ndefopts;
    71 static cmdopts_t cmdoptbuf [32];
    72 static cmdopts_t *cmdopts = cmdoptbuf;
    73 static size_t optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
    74 
    75 /**************************************************************************/
    76 
    77 static int
    78 rw_print_help (int argc, char *argv[])
    79 {
    80     if (1 == argc && argv && 0 == argv [0]) {
    81         static const char helpstr[] = {
    82             "Without an argument, prints this help to stdout and exits with\n"
    83             "a status of 0 without further processing.\n"
    84             "With the optional argument prints help on the option with that\n"
    85             "name, if one exists, and exits with a status of 0. If an option\n"
    86             "with the specified name does not exist, prints an error message\n"
    87             "to stderr and exits with a status of 1.\n"
    88             "The leading underscores in the name of an option are optional.\n"
    89         };
    90 
    91         argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
    92 
    93         return 0;
    94     }
    95 
    96     // the option name to get help on, if any
    97     const char* opthelp = 1 < argc ? argv [1] : 0;
    98 
    99     // remove the optional one or two leading underscores
   100     if (opthelp && '-' == opthelp [0] && opthelp [1])
   101         opthelp += 1 + ('-' == opthelp [1]);
   102 
   103     if (0 == opthelp)
   104         printf ("OPTIONS\n");
   105 
   106     // set to a non-zero when the specified option is found
   107     int option_found = 0;
   108 
   109     for (size_t i = 0; i != ncmdopts; ++i) {
   110 
   111         // get a pointer to the name of the long option, if any
   112         const char* const lopt =
   113             cmdopts [i].lopt_ ? cmdopts [i].lopt_ : cmdopts [i].loptbuf_;
   114 
   115         if (opthelp && *opthelp) {
   116 
   117             if (   cmdopts [i].sopt_ == opthelp [0] && '\0' == opthelp [1]
   118                 || *lopt && 0 == strcmp (lopt + 1, opthelp)) {
   119 
   120                 // remember that we found the option whose (short
   121                 // or long) name we're to give help on; after printing
   122                 // the help text on the option keep looping in case
   123                 // there is another option and callback with the same
   124                 // name (unlikely but possible)
   125                 option_found = 1;
   126             }
   127             else {
   128                 // the option doesn't match, continue searching
   129                 continue;
   130             }
   131         }
   132 
   133         printf ("     ");
   134 
   135         if (cmdopts [i].sopt_) {
   136             printf ("-%c", cmdopts [i].sopt_);
   137 
   138             if (lopt)
   139                 printf (" | ");
   140         }
   141 
   142         const char *pfx = "";
   143         const char *sfx = pfx;
   144 
   145         if (lopt) {
   146             printf ("-%s", lopt);
   147             if (   cmdopts [i].arg_
   148                 && '=' != lopt [strlen (lopt) - 1]) {
   149                 pfx = " [ ";
   150                 sfx = " ]";
   151             }
   152         }
   153 
   154         printf ("%s%s%s", pfx, cmdopts [i].arg_ ? "<arg>" : "", sfx);
   155 
   156         if (_RWSTD_SIZE_MAX == cmdopts [i].maxcalls_)
   157             printf (" (each occurrence evaluated)\n");
   158         else if (1 < cmdopts [i].maxcalls_)
   159             printf (" (at most %u occurrences evaluated)\n",
   160                     unsigned (cmdopts [i].maxcalls_));
   161         else
   162             printf (" (only the first occurrence evaluated)\n");
   163 
   164         // invoke callback with the "--help" option
   165         if (cmdopts [i].callback_) {
   166 
   167             char* help [2] = { 0, 0 };
   168 
   169             cmdopts [i].callback_ (1, help);
   170 
   171             for (const char *line = help [0]; line; ) {
   172 
   173                 const char* const nl = strchr (line, '\n');
   174                 const int len = nl ? int (nl - line) : int (strlen (line));
   175 
   176                 printf ("       %.*s\n", len, line);
   177 
   178                 line = nl;
   179                 if (nl)
   180                     ++line;
   181             }
   182         }
   183     }
   184 
   185     if (opthelp && !option_found) {
   186         fprintf (stderr, "Unknown option \"%s\".\n", opthelp);
   187         exit (1);
   188     }
   189 
   190     exit (0);
   191 
   192     return 0;
   193 }
   194 
   195 /**************************************************************************/
   196 
   197 static int
   198 rw_set_ignenv (int argc, char *argv[])
   199 {
   200     if (1 == argc && argv && 0 == argv [0]) {
   201         static const char helpstr[] = {
   202             "Prevents options specified in the RWSTD_TESTOPTS environment\n"
   203             "variable from taking effect.\n"
   204             "Unless this option is specified, the RWSTD_TESTOPTS environment\n"
   205             "variable is processed as if its value were specified on the \n"
   206             "command line.\n"
   207             "For example, setting the value of the variable to the string\n"
   208             "\"--verbose --no-wchar\" and invoking this program with no\n"
   209             "command line arguments will have the same effect as invoking\n"
   210             "it with the two arguments on the command line.\n"
   211         };
   212 
   213         argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
   214 
   215         return 0;
   216     }
   217 
   218     return 0;
   219 }
   220 
   221 extern "C" {
   222 
   223 static void
   224 rw_clear_opts ()
   225 {
   226     // reset all options, deallocating dynamically allocated storage
   227 
   228     for (size_t i = 0; i != ncmdopts; ++i) {
   229 
   230         // free any storage allocated for the option name
   231         free (cmdopts [i].lopt_);
   232     }
   233 
   234     if (cmdopts != cmdoptbuf) {
   235         // free the storage allocated for all the options
   236         free (cmdopts);
   237     }
   238 
   239     // reset the options pointer to point at the statically
   240     // allocated buffer and the count back to 0
   241     ncmdopts   = 0;
   242     cmdopts    = cmdoptbuf;
   243     optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
   244 }
   245 
   246 }
   247 
   248 static void
   249 rw_set_myopts ()
   250 {
   251     static int cleanup_handler_registered;
   252 
   253     if (0 == cleanup_handler_registered) {
   254         atexit (rw_clear_opts);
   255         cleanup_handler_registered = 1;
   256     }
   257 
   258     if (0 != ncmdopts)
   259         return;
   260 
   261     static int recursive;
   262 
   263     if (recursive)
   264         return;
   265 
   266     ++recursive;
   267 
   268     rw_setopts ("|-help: "   // argument optional
   269                 "|-ignenv ",
   270                 rw_print_help,
   271                 rw_set_ignenv);
   272 
   273     ndefopts = ncmdopts;
   274 
   275     recursive = 0;
   276 }
   277 
   278 /**************************************************************************/
   279 
   280 //////////////////////////////////////////////////////////////////////
   281 // syntax of the option description string:
   282 //
   283 // opts ::= opt [ ':' | '=' | '#' ] [ @N | @* | '!' ] [ opts ]
   284 // opt  ::= <sopt> [ '|' <lopt>]
   285 //      ::= '|' <lopt>
   286 // sopt ::= char
   287 // lopt ::= char char*
   288 // char ::= A-Z a-z _ 0-9
   289 
   290 _TEST_EXPORT int
   291 rw_vsetopts (const char *opts, va_list va)
   292 {
   293     if (0 == opts) {
   294 
   295         rw_clear_opts ();
   296         return 0;
   297     }
   298 
   299     rw_set_myopts ();
   300 
   301     const char *next = opts;
   302 
   303     for ( ; ; ++ncmdopts) {
   304 
   305         while (' ' == *next)
   306             ++next;
   307 
   308         if ('\0' == *next) {
   309             break;
   310         }
   311 
   312         if (ncmdopts == optbufsize) {
   313 
   314             const size_t newbufsize = 2 * ncmdopts + 1;
   315             
   316             cmdopts_t* const newopts =
   317                 (cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t));
   318 
   319             if (0 == newopts) {
   320                 fprintf (stderr, "%s%d: failed to allocate memory\n",
   321                          __FILE__, __LINE__);
   322                 abort ();
   323             }
   324 
   325             memcpy (newopts, cmdopts, ncmdopts * sizeof (cmdopts_t));
   326 
   327             if (cmdopts != cmdoptbuf)
   328                 free (cmdopts);
   329 
   330             cmdopts    = newopts;
   331             optbufsize = newbufsize;
   332         }
   333 
   334         // clear the next option info
   335         memset (cmdopts + ncmdopts, 0, sizeof *cmdopts);
   336 
   337         if ('|' != *next)
   338             cmdopts [ncmdopts].sopt_ = *next++;
   339 
   340         if ('|' == *next) {
   341             const char* end = strpbrk (++next, "|@:=*!# ");
   342             if (0 == end)
   343                 end = next + strlen (next);
   344 
   345             // copy the option name up to but not including the delimiter
   346             // (except when the delimiter is the equals sign ('='), which
   347             // becomes the last character of the option name
   348             const size_t optlen = size_t (end - next) + ('=' == *end);
   349 
   350             char *lopt = 0;
   351 
   352             if (optlen < sizeof cmdopts [ncmdopts].loptbuf_)
   353                 lopt = cmdopts [ncmdopts].loptbuf_;
   354             else {
   355                 lopt = (char*)malloc (optlen + 1);
   356                 cmdopts [ncmdopts].lopt_ = lopt;
   357             }
   358 
   359             memcpy (lopt, next, optlen);
   360             lopt [optlen] = '\0';
   361 
   362             next = end;
   363         }
   364 
   365         // only the first occurrence of each command line option
   366         // causes an invocation of the callback, all subsequent
   367         // ones will be ignored by default
   368         cmdopts [ncmdopts].maxcalls_ = 1;
   369 
   370         int arg_is_callback = true;
   371 
   372         if ('#' == *next) {
   373             // insead of a pointer to a callback, the argument
   374             // is a pointer to an int counter that is to be
   375             // incremented for each occurrence of the option
   376             // during processing; when the option is immediately
   377             // followed by the equals sign ('=') and a numeric
   378             // argument the value of the argument will be stored
   379             arg_is_callback = false;
   380             ++next;
   381             
   382             // an unlimited number of occurrences of the option
   383             // are allowed and will be counted
   384             cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
   385         }
   386         else if (':' == *next || '=' == *next) {
   387             // ':' : argument optional
   388             // '=' : argument required
   389             cmdopts [ncmdopts].arg_ = true;
   390             ++next;
   391         }
   392 
   393         if ('@' == *next) {
   394 
   395             ++next;
   396 
   397             // at most how many occurrences of an option can be processed?
   398             if ('*' == *next) {
   399                 // unlimited
   400                 cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
   401                 ++next;
   402             }
   403             else {
   404                 // at most this many
   405                 char *end;
   406                 cmdopts [ncmdopts].maxcalls_ = strtoul (next, &end, 10);
   407                 next = end;
   408             }
   409         }
   410         else if ('!' == *next) {
   411             cmdopts [ncmdopts].inv_ = true;
   412             ++next;
   413         }
   414 
   415         if (arg_is_callback) {
   416             // retrieve the callback and verify it's not null
   417             // (null callback is permitted in the special case when
   418             // the short option is '-', i.e., when setting up or
   419             // resetting an "unknown option" handler)
   420             cmdopts [ncmdopts].callback_ = va_arg (va, optcallback_t*);
   421         }
   422         else {
   423             // retrieve the address of the int counter where to keep
   424             // track of the number of occurrences of the option, or
   425             // where to store the value of the numeric argument of
   426             // the option
   427             cmdopts [ncmdopts].pcntr_ = va_arg (va, int*);
   428         }
   429 
   430         if (   '-' != cmdopts [ncmdopts].sopt_
   431             && 0 == cmdopts [ncmdopts].callback_
   432             && 0 == cmdopts [ncmdopts].pcntr_) {
   433 
   434             // get a pointer to the long option name
   435             const char* const lopt = cmdopts [ncmdopts].lopt_
   436                 ? cmdopts [ncmdopts].lopt_ : cmdopts [ncmdopts].loptbuf_;
   437 
   438             if (*lopt)
   439                 fprintf (stderr, "null handler for option -%s\n", lopt);
   440             else
   441                 fprintf (stderr, "null handler for option -%c\n",
   442                          cmdopts [ncmdopts].sopt_);
   443                 
   444             abort ();
   445         }
   446     }
   447 
   448     return int (ncmdopts - ndefopts);
   449 }
   450 
   451 /**************************************************************************/
   452 
   453 _TEST_EXPORT int
   454 rw_setopts (const char *opts, ...)
   455 {
   456     va_list va;
   457     va_start (va, opts);
   458     const int result = rw_vsetopts (opts, va);
   459     va_end (va);
   460     return result;
   461 }
   462 
   463 /**************************************************************************/
   464 
   465 _TEST_EXPORT int
   466 rw_runopts (int argc, char *argv[])
   467 {
   468     rw_set_myopts ();
   469 
   470     static int recursive = false;
   471 
   472     // ignore options set in the environment?
   473     int ignenv = recursive;
   474 
   475     // return status
   476     int status = 0;
   477 
   478     // number of options processed
   479     int nopts = 0;
   480 
   481     // index of registered option whose callback should be invoked
   482     // for command line options that do not match any other
   483     size_t not_found_inx = _RWSTD_SIZE_MAX;
   484 
   485     // iterate over the command line arguments until a callback
   486     // returns a non-zero value or until all options have been
   487     // successfully processed
   488     for (int i = 0; i < argc && argv [i] && 0 == status; ++i) {
   489 
   490         if (0 == strcmp ("--ignore-environment", argv [i])) {
   491             // ignore options set in the environment
   492             ignenv = true;
   493             continue;
   494         }
   495 
   496         if (0 == strcmp ("--", argv [i])) {
   497             // "--" terminates options, everything
   498             // after it is treated as an argument
   499             break;
   500         }
   501 
   502         // the name of the option without the leading dash
   503         const char* const optname = argv [i] + 1;
   504 
   505         // look for the first equals sign
   506         const char* const eq = strchr (optname, '=');
   507 
   508         // compute the length of the option including the equals sign (if any)
   509         const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname);
   510 
   511         int found = false;
   512 
   513         // look up each command line option (i.e., a string that starts
   514         // with a dash ('-')) and invoke the callback associated with it
   515         for (size_t j = 0; j != ncmdopts; ++j) {
   516 
   517             if ('-' == cmdopts [j].sopt_)
   518                 not_found_inx = j;
   519 
   520             if ('-' == argv [i][0]) {
   521 
   522                 const size_t cmplen =
   523                     eq && cmdopts [j].pcntr_ ? optlen - 1 : optlen;
   524 
   525                 // get a pointer to the (possibly empty) name
   526                 // of the long option
   527                 const char* const lopt = cmdopts [j].lopt_ ? 
   528                     cmdopts [j].lopt_ : cmdopts [j].loptbuf_;
   529 
   530                 // try to match the long option first, and only if it
   531                 // doesn't match try the short single-character option
   532                 if (   cmplen == strlen (lopt)
   533                     && 0 == memcmp (optname, lopt, cmplen)
   534                     || cmdopts [j].sopt_
   535                     && optname [0] == cmdopts [j].sopt_
   536                     && (1 == optlen || cmdopts [j].arg_)) {
   537 
   538                     // matching option has been found
   539                     found = true;
   540 
   541                     // ignore the option if invoked recursively (by processing
   542                     // options set in the environment) and the option has
   543                     // already been seen (this prevents duplicate processing
   544                     // of options that are set both on the command line and
   545                     // in the environment and allows option with an argument
   546                     // set on the command line to override those set in the
   547                     // environment)
   548 
   549                     if (cmdopts [j].ncalls_ && recursive)
   550                         continue;
   551 
   552                     // if the option has been evaluated the maximum number
   553                     // of times, avoid evaluating it and continue processing
   554                     if (cmdopts [j].maxcalls_ <= cmdopts [j].ncalls_)
   555                         continue;
   556 
   557                     if (cmdopts [j].callback_) {
   558                         if (!cmdopts [j].inv_) {
   559                             // when the command line argument matched
   560                             // the option,  invoke the callback function
   561                             status = cmdopts [j].callback_ (argc - i, argv + i);
   562                         }
   563                     }
   564                     else if (eq) {
   565                         assert (0 != cmdopts [j].pcntr_);
   566 
   567                         // obtain the numeric argument
   568                         char *end = 0;
   569                         const long optval = strtol (eq + 1, &end, 0);
   570 
   571                         if (end && '\0' != *end) {
   572                             fprintf (stderr, "expected numeric argument: %s\n",
   573                                      optname);
   574                             ignenv = true;
   575                             errno  = EINVAL;
   576                             status = 1;
   577                         }
   578 
   579 #if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
   580 
   581                         else if (   optval < _RWSTD_INT_MIN
   582                                  || _RWSTD_INT_MAX < optval) {
   583                             fprintf (stderr, "numeric argument %ld out of range"
   584                                      " [%d, %d]: %s\n", optval,
   585                                      _RWSTD_INT_MIN, _RWSTD_INT_MAX, optname);
   586                             ignenv = true;
   587                             errno  = EINVAL;
   588                             status = 1;
   589                         }
   590 
   591 #endif   // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
   592 
   593                         else {
   594                             *cmdopts [j].pcntr_ = optval;
   595                         }
   596                     }
   597                     else {
   598                         assert (0 != cmdopts [j].pcntr_);
   599                         ++*cmdopts [j].pcntr_;
   600                     }
   601 
   602                     ++cmdopts [j].ncalls_;
   603 
   604                     if (recursive)
   605                         cmdopts [j].envseen_ = true;
   606 
   607                     ++nopts;
   608 
   609                     if (status) {
   610                         // when the status returned from the last callback
   611                         // is non-0 stop further processing (including
   612                         // any options set in the environment) and return
   613                         // status to the caller
   614                         ignenv = true;
   615                         break;
   616                     }
   617                 }
   618             }
   619         }
   620 
   621         if (!found && '-' == argv [i][0]) {
   622 
   623             // invoke the appropriate error handler for an option
   624             // that was not found
   625             if (_RWSTD_SIZE_MAX != not_found_inx) {
   626 
   627                 // invoke the error handler set up through rw_setopts()
   628                 // and let the handler decide whether to go on processing
   629                 // other options or whether to abort
   630                 status = cmdopts [not_found_inx].callback_ (argc - i, argv + i);
   631                 if (status) {
   632                     // no further processing done
   633                     ignenv = true;
   634                     break;
   635                 }
   636             }
   637             else {
   638                 // print an error message to stderr when no error
   639                 // handler has been set up
   640                 fprintf (stderr, "unknown option: %s\n", argv [i]);
   641                 ignenv = true;
   642                 errno  = EINVAL;
   643                 status = 1;
   644                 break;
   645             }
   646         }
   647     }
   648 
   649     if (!ignenv) {
   650         // process options from the environment
   651         const char* const envar = getenv ("RWSTD_TESTOPTS");
   652         if (envar) {
   653             recursive = true;
   654             rw_runopts (envar);
   655             recursive = false;
   656         }
   657     }
   658 
   659     // invoke any inverted callbacks or bump their user-specified counters,
   660     // and reset internal counters indicating if/how many times each option
   661     // has been processed
   662     for (size_t j = 0; j != ncmdopts; ++j) {
   663 
   664         if (cmdopts [j].inv_ && 0 == cmdopts [j].ncalls_ && 0 == status) {
   665 
   666             if (cmdopts [j].callback_)
   667                 status = cmdopts [j].callback_ (0, 0);
   668             else {
   669                 assert (0 != cmdopts [j].pcntr_);
   670                 ++*cmdopts [j].pcntr_;
   671             }
   672         }
   673 
   674         cmdopts [j].ncalls_  = 0;
   675         cmdopts [j].envseen_ = false;
   676     }
   677 
   678     return status;
   679 }
   680 
   681 /**************************************************************************/
   682 
   683 _TEST_EXPORT int
   684 rw_runopts (const char *str)
   685 {
   686     assert (0 != str);
   687 
   688     rw_set_myopts ();
   689 
   690     char buf [80];      // fixed size buffer to copy `str' into
   691     char *pbuf = buf;   // a modifiable copy of `str'
   692 
   693     size_t len = strlen (str);
   694     if (len >= sizeof buf) {
   695         // allocate if necessary
   696         pbuf = (char*)malloc (len + 1);
   697         if (!pbuf)
   698             return -1;
   699     }
   700 
   701     // copy `str' to modifiable buffer
   702     memcpy (pbuf, str, len + 1);
   703 
   704     char *tmp_argv_buf [32] = { 0 };   // fixed size argv buffer
   705     char **argv = tmp_argv_buf;        // array of arguments
   706 
   707     // initial size of argument array (will grow as necessary)
   708     size_t argv_size = sizeof tmp_argv_buf / sizeof *tmp_argv_buf;
   709     size_t argc = 0;   // number of arguments in array
   710 
   711     int in_quotes = 0;   // quoted argument being processed
   712 
   713     for (char *s = pbuf; *s; ++s) {
   714         if ('"' == *s) {
   715             in_quotes = !in_quotes;
   716             continue;
   717         }
   718 
   719         if (in_quotes)
   720             continue;
   721 
   722         // split up unquoted space-separated arguments
   723         if (argc == 0 || ' ' == *s) {
   724             if (argc > 0) 
   725                 *s = 0;
   726 
   727             // skip over leading spaces
   728             if (argc > 0 || ' ' == *s)
   729                 while (' ' == *++s);
   730 
   731             if (*s) {
   732                 if (argc == argv_size) {
   733                     // grow `argv' as necessary
   734                     char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2);
   735                     if (!tmp) {
   736                         if (argv != tmp_argv_buf)
   737                             free (argv);
   738                         return -1;
   739                     }
   740 
   741                     // copy existing elementes and zero out any new entries
   742                     memcpy (tmp, argv, sizeof *tmp * argv_size);
   743                     memset (tmp + argv_size, 0, sizeof *tmp * argv_size);
   744 
   745                     // free existing buffer if necessary
   746                     if (argv != tmp_argv_buf)
   747                         free (argv);
   748 
   749                     // reassign buffer and increase size
   750                     argv       = tmp;
   751                     argv_size *= 2;
   752                 }
   753 
   754                 // add argument to array
   755                 argv [argc++] = s;
   756             }
   757         }
   758     }
   759 
   760     // process `argc' options pointed to by `argv'
   761     const int status = rw_runopts (int (argc), argv);
   762 
   763     // free buffers if necessary
   764     if (argv != tmp_argv_buf)
   765         free (argv);
   766 
   767     if (pbuf != buf)
   768         free (pbuf);
   769 
   770     return status;
   771 }