Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
cmdopt.cpp
Go to the documentation of this file.
1
27// expand _TEST_EXPORT macros
28#define _RWSTD_TEST_SRC
29
30#include <rw_cmdopt.h>
31
32#include <ctype.h> // isdigit(), isspace()
33#include <errno.h> // for errno
34#include <stdarg.h> // for va_arg, ...
35#include <stdio.h> // for fprintf
36#include <stdlib.h> // for atexit, free, malloc
37#include <string.h> // for memcpy, strcpy, strcmp, ...
38
39#ifndef EINVAL
40# define EINVAL 22 /* e.g., HP-UX, Linux, Solaris */
41#endif // EINVAL
42
43
45typedef int (optcallback_t)(int, char*[]);
46
48{
49 char loptbuf_ [32]; // buffer for long option name
50 optcallback_t *callback_; // function to call to process option
51
52 // counter to increment for each occurrence of an option
53 // or to set to the numeric argument (when specified)
54 int *pcntr_;
55
56 char *lopt_; // long option name
57 char sopt_; // short option name
58
59 int minval_; // minimum value of a numerical argument
60 int maxval_; // maximum value of a numerical argument
61 size_t maxcount_; // how many times option can be invoked
62 size_t count_; // how many times it has been invoked
63
64 unsigned arg_ : 1; // option takes an argument
65 unsigned inv_ : 1; // callback invocation inverted
66 unsigned tristate_ : 1; // option is a tristate
67 unsigned envseen_ : 1; // environment option already processed
68};
69
70
71// total number of registered options
72static size_t _rw_ncmdopts;
73
74// number of default (always defined) options
75static size_t _rw_ndefopts;
76static cmdopts_t _rw_cmdoptbuf [32];
77static cmdopts_t *_rw_cmdopts = _rw_cmdoptbuf;
78static size_t _rw_optbufsize = sizeof _rw_cmdoptbuf / sizeof *_rw_cmdoptbuf;
79
80
82static int
83_rw_print_help (int argc, char *argv[])
84{
85 if (1 == argc && argv && 0 == argv [0]) {
86 static const char helpstr[] = {
87 "Without an argument, prints this help to stdout and exits with\n"
88 "a status of 0 without further processing.\n"
89 "With the optional argument prints help on the option with that\n"
90 "name, if one exists, and exits with a status of 0. If an option\n"
91 "with the specified name does not exist, prints an error message\n"
92 "to stderr and exits with a status of 1.\n"
93 "The leading underscores in the name of an option are optional.\n"
94 };
95
96 argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
97
98 return 0;
99 }
100
101 // the option name to get help on, if any
102 const char* opthelp = 1 < argc ? argv [1] : 0;
103
104 // remove the optional one or two leading underscores
105 if (opthelp && '-' == opthelp [0] && opthelp [1])
106 opthelp += 1 + ('-' == opthelp [1]);
107
108 if (0 == opthelp)
109 printf ("OPTIONS\n");
110
111 // set to a non-zero when the specified option is found
112 int option_found = 0;
113
114 for (size_t i = 0; i != _rw_ncmdopts; ++i) {
115
116 // for convenience
117 const cmdopts_t* const opt = _rw_cmdopts + i;
118
119 // get a pointer to the name of the long option, if any
120 const char* const lopt = opt->lopt_ ? opt->lopt_ : opt->loptbuf_;
121
122 if (opthelp && *opthelp) {
123
124 if ( (opt->sopt_ == opthelp [0] && '\0' == opthelp [1])
125 || (*lopt && 0 == strcmp (lopt + 1, opthelp))) {
126
127 // remember that we found the option whose (short
128 // or long) name we're to give help on; after printing
129 // the help text on the option keep looping in case
130 // there is another option and callback with the same
131 // name (unlikely but possible)
132 option_found = 1;
133 }
134 else {
135 // the option doesn't match, continue searching
136 continue;
137 }
138 }
139
140 // separate options with help functionality (which typically
141 // produce multiline output) from previous options without
142 // it (and thus very brief output)
143 if (i && 0 == _rw_cmdopts [i - 1].callback_ && opt->callback_)
144 puts ("");
145
146 printf (" ");
147
148 if (opt->sopt_) {
149 printf ("-%c", opt->sopt_);
150
151 if (lopt)
152 printf (" | ");
153 }
154
155 const char *pfx = "";
156 const char *sfx = pfx;
157
158 if (lopt) {
159 printf ("-%s", lopt);
160 if (opt->arg_ && '=' != lopt [strlen (lopt) - 1]) {
161 pfx = " [ ";
162 sfx = " ]";
163 }
164 }
165
166 const char* const arg =
167 _RWSTD_INT_MIN < opt->minval_
168 || opt->maxval_ < _RWSTD_INT_MAX ? "int" : "arg";
169
170 if (opt->arg_) {
171 // print argument (in brackets when it's optional)
172 printf ("%s<%s>%s", pfx, arg, sfx);
173 }
174
175 if (opt->pcntr_)
176 printf (" | -%s=<%s>", lopt, arg);
177
178 if ('i' == *arg) {
179 printf (", with ");
180
181 if (_RWSTD_INT_MIN < opt->minval_)
182 printf ("%d <= ", opt->minval_);
183
184 printf ("<%s>", arg);
185
186 if (opt->maxval_ < _RWSTD_INT_MAX)
187 printf (" <= %d", opt->maxval_);
188
189 printf ("\n ");
190 }
191
192 if (_RWSTD_SIZE_MAX == opt->maxcount_)
193 printf (" (each occurrence evaluated)\n");
194 else if (1 < opt->maxcount_)
195 printf (" (at most %u occurrences evaluated)\n",
196 unsigned (opt->maxcount_));
197 else
198 printf (" (only the first occurrence evaluated)\n");
199
200 // invoke callback with the "--help" option
201 if (opt->callback_) {
202
203 char* help [2] = { 0, 0 };
204
205 opt->callback_ (1, help);
206
207 for (const char *line = help [0]; line; ) {
208
209 const char* const nl = strchr (line, '\n');
210 const int len = nl ? int (nl - line) : int (strlen (line));
211
212 printf (" %.*s\n", len, line);
213
214 line = nl;
215 if (nl)
216 ++line;
217 }
218 }
219 }
220
221 if (opthelp && !option_found) {
222 fprintf (stderr, "Unknown option \"%s\".\n", opthelp);
223 exit (1);
224 }
225
226 exit (0);
227
228 return 0;
229}
230
231
233static int
234_rw_set_ignenv (int argc, char *argv[])
235{
236 if (1 == argc && argv && 0 == argv [0]) {
237 static const char helpstr[] = {
238 "Prevents options specified in the RWSTD_TESTOPTS environment\n"
239 "variable from taking effect.\n"
240 "Unless this option is specified, the RWSTD_TESTOPTS environment\n"
241 "variable is processed as if its value were specified on the \n"
242 "command line.\n"
243 "For example, setting the value of the variable to the string\n"
244 "\"--verbose --no-wchar\" and invoking this program with no\n"
245 "command line arguments will have the same effect as invoking\n"
246 "it with the two arguments on the command line.\n"
247 };
248
249 argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
250
251 return 0;
252 }
253
254 return 0;
255}
256
257extern "C" {
258
259static void
260_rw_clear_opts ()
261{
262 // reset all options, deallocating dynamically allocated storage
263
264 for (size_t i = 0; i != _rw_ncmdopts; ++i) {
265
266 // free any storage allocated for the option name
267 free (_rw_cmdopts [i].lopt_);
268 }
269
270 if (_rw_cmdopts != _rw_cmdoptbuf) {
271 // free the storage allocated for all the options
272 free (_rw_cmdopts);
273 }
274
275 // reset the options pointer to point at the statically
276 // allocated buffer and the count back to 0
277 _rw_ncmdopts = 0;
278 _rw_cmdopts = _rw_cmdoptbuf;
279 _rw_optbufsize = sizeof _rw_cmdoptbuf / sizeof *_rw_cmdoptbuf;
280}
281
282}
283
284static void
285_rw_set_myopts ()
286{
287 static int cleanup_handler_registered;
288
289 if (0 == cleanup_handler_registered) {
290 atexit (_rw_clear_opts);
291 cleanup_handler_registered = 1;
292 }
293
294 if (0 != _rw_ncmdopts)
295 return;
296
297 static int recursive;
298
299 if (recursive)
300 return;
301
302 ++recursive;
303
304 rw_setopts (""
305 "|-help: " // argument optional
306 "|-ignenv ",
307 _rw_print_help,
308 _rw_set_ignenv);
309
310 _rw_ndefopts = _rw_ncmdopts;
311
312 recursive = 0;
313}
314
315
317#ifndef _RWSTD_NO_VA_LIST_ARRAY
318 // va_list is an array type
319# define RW_VA_LIST_ARG_PTR va_list
320# define RW_VA_LIST_ARG_TO_PTR(va) va
321# define RW_VA_LIST_PTR_ARG_TO_VA_LIST(va) va
322#else // if defined (_RWSTD_NO_VA_LIST_ARRAY)
323 // va_list is an object type
324# define RW_VA_LIST_ARG_PTR va_list*
325# define RW_VA_LIST_ARG_TO_PTR(va) &va
326# define RW_VA_LIST_PTR_ARG_TO_VA_LIST(pva) *pva
327#endif // _RWSTD_NO_VA_LIST_ARRAY
328
329
330static const char*
331_rw_getbounds (const char *next, char sep, RW_VA_LIST_ARG_PTR pva)
332{
333 ++next;
334
335 if ( '*' == *next || '+' == *next || '-' == *next || sep == *next
336 || isdigit (*next)) {
337
338 bool have_maxval = false;
339
340 if (*next == sep && '-' != sep) {
341 have_maxval = true;
342 ++next;
343 }
344
345 char *end = 0;
346
347 // '*' designates an int va_arg argument
348 const long val = '*' == *next ?
349 long (va_arg (RW_VA_LIST_PTR_ARG_TO_VA_LIST (pva), int))
350 : strtol (next, &end, 10);
351
352#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
353 // validate
354 if (val < _RWSTD_INT_MIN || _RWSTD_INT_MAX < val) {
355 fprintf (stderr,
356 "lower bound %ld out of range [%d, %d]: "
357 "%s\n", val, _RWSTD_INT_MIN, _RWSTD_INT_MAX,
358 next);
359 return 0;
360 }
361#endif // INT_SIZE < LONG_SIZE
362
363 next = end ? end : next + 1;
364
365 if (have_maxval) {
366 _rw_cmdopts [_rw_ncmdopts].minval_ = 0;
367 _rw_cmdopts [_rw_ncmdopts].maxval_ = int (val);
368 }
369 else
370 _rw_cmdopts [_rw_ncmdopts].minval_ = int (val);
371
372 if (sep == *next && !have_maxval) {
373 ++next;
374
375 if ( '*' == *next
376 || '+' == *next
377 || (val < 0 && '-' == *next)
378 || isdigit (*next)) {
379
380 end = 0;
381
382 // '*' designates an int va_arg argument
383 const long maxval = '*' == *next ?
384 long (va_arg (RW_VA_LIST_PTR_ARG_TO_VA_LIST (pva), int))
385 : strtol (next, &end, 10);
386
387#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
388 // validate
389 if (maxval < _RWSTD_INT_MIN || _RWSTD_INT_MAX < maxval) {
390
391 fprintf (stderr,
392 "upper bound %ld out of range [%ld, %d]: "
393 "%s\n", maxval, val, _RWSTD_INT_MAX,
394 next);
395 return 0;
396 }
397#endif // INT_SIZE < LONG_SIZE
398
399 if ('-' == sep && maxval < val) {
400 // invalid range (upper bound < lower bound
401 fprintf (stderr,
402 "invalid range [%ld, %ld]: %s\n",
403 val, maxval, next);
404 return 0;
405 }
406
407 next = end ? end : next + 1;
408 _rw_cmdopts [_rw_ncmdopts].maxval_ = int (maxval);
409 }
410 else {
411 // syntax error in range
412 fprintf (stderr,
413 "syntax error in range specification: %s\n",
414 next);
415 return 0;
416 }
417 }
418 else if (*next && !isspace (*next)) {
419 // syntax error in numeric argument
420 fprintf (stderr,
421 "syntax error numeric argument: %s\n",
422 next);
423 return 0;
424 }
425 else if (!have_maxval) {
426 // no upper bound on the value of the option argument
427 _rw_cmdopts [_rw_ncmdopts].maxval_ = _RWSTD_INT_MAX;
428 }
429 }
430 else if (':' == sep) {
431 // special value indicating no bits specified for a tristate
432 _rw_cmdopts [_rw_ncmdopts].minval_ = 0;
433 _rw_cmdopts [_rw_ncmdopts].maxval_ = 0;
434 }
435 else {
436 // no minimum/maximum value for this option is set
437 _rw_cmdopts [_rw_ncmdopts].minval_ = _RWSTD_INT_MIN;
438 _rw_cmdopts [_rw_ncmdopts].maxval_ = _RWSTD_INT_MAX;
439 }
440
441 // an unlimited number of occurrences of the option
442 // are allowed and will be counted
443 _rw_cmdopts [_rw_ncmdopts].maxcount_ = _RWSTD_SIZE_MAX;
444
445 return next;
446}
447
448
449_TEST_EXPORT int
450rw_vsetopts (const char *opts, va_list va)
451{
452 if (0 == opts) {
453
454 _rw_clear_opts ();
455 return 0;
456 }
457
458 _rw_set_myopts ();
459
460 const char *next = opts;
461
462 for ( ; ; ++_rw_ncmdopts) {
463
464 while (isspace (*next))
465 ++next;
466
467 if ('\0' == *next) {
468 break;
469 }
470
471 if (_rw_ncmdopts == _rw_optbufsize) {
472
473 const size_t newbufsize = 2 * _rw_ncmdopts + 1;
474
475 cmdopts_t* const newopts =
476 (cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t));
477
478 if (0 == newopts) {
479 fprintf (stderr, "%s%d: failed to allocate memory\n",
480 __FILE__, __LINE__);
481 abort ();
482 }
483
484 memcpy (newopts, _rw_cmdopts, _rw_ncmdopts * sizeof (cmdopts_t));
485
486 if (_rw_cmdopts != _rw_cmdoptbuf)
487 free (_rw_cmdopts);
488
489 _rw_cmdopts = newopts;
490 _rw_optbufsize = newbufsize;
491 }
492
493 // clear the next option info
494 memset (_rw_cmdopts + _rw_ncmdopts, 0, sizeof *_rw_cmdopts);
495
496 // for convenience
497 cmdopts_t* const lastopt = _rw_cmdopts + _rw_ncmdopts;
498
499 lastopt->minval_ = _RWSTD_INT_MIN;
500 lastopt->maxval_ = _RWSTD_INT_MAX;
501
502 if ('|' != *next)
503 lastopt->sopt_ = *next++;
504
505 if ('|' == *next) {
506 const char* end = strpbrk (++next, "|@:=*!#~ ");
507 if (0 == end)
508 end = next + strlen (next);
509
510 // copy the option name up to but not including the delimiter
511 // (except when the delimiter is the equals sign ('='), which
512 // becomes the last character of the option name
513 const size_t optlen = size_t (end - next) + ('=' == *end);
514
515 char *lopt = 0;
516
517 if (optlen < sizeof lastopt->loptbuf_)
518 lopt = lastopt->loptbuf_;
519 else {
520 lopt = (char*)malloc (optlen + 1);
521 if (0 == lopt)
522 return -1; // error
523 lastopt->lopt_ = lopt;
524 }
525
526 memcpy (lopt, next, optlen);
527 lopt [optlen] = '\0';
528
529 next = end;
530 }
531
532 // only the first occurrence of each command line option
533 // causes an invocation of the callback, all subsequent
534 // ones will be ignored by default
535 lastopt->maxcount_ = 1;
536
537 int arg_is_callback = true;
538
539 RW_ASSERT (0 != next);
540
541 if ('#' == *next) {
542 // examples of option specification:
543 // --foo# option takes an optional numeric argument
544 // --foo#3 numeric argument must be equal to 3
545 // --foo#1-5 numeric argument must be in the range [1,5]
546
547 // instead of a pointer to a callback, the argument is a pointer
548 // to an int counter that is to be incremented for each occurrence
549 // of the option during processing
550 // when the option is immediately followed by the equals sign ('=')
551 // and a numeric argument N, the value of N will be stored instead
552 next = _rw_getbounds (next, '-', RW_VA_LIST_ARG_TO_PTR (va));
553
554 if (0 == next)
555 return -1; // error
556
557 arg_is_callback = false;
558
559 }
560 else if ('~' == *next) {
561 // examples of option specification:
562 // --foo~ word 0
563 // --foo~3 word 3
564 // --foo~:4 word 0, bit 2
565 // --foo~3:6 word 3, bits 2 and 3
566
567 // get the optional word number and bit number
568 next = _rw_getbounds (next, ':', RW_VA_LIST_ARG_TO_PTR (va));
569
570 if (0 == next)
571 return -1; // error
572
573 // unlimited number of tristates are allowed
574 lastopt->tristate_ = 1;
575
576 // no callback function expected
577 arg_is_callback = false;
578 }
579 else if (':' == *next || '=' == *next) {
580 // ':' : argument optional
581 // '=' : argument required
582 lastopt->arg_ = true;
583
584 // check if the value of the argument is restricted
585 next = _rw_getbounds (next, '-', RW_VA_LIST_ARG_TO_PTR (va));
586
587 if (0 == next)
588 return -1; // error
589 }
590
591 if ('@' == *next) {
592
593 ++next;
594
595 // at most how many occurrences of an option can be processed?
596 if ('*' == *next) {
597 // unlimited
598 lastopt->maxcount_ = _RWSTD_SIZE_MAX;
599 ++next;
600 }
601 else {
602 // at most this many
603 char *end;
604 lastopt->maxcount_ = strtoul (next, &end, 10);
605 next = end;
606 }
607 }
608 else if ('!' == *next) {
609 lastopt->inv_ = true;
610 ++next;
611 }
612
613 if (arg_is_callback) {
614 // retrieve the callback and verify it's not null
615 // (null callback is permitted in the special case when
616 // the short option is '-', i.e., when setting up or
617 // resetting an "unknown option" handler)
618 lastopt->callback_ = va_arg (va, optcallback_t*);
619 }
620 else {
621 // retrieve the address of the int counter where to keep
622 // track of the number of occurrences of the option, or
623 // where to store the value of the numeric argument of
624 // the option
625 lastopt->pcntr_ = va_arg (va, int*);
626 }
627
628 if ( '-' != lastopt->sopt_
629 && 0 == lastopt->callback_
630 && 0 == lastopt->pcntr_) {
631
632 // get a pointer to the long option name
633 const char* const lopt = lastopt->lopt_
634 ? lastopt->lopt_ : lastopt->loptbuf_;
635
636 if (*lopt)
637 fprintf (stderr, "null handler for option -%s\n", lopt);
638 else
639 fprintf (stderr, "null handler for option -%c\n",
640 lastopt->sopt_);
641
642 abort ();
643 }
644 }
645
646 return int (_rw_ncmdopts - _rw_ndefopts);
647}
648
649
651_TEST_EXPORT int
652rw_setopts (const char *opts, ...)
653{
654 va_list va;
655 va_start (va, opts);
656 const int result = rw_vsetopts (opts, va);
657 va_end (va);
658 return result;
659}
660
661
663// validates (and optionally stores) a restricted numeric option argument
664static int
665_rw_getarg (cmdopts_t *optspec, const char *opt, const char *arg)
666{
667 RW_ASSERT (0 != optspec);
668 RW_ASSERT (0 != arg);
669
670 // obtain the numeric argument
671 char *end = 0;
672 const long optval = strtol (arg, &end, 0);
673
674 int status = 0;
675
676 if (end && '\0' != *end) {
677 fprintf (stderr, "expected numeric argument: %s\n", opt);
678 errno = EINVAL;
679 status = 1;
680 }
681
682#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
683
684 else if (optval < _RWSTD_INT_MIN || _RWSTD_INT_MAX < optval) {
685 fprintf (stderr, "numeric argument %ld out of range"
686 " [%d, %d]: %s\n", optval,
687 _RWSTD_INT_MIN, _RWSTD_INT_MAX, opt);
688 errno = EINVAL;
689 status = 1;
690 }
691
692#endif // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
693
694 else if ( optval < long (optspec->minval_)
695 || long (optspec->maxval_) < optval) {
696
697 // numeric argument out of range
698 fprintf (stderr, "numeric argument %ld out of range"
699 " [%d, %d]: %s\n", optval,
700 optspec->minval_, optspec->maxval_, opt);
701 errno = EINVAL;
702 status = 1;
703 }
704 else if (optspec->pcntr_) {
705
706 // set the counter
707 *optspec->pcntr_ = int (optval);
708 }
709
710 return status;
711}
712
713
715static bool
716_rw_runopt_recursive = false;
717
718
719// processes a single command line option and its arguments
720static int
721_rw_runopt (cmdopts_t *optspec, int argc, char *argv[])
722{
723 RW_ASSERT (0 != optspec);
724 RW_ASSERT (0 != argv);
725
726 // ignore the option if invoked recursively (by processing options
727 // set in the environment) and the option has already been seen
728 // (this prevents duplicate processing of options that are set both
729 // on the command line and in the environment and allows option with
730 // an argument set on the command line to override those set in the
731 // environment)
732
733 if (optspec->count_ && _rw_runopt_recursive)
734 return 0;
735
736 // if the option has been evaluated the maximum number
737 // of times, avoid evaluating it and continue processing
738 if (optspec->maxcount_ <= optspec->count_)
739 return 0;
740
741 int status = 0;
742
743 char* equals = strchr (argv [0], '=');
744
745 if (optspec->callback_) {
746 // option matched the specification
747
748 if (!optspec->inv_) {
749 // option is not inverted (i.e., is processed when seen
750 // on the command line as opposed processed when missing)
751
752 const char* const lopt = optspec->lopt_ ?
753 optspec->lopt_ : optspec->loptbuf_;
754
755 if ( optspec->arg_
756 && ( _RWSTD_INT_MIN < optspec->minval_
757 || optspec->maxval_ < _RWSTD_INT_MAX)) {
758
759 if (equals) {
760 if (strchr (lopt, '=')) {
761 // option requires a restricted numeric argument
762 // retrieve and validate it before invoking the
763 // callback
764
765 status = _rw_getarg (optspec, argv [0], equals + 1);
766 }
767 }
768 else if (1 < argc && '-' != argv [1][0]) {
769
770 const char *arg = argv [1];
771
772 // handle escaped minus (necessary to disambiguate
773 // it from a leading dash that introduces an option)
774 if ('\\' == arg [0] && '-' == arg [1])
775 ++arg;
776
777 status = _rw_getarg (optspec, argv [0], arg);
778 }
779 }
780
781 if (0 == status)
782 status = optspec->callback_ (argc, argv);
783 }
784 }
785 else if (equals) {
786 // option takes an optional numeric argument
787
788 RW_ASSERT (0 != optspec->pcntr_);
789
790 status = _rw_getarg (optspec, argv [0], equals + 1);
791 }
792 else {
793 // option must not be a tristate (those are handled elsewhere)
794 RW_ASSERT (0 == optspec->tristate_);
795 RW_ASSERT (0 != optspec->pcntr_);
796 ++*optspec->pcntr_;
797 }
798
799 ++optspec->count_;
800
801 if (_rw_runopt_recursive)
802 optspec->envseen_ = true;
803
804 return status;
805}
806
807
809// tries to match the option named by optname with the option
810// specification opt and returns a non-zero value on success,
811// 0 otherwise; the returned value is negative when the matched
812// option is being disabled and positive when it's being
813// enabled
814static int
815_rw_match_tristate (const cmdopts_t *opt, const char *optname)
816{
817 RW_ASSERT (0 != opt);
818 RW_ASSERT (0 != optname);
819
820 static const char* const prefix[] = {
821 "+enable", "+use", "+with",
822 "-disable", "-no", "-without",
823 0
824 };
825
826 int tristate = 0;
827
828 if ('-' == optname [0] && '-' != optname [2]) {
829
830 ++optname;
831
832 const size_t optlen = strlen (optname);
833
834 for (size_t i = 0; prefix [i]; ++i) {
835 const char* const pfx = prefix [i] + 1;
836 const size_t pfxlen = strlen (pfx);
837
838 if (pfxlen < optlen && 0 == memcmp (optname, pfx, pfxlen)) {
839 const char* const name = optname + pfxlen;
840
841 // return -1 to disable, +1 to enable, 0 when not found
842 const char* const lopt =
843 opt->lopt_ ? opt->lopt_ : opt->loptbuf_;
844
845 if (0 == strcmp (lopt, name)) {
846 tristate = '+' == prefix [i][0] ? 1 : -1;
847 break;
848 }
849 }
850 }
851 }
852
853 return tristate;
854}
855
856
858// returns a bitmap all of whose bits are set starting with the most
859// significant non-zero bit corresponding to first such bit in val
860// e.g., _rw_set_bits(4) --> 7
861static int
862_rw_set_bits (int val)
863{
864 int res = 0;
865
866 for (unsigned uval = val; uval; uval >>= 1) {
867 res = (res << 1) | 1;
868 }
869
870 return res;
871}
872
873
874_TEST_EXPORT int
875rw_runopts (int argc, char *argv[])
876{
877 _rw_set_myopts ();
878
879 // ignore options set in the environment?
880 int ignenv = _rw_runopt_recursive;
881
882 // return status
883 int status = 0;
884
885 // number of options processed
886 int nopts = 0;
887
888 // pointer to the option whose callback will be invoked
889 // for command line options that do not match any other
890 cmdopts_t* not_found_opt = 0;
891
892 // iterate over the command line arguments until a callback
893 // returns a non-zero value or until all options have been
894 // successfully processed
895 for (int i = 0; i < argc && argv [i] && 0 == status; ++i) {
896
897 if (0 == strcmp ("--ignore-environment", argv [i])) {
898 // ignore options set in the environment
899 ignenv = true;
900 continue;
901 }
902
903 if (0 == strcmp ("--", argv [i])) {
904 // "--" terminates options, everything
905 // after it is treated as an argument
906 break;
907 }
908
909 // the name of the option without the leading dash
910 const char* const optname = argv [i] + 1;
911
912 // the argc and argv to pass to the option handler
913 const int opt_argc = argc - i;
914 char** const opt_argv = argv + i;
915
916 // look for the first equals sign
917 const char* const eq = strchr (optname, '=');
918
919 // compute the length of the option including the equals sign (if any)
920 const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname);
921
922 int found = false;
923
924 // look up each command line option (i.e., a string that starts
925 // with a dash ('-')) and invoke the callback associated with it
926 for (size_t j = 0; j != _rw_ncmdopts; ++j) {
927
928 // for convenience
929 cmdopts_t* const opt = _rw_cmdopts + j;
930
931 if ('-' == opt->sopt_)
932 not_found_opt = opt;
933
934 if ('-' == argv [i][0]) {
935
936 const size_t cmplen =
937 eq && opt->pcntr_ ? optlen - 1 : optlen;
938
939 // get a pointer to the (possibly empty) name
940 // of the long option
941 const char* const lopt = opt->lopt_ ?
942 opt->lopt_ : opt->loptbuf_;
943
944 if (opt->tristate_) {
945 // option specification denotes a tristate, see if it
946 // matches and if so, whether it's being disabled or
947 // enabled (-1 or +1, respectively)
948 const int tristate = _rw_match_tristate (opt, optname);
949
950 if (tristate) {
951 RW_ASSERT (0 != opt->pcntr_);
952
953 const int word_bits = _RWSTD_CHAR_BIT * sizeof (int);
954
955 // the optional minval_ specifies the bit number
956 // of the first of a sequence of bits in an array
957 // stored at pcntr_
958 const int word_inx = opt->minval_ / word_bits;
959 const int bit_inx = opt->minval_ % word_bits;
960
961 const int bit_val = _RWSTD_INT_MAX == opt->maxval_
962 ? 1 : opt->maxval_;
963
964 if (bit_val) {
965 const int bit_mask =
966 _rw_set_bits (bit_val) << bit_inx;
967
968 // clear the bit field
969 opt->pcntr_ [word_inx] &= ~bit_mask;
970
971 if (tristate < 0)
972 opt->pcntr_ [word_inx] = ~(bit_val << bit_inx);
973 else
974 opt->pcntr_ [word_inx] |= bit_val << bit_inx;
975 }
976 else {
977 // special case
978 opt->pcntr_ [word_inx] = tristate < 0 ? -1 : +1;
979 }
980
981 // matching option has been found
982 found = true;
983
984 // increment the number of options processed
985 ++nopts;
986 }
987
988 // avoid ordinary option processing below
989 continue;
990 }
991
992 // try to match the long option first, and only if it
993 // doesn't match try the short single-character option
994 if ( ( cmplen == strlen (lopt)
995 && 0 == memcmp (optname, lopt, cmplen))
996 || ( opt->sopt_
997 && optname [0] == opt->sopt_
998 && (1 == optlen || opt->arg_))) {
999
1000 // matching option has been found
1001 found = true;
1002
1003 // process it and its arguments, if any
1004 status = _rw_runopt (opt, opt_argc, opt_argv);
1005
1006 // increment the number of options processed
1007 // (whether successfully or otherwise)
1008 ++nopts;
1009
1010 if (status) {
1011 // when the status returned from the last callback
1012 // is non-0 stop further processing (including
1013 // any options set in the environment) and return
1014 // status to the caller
1015 ignenv = true;
1016 break;
1017 }
1018 }
1019 }
1020 }
1021
1022 if (!found && '-' == argv [i][0]) {
1023
1024 // invoke the appropriate error handler for an option
1025 // that was not found
1026 if (0 != not_found_opt) {
1027
1028 // invoke the error handler set up through rw_setopts()
1029 // and let the handler decide whether to go on processing
1030 // other options or whether to abort
1031 status = not_found_opt->callback_ (opt_argc, opt_argv);
1032 if (status) {
1033 // no further processing done
1034 ignenv = true;
1035 break;
1036 }
1037 }
1038 else {
1039 // print an error message to stderr when no error
1040 // handler has been set up
1041 fprintf (stderr, "unknown option: %s\n", argv [i]);
1042 ignenv = true;
1043 errno = EINVAL;
1044 status = 1;
1045 break;
1046 }
1047 }
1048 }
1049
1050 if (!ignenv) {
1051 // process options from the environment
1052 const char* const envar = getenv ("RWSTD_TESTOPTS");
1053 if (envar) {
1054 _rw_runopt_recursive = true;
1055 rw_runopts (envar);
1056 _rw_runopt_recursive = false;
1057 }
1058 }
1059
1060 // invoke any inverted callbacks or bump their user-specified counters,
1061 // and reset internal counters indicating if/how many times each option
1062 // has been processed
1063 for (size_t j = 0; j != _rw_ncmdopts; ++j) {
1064
1065 // for convenience
1066 cmdopts_t* const opt = _rw_cmdopts + j;
1067
1068 if (opt->inv_ && 0 == opt->count_ && 0 == status) {
1069
1070 if (opt->callback_)
1071 status = opt->callback_ (0, 0);
1072 else {
1073 RW_ASSERT (0 != opt->pcntr_);
1074 ++*opt->pcntr_;
1075 }
1076 }
1077
1078 opt->count_ = 0;
1079 opt->envseen_ = false;
1080 }
1081
1082 return status;
1083}
1084
1085
1087_TEST_EXPORT int
1088rw_runopts (const char *str)
1089{
1090 RW_ASSERT (0 != str);
1091
1092 _rw_set_myopts ();
1093
1094 char buf [80]; // fixed size buffer to copy `str' into
1095 char *pbuf = buf; // a modifiable copy of `str'
1096
1097 size_t len = strlen (str);
1098 if (len >= sizeof buf) {
1099 // allocate if necessary
1100 pbuf = (char*)malloc (len + 1);
1101 if (!pbuf)
1102 return -1;
1103 }
1104
1105 // copy `str' to modifiable buffer
1106 memcpy (pbuf, str, len + 1);
1107
1108 char *tmp_argv_buf [32] = { 0 }; // fixed size argv buffer
1109 char **argv = tmp_argv_buf; // array of arguments
1110
1111 // initial size of argument array (will grow as necessary)
1112 size_t argv_size = sizeof tmp_argv_buf / sizeof *tmp_argv_buf;
1113 size_t argc = 0; // number of arguments in array
1114
1115 int in_quotes = 0; // quoted argument being processed
1116
1117 for (char *s = pbuf; *s; ++s) {
1118 if ('"' == *s) {
1119 in_quotes = !in_quotes;
1120 continue;
1121 }
1122
1123 if (in_quotes)
1124 continue;
1125
1126 // split up unquoted space-separated arguments
1127 if (argc == 0 || isspace (*s)) {
1128 if (argc > 0)
1129 *s = 0;
1130
1131 // skip over leading spaces
1132 if (argc > 0 || isspace (*s))
1133 while (isspace (*++s));
1134
1135 if (*s) {
1136 if (argc == argv_size) {
1137 // grow `argv' as necessary
1138 char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2);
1139 if (!tmp) {
1140 if (argv != tmp_argv_buf)
1141 free (argv);
1142 return -1;
1143 }
1144
1145 // copy existing elementes and zero out any new entries
1146 memcpy (tmp, argv, sizeof *tmp * argv_size);
1147 memset (tmp + argv_size, 0, sizeof *tmp * argv_size);
1148
1149 // free existing buffer if necessary
1150 if (argv != tmp_argv_buf)
1151 free (argv);
1152
1153 // reassign buffer and increase size
1154 argv = tmp;
1155 argv_size *= 2;
1156 }
1157
1158 // add argument to array
1159 argv [argc++] = s;
1160 }
1161 }
1162 }
1163
1164 // process `argc' options pointed to by `argv'
1165 const int status = rw_runopts (int (argc), argv);
1166
1167 // free buffers if necessary
1168 if (argv != tmp_argv_buf)
1169 free (argv);
1170
1171 if (pbuf != buf)
1172 free (pbuf);
1173
1174 return status;
1175}
#define i
Definition 17.names.cpp:126
#define found
Definition 17.names.cpp:470
#define line
Definition 17.names.cpp:490
#define res
Definition 17.names.cpp:531
#define buf
Definition 17.names.cpp:452
#define j
Definition 17.names.cpp:127
#define tmp
Definition 17.names.cpp:553
#define s
#define len
Definition 17.names.cpp:488
#define val
Definition 17.names.cpp:570
volatile int result
const int end
IMPORT_C SInt32 printf(const Char *,...)
IMPORT_C SInt32 atexit(Void(*function)(Void))
#define va_arg(va, T)
#define va_end(ignore)
#define va_start(va, name)
#define stderr
#define _RWSTD_CONST_CAST(T, x)
Definition _defs.h:536
const char * name
Definition codecvt1.cpp:72
const char * str
Definition exec.cpp:170
#define _TEST_EXPORT
Definition rw_testdefs.h:55
#define RW_ASSERT(expr)
unsigned arg_
Definition cmdopt.cpp:64
int minval_
Definition cmdopt.cpp:59
int * pcntr_
Definition cmdopt.cpp:54
char * lopt_
Definition cmdopt.cpp:56
unsigned tristate_
Definition cmdopt.cpp:66
size_t maxcount_
Definition cmdopt.cpp:61
unsigned envseen_
Definition cmdopt.cpp:67
unsigned inv_
Definition cmdopt.cpp:65
size_t count_
Definition cmdopt.cpp:62
optcallback_t * callback_
Definition cmdopt.cpp:50
int maxval_
Definition cmdopt.cpp:60
char loptbuf_[32]
Definition cmdopt.cpp:49
char sopt_
Definition cmdopt.cpp:57
long prefix(const entry_type &entry)
Definition tele.cpp:58
#define RW_VA_LIST_PTR_ARG_TO_VA_LIST(va)
Definition cmdopt.cpp:321
#define EINVAL
Definition cmdopt.cpp:40
_TEST_EXPORT int rw_vsetopts(const char *opts, va_list va)
Definition cmdopt.cpp:450
_TEST_EXPORT int rw_setopts(const char *opts,...)
Definition cmdopt.cpp:652
#define RW_VA_LIST_ARG_TO_PTR(va)
Definition cmdopt.cpp:320
int() optcallback_t(int, char *[])
Definition cmdopt.cpp:45
#define RW_VA_LIST_ARG_PTR
Definition cmdopt.cpp:319
_TEST_EXPORT int rw_runopts(int argc, char *argv[])
Definition cmdopt.cpp:875
int arg
Definition locale.cpp:2806