Update contrib.
1 /***************************************************************************
3 * $Id: cmdopt.cpp 348342 2005-11-23 02:03:26Z sebor $
5 ************************************************************************
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
18 **************************************************************************/
20 // expand _TEST_EXPORT macros
21 #define _RWSTD_TEST_SRC
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, ...
33 #pragma diag_suppress 61
34 #pragma diag_suppress 63
38 # define EINVAL 22 /* e.g., HP-UX, Linux, Solaris */
41 /**************************************************************************/
43 typedef int (optcallback_t)(int, char*[]);
47 char loptbuf_ [32]; // buffer for long option name
48 optcallback_t *callback_; // function to call to process option
50 // counter to increment for each occurrence of an option
51 // or to set to the numeric argument (when specified)
54 char *lopt_; // long option name
55 char sopt_; // short option name
57 size_t maxcalls_; // how many times option can be invoked
58 size_t ncalls_; // how many times it has been invoked
60 unsigned arg_ : 1; // option takes an argument?
61 unsigned inv_ : 1; // callback invocation inverted
62 unsigned envseen_ : 1; // environment option already processed
66 // total number of registered options
67 static size_t ncmdopts;
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;
75 /**************************************************************************/
78 rw_print_help (int argc, char *argv[])
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"
91 argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
96 // the option name to get help on, if any
97 const char* opthelp = 1 < argc ? argv [1] : 0;
99 // remove the optional one or two leading underscores
100 if (opthelp && '-' == opthelp [0] && opthelp [1])
101 opthelp += 1 + ('-' == opthelp [1]);
104 printf ("OPTIONS\n");
106 // set to a non-zero when the specified option is found
107 int option_found = 0;
109 for (size_t i = 0; i != ncmdopts; ++i) {
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_;
115 if (opthelp && *opthelp) {
117 if ( cmdopts [i].sopt_ == opthelp [0] && '\0' == opthelp [1]
118 || *lopt && 0 == strcmp (lopt + 1, opthelp)) {
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)
128 // the option doesn't match, continue searching
135 if (cmdopts [i].sopt_) {
136 printf ("-%c", cmdopts [i].sopt_);
142 const char *pfx = "";
143 const char *sfx = pfx;
146 printf ("-%s", lopt);
147 if ( cmdopts [i].arg_
148 && '=' != lopt [strlen (lopt) - 1]) {
154 printf ("%s%s%s", pfx, cmdopts [i].arg_ ? "<arg>" : "", sfx);
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_));
162 printf (" (only the first occurrence evaluated)\n");
164 // invoke callback with the "--help" option
165 if (cmdopts [i].callback_) {
167 char* help [2] = { 0, 0 };
169 cmdopts [i].callback_ (1, help);
171 for (const char *line = help [0]; line; ) {
173 const char* const nl = strchr (line, '\n');
174 const int len = nl ? int (nl - line) : int (strlen (line));
176 printf (" %.*s\n", len, line);
185 if (opthelp && !option_found) {
186 fprintf (stderr, "Unknown option \"%s\".\n", opthelp);
195 /**************************************************************************/
198 rw_set_ignenv (int argc, char *argv[])
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"
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"
213 argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
226 // reset all options, deallocating dynamically allocated storage
228 for (size_t i = 0; i != ncmdopts; ++i) {
230 // free any storage allocated for the option name
231 free (cmdopts [i].lopt_);
234 if (cmdopts != cmdoptbuf) {
235 // free the storage allocated for all the options
239 // reset the options pointer to point at the statically
240 // allocated buffer and the count back to 0
243 optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
251 static int cleanup_handler_registered;
253 if (0 == cleanup_handler_registered) {
254 atexit (rw_clear_opts);
255 cleanup_handler_registered = 1;
261 static int recursive;
268 rw_setopts ("|-help: " // argument optional
278 /**************************************************************************/
280 //////////////////////////////////////////////////////////////////////
281 // syntax of the option description string:
283 // opts ::= opt [ ':' | '=' | '#' ] [ @N | @* | '!' ] [ opts ]
284 // opt ::= <sopt> [ '|' <lopt>]
287 // lopt ::= char char*
288 // char ::= A-Z a-z _ 0-9
291 rw_vsetopts (const char *opts, va_list va)
301 const char *next = opts;
303 for ( ; ; ++ncmdopts) {
312 if (ncmdopts == optbufsize) {
314 const size_t newbufsize = 2 * ncmdopts + 1;
316 cmdopts_t* const newopts =
317 (cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t));
320 fprintf (stderr, "%s%d: failed to allocate memory\n",
325 memcpy (newopts, cmdopts, ncmdopts * sizeof (cmdopts_t));
327 if (cmdopts != cmdoptbuf)
331 optbufsize = newbufsize;
334 // clear the next option info
335 memset (cmdopts + ncmdopts, 0, sizeof *cmdopts);
338 cmdopts [ncmdopts].sopt_ = *next++;
341 const char* end = strpbrk (++next, "|@:=*!# ");
343 end = next + strlen (next);
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);
352 if (optlen < sizeof cmdopts [ncmdopts].loptbuf_)
353 lopt = cmdopts [ncmdopts].loptbuf_;
355 lopt = (char*)malloc (optlen + 1);
356 cmdopts [ncmdopts].lopt_ = lopt;
359 memcpy (lopt, next, optlen);
360 lopt [optlen] = '\0';
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;
370 int arg_is_callback = true;
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;
382 // an unlimited number of occurrences of the option
383 // are allowed and will be counted
384 cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
386 else if (':' == *next || '=' == *next) {
387 // ':' : argument optional
388 // '=' : argument required
389 cmdopts [ncmdopts].arg_ = true;
397 // at most how many occurrences of an option can be processed?
400 cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
406 cmdopts [ncmdopts].maxcalls_ = strtoul (next, &end, 10);
410 else if ('!' == *next) {
411 cmdopts [ncmdopts].inv_ = true;
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*);
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
427 cmdopts [ncmdopts].pcntr_ = va_arg (va, int*);
430 if ( '-' != cmdopts [ncmdopts].sopt_
431 && 0 == cmdopts [ncmdopts].callback_
432 && 0 == cmdopts [ncmdopts].pcntr_) {
434 // get a pointer to the long option name
435 const char* const lopt = cmdopts [ncmdopts].lopt_
436 ? cmdopts [ncmdopts].lopt_ : cmdopts [ncmdopts].loptbuf_;
439 fprintf (stderr, "null handler for option -%s\n", lopt);
441 fprintf (stderr, "null handler for option -%c\n",
442 cmdopts [ncmdopts].sopt_);
448 return int (ncmdopts - ndefopts);
451 /**************************************************************************/
454 rw_setopts (const char *opts, ...)
458 const int result = rw_vsetopts (opts, va);
463 /**************************************************************************/
466 rw_runopts (int argc, char *argv[])
470 static int recursive = false;
472 // ignore options set in the environment?
473 int ignenv = recursive;
478 // number of options processed
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;
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) {
490 if (0 == strcmp ("--ignore-environment", argv [i])) {
491 // ignore options set in the environment
496 if (0 == strcmp ("--", argv [i])) {
497 // "--" terminates options, everything
498 // after it is treated as an argument
502 // the name of the option without the leading dash
503 const char* const optname = argv [i] + 1;
505 // look for the first equals sign
506 const char* const eq = strchr (optname, '=');
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);
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) {
517 if ('-' == cmdopts [j].sopt_)
520 if ('-' == argv [i][0]) {
522 const size_t cmplen =
523 eq && cmdopts [j].pcntr_ ? optlen - 1 : optlen;
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_;
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)
535 && optname [0] == cmdopts [j].sopt_
536 && (1 == optlen || cmdopts [j].arg_)) {
538 // matching option has been found
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
549 if (cmdopts [j].ncalls_ && recursive)
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_)
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);
565 assert (0 != cmdopts [j].pcntr_);
567 // obtain the numeric argument
569 const long optval = strtol (eq + 1, &end, 0);
571 if (end && '\0' != *end) {
572 fprintf (stderr, "expected numeric argument: %s\n",
579 #if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
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);
591 #endif // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
594 *cmdopts [j].pcntr_ = optval;
598 assert (0 != cmdopts [j].pcntr_);
599 ++*cmdopts [j].pcntr_;
602 ++cmdopts [j].ncalls_;
605 cmdopts [j].envseen_ = true;
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
621 if (!found && '-' == argv [i][0]) {
623 // invoke the appropriate error handler for an option
624 // that was not found
625 if (_RWSTD_SIZE_MAX != not_found_inx) {
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);
632 // no further processing done
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]);
650 // process options from the environment
651 const char* const envar = getenv ("RWSTD_TESTOPTS");
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) {
664 if (cmdopts [j].inv_ && 0 == cmdopts [j].ncalls_ && 0 == status) {
666 if (cmdopts [j].callback_)
667 status = cmdopts [j].callback_ (0, 0);
669 assert (0 != cmdopts [j].pcntr_);
670 ++*cmdopts [j].pcntr_;
674 cmdopts [j].ncalls_ = 0;
675 cmdopts [j].envseen_ = false;
681 /**************************************************************************/
684 rw_runopts (const char *str)
690 char buf [80]; // fixed size buffer to copy `str' into
691 char *pbuf = buf; // a modifiable copy of `str'
693 size_t len = strlen (str);
694 if (len >= sizeof buf) {
695 // allocate if necessary
696 pbuf = (char*)malloc (len + 1);
701 // copy `str' to modifiable buffer
702 memcpy (pbuf, str, len + 1);
704 char *tmp_argv_buf [32] = { 0 }; // fixed size argv buffer
705 char **argv = tmp_argv_buf; // array of arguments
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
711 int in_quotes = 0; // quoted argument being processed
713 for (char *s = pbuf; *s; ++s) {
715 in_quotes = !in_quotes;
722 // split up unquoted space-separated arguments
723 if (argc == 0 || ' ' == *s) {
727 // skip over leading spaces
728 if (argc > 0 || ' ' == *s)
732 if (argc == argv_size) {
733 // grow `argv' as necessary
734 char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2);
736 if (argv != tmp_argv_buf)
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);
745 // free existing buffer if necessary
746 if (argv != tmp_argv_buf)
749 // reassign buffer and increase size
754 // add argument to array
760 // process `argc' options pointed to by `argv'
761 const int status = rw_runopts (int (argc), argv);
763 // free buffers if necessary
764 if (argv != tmp_argv_buf)