2 * Copyright (C) 2004-2005 Net Integration Technologies, Inc.
4 * WvStreams interface for command-line argument processing
8 #include "wvscatterhash.h"
10 // Some screwy defines that show up in _WIN32 and cause problems
12 #undef __error_t_defined
29 WvArgsOption(int _short_option
,
30 WvStringParm _long_option
,
32 : short_option(_short_option
), long_option(_long_option
), desc(_desc
)
36 virtual ~WvArgsOption()
40 virtual WvString
process(WvStringParm arg
)
42 return WvString::null
;
45 virtual void add_to_argp(WvArgsData
&data
);
49 DeclareWvList(WvArgsOption
);
50 DeclareWvScatterDict(WvArgsOption
, int, short_option
);
58 argp_option
*argp() const;
61 void add(WvArgsOption
*option
);
62 void remove(char short_option
, WvStringParm long_option
);
65 void add_required_arg();
66 void subtract_required_arg();
67 const WvStringList
&args() const;
69 static error_t
parser(int key
, char *arg
, argp_state
*state
);
74 friend class WvArgsOption
;
75 friend class WvArgsArgOption
;
79 bool argp_add(const char *name
, int key
, const char *arg
, int flags
,
80 const char *doc
, int group
);
82 void argp_init(size_t size
= 0);
84 bool argp_add(const argp_option
&option
);
88 size_t argp_index
; // Last element in the options array
89 size_t argp_size
; // Size of the options array
91 // I create two data-structures, only one of them actually owning
92 // the objects, of course. The List is for ordered construction
93 // of argp_. The Dict is for constant-time lookups when
94 // process()ing options.
95 WvArgsOptionList options_list
; // An ordered list of WvArgsOptions
96 WvArgsOptionDict options_dict
; // A constant-time lookup of them
98 WvStringList args_
; // Arguments after all options have been parsed
99 size_t required_args
; // Number of these mandatory arguments.
100 size_t maximum_args
; // Number of maximum arguments.
102 int last_no_key
; // Last key for options with no short_option
106 void WvArgsOption::add_to_argp(WvArgsData
&data
)
108 data
.argp_add(long_option
, short_option
, 0, 0, desc
, 0);
112 class WvArgsNoArgOption
: public WvArgsOption
117 WvArgsNoArgOption(int _short_option
,
118 WvStringParm _long_option
,
120 : WvArgsOption(_short_option
, _long_option
, _desc
)
126 class WvArgsSetBoolOption
: public WvArgsNoArgOption
135 WvArgsSetBoolOption(int _short_option
,
136 WvStringParm _long_option
,
139 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
144 virtual WvString
process(WvStringParm arg
)
147 return WvString::null
;
152 class WvArgsResetBoolOption
: public WvArgsNoArgOption
161 WvArgsResetBoolOption(int _short_option
,
162 WvStringParm _long_option
,
165 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
170 virtual WvString
process(WvStringParm arg
)
173 return WvString::null
;
178 class WvArgsFlipBoolOption
: public WvArgsNoArgOption
187 WvArgsFlipBoolOption(int _short_option
,
188 WvStringParm _long_option
,
191 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
196 virtual WvString
process(WvStringParm arg
)
199 return WvString::null
;
204 class WvArgsIncIntOption
: public WvArgsNoArgOption
210 WvArgsIncIntOption(int _short_option
,
211 WvStringParm _long_option
,
214 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
219 virtual WvString
process(WvStringParm arg
)
222 return WvString::null
;
227 class WvArgsNoArgCallbackOption
: public WvArgsNoArgOption
232 WvArgs::NoArgCallback cb
;
237 WvArgsNoArgCallbackOption(int _short_option
,
238 WvStringParm _long_option
,
240 WvArgs::NoArgCallback _cb
,
242 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
247 virtual WvString
process(WvStringParm arg
)
250 return WvString::null
;
252 return WvString("invalid option `%s'", arg
);
257 class WvArgsArgOption
: public WvArgsOption
265 WvArgsArgOption(int _short_option
,
266 WvStringParm _long_option
,
268 WvStringParm _arg_desc
)
269 : WvArgsOption(_short_option
, _long_option
, _desc
),
274 virtual void add_to_argp(WvArgsData
&data
)
276 data
.argp_add(long_option
, short_option
, arg_desc
, 0, desc
, 0);
281 class WvArgsIntOption
: public WvArgsArgOption
289 WvArgsIntOption(int _short_option
,
290 WvStringParm _long_option
,
292 WvStringParm _arg_desc
,
294 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
299 virtual WvString
process(WvStringParm arg
)
301 char *tailptr
= NULL
;
303 long int tmp
= strtol(arg
, &tailptr
, 10);
304 if (errno
== ERANGE
|| tmp
> INT_MAX
|| tmp
< INT_MIN
)
307 return WvString("`%s': invalid number.", arg
);
312 return WvString("`%s': invalid number.", arg
);
317 return WvString::null
;
323 class WvArgsLongOption
: public WvArgsArgOption
331 WvArgsLongOption(int _short_option
,
332 WvStringParm _long_option
,
334 WvStringParm _arg_desc
,
336 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
341 virtual WvString
process(WvStringParm arg
)
343 char *tailptr
= NULL
;
345 long int tmp
= strtol(arg
, &tailptr
, 10);
349 return WvString("`%s': invalid number.", arg
);
354 return WvString("`%s': invalid number.", arg
);
359 return WvString::null
;
365 class WvArgsFloatOption
: public WvArgsArgOption
373 WvArgsFloatOption(int _short_option
,
374 WvStringParm _long_option
,
376 WvStringParm _arg_desc
,
378 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
383 virtual WvString
process(WvStringParm arg
)
385 char *tailptr
= NULL
;
387 float tmp
= strtof(arg
, &tailptr
);
391 return WvString("`%s': invalid number.", arg
);
396 return WvString("`%s': invalid number.", arg
);
401 return WvString::null
;
407 class WvArgsDoubleOption
: public WvArgsArgOption
415 WvArgsDoubleOption(int _short_option
,
416 WvStringParm _long_option
,
418 WvStringParm _arg_desc
,
420 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
425 virtual WvString
process(WvStringParm arg
)
427 char *tailptr
= NULL
;
429 double tmp
= strtod(arg
, &tailptr
);
433 return WvString("`%s': invalid number.", arg
);
438 return WvString("`%s': invalid number.", arg
);
443 return WvString::null
;
449 class WvArgsStringOption
: public WvArgsArgOption
457 WvArgsStringOption(int _short_option
,
458 WvStringParm _long_option
,
460 WvStringParm _arg_desc
,
462 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
467 virtual WvString
process(WvStringParm arg
)
470 return WvString::null
;
475 class WvArgsStringListAppendOption
: public WvArgsArgOption
483 WvArgsStringListAppendOption(int _short_option
,
484 WvStringParm _long_option
,
486 WvStringParm _arg_desc
,
488 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
493 virtual WvString
process(WvStringParm arg
)
496 return WvString::null
;
501 class WvArgsArgCallbackOption
: public WvArgsArgOption
505 WvArgs::ArgCallback cb
;
510 WvArgsArgCallbackOption(int _short_option
,
511 WvStringParm _long_option
,
513 WvStringParm _arg_desc
,
514 WvArgs::ArgCallback _cb
,
516 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
521 virtual WvString
process(WvStringParm arg
)
524 return WvString::null
;
526 return WvString("invalid option: `%s'", arg
);
531 WvArgsData::WvArgsData()
532 : flags(0), argp_(NULL
), argp_index(0), argp_size(0),
533 required_args(0), maximum_args(0), last_no_key(-1)
538 WvArgsData::~WvArgsData()
545 argp_option
*WvArgsData::argp() const
551 void *WvArgsData::self() const
557 void WvArgsData::add(WvArgsOption
*option
)
562 if (!option
->short_option
)
563 option
->short_option
= last_no_key
--;
565 options_list
.append(option
, true);
566 options_dict
.add(option
, false);
570 // This method removes both short_option and long_option from the
571 // options_* structures. Completely.
572 void WvArgsData::remove(char short_option
, WvStringParm long_option
)
574 // First, look through options_list, and remove them from
575 // options_dict once we find them.
576 WvArgsOptionList::Iter
i(options_list
);
577 for (i
.rewind(); i
.next(); )
579 bool matches_short
= false;
580 bool matches_long
= false;
582 if (short_option
!= '\0' && i
->short_option
== short_option
)
583 matches_short
= true;
584 if (!long_option
.isnull() && i
->long_option
== long_option
)
587 if (matches_short
&& matches_long
588 || matches_short
&& i
->long_option
.isnull()
589 || matches_long
&& i
->short_option
== '\0')
591 // Delete this item from the data-structures
592 options_dict
.remove(i
.ptr());
600 else if (matches_short
)
602 // Update the short description and change how it's filed
603 // in the dictionary.
604 i
->short_option
= '\0';
605 options_dict
.remove(i
.ptr());
606 options_dict
.add(i
.ptr(), false);
608 else if (matches_long
)
610 // Update the long description only
611 i
->long_option
= WvString::null
;
617 void WvArgsData::zap()
630 void WvArgsData::argp_init(size_t size
)
636 // I'm sorry to use malloc(), but this argp is a C library
637 argp_
= (argp_option
*)malloc(argp_size
* sizeof(argp_option
));
638 // Terminate the empty array
639 memset(argp_
, 0, sizeof(argp_option
));
643 void WvArgsData::argp_build()
646 argp_init(options_list
.count() + 2);
648 WvArgsOptionList::Iter
i(options_list
);
649 for (i
.rewind(); i
.next(); )
650 i
->add_to_argp(*this);
654 bool WvArgsData::argp_add(const argp_option
&option
)
656 if (argp_index
>= (argp_size
- 1))
662 // Make a copy of the option that we're building.
663 memcpy(argp_
+ argp_index
, &option
, sizeof(argp_option
));
664 // Terminate the array.
666 memset(argp_
+ argp_index
, 0, sizeof(argp_option
));
671 bool WvArgsData::argp_add(const char *name
, int key
, const char *arg
,
672 int flags
, const char *doc
, int group
)
674 if (argp_index
>= (argp_size
- 1))
681 argp_option
*option
= argp_
+ argp_index
;
685 option
->flags
= flags
;
687 option
->group
= group
;
688 // Terminate the array.
690 memset(argp_
+ argp_index
, 0, sizeof(argp_option
));
695 bool WvArgsData::argp_double()
697 // We won't be able to fit the next entry into the array
698 void *tmp
= realloc(argp_
, 2 * argp_size
* sizeof(argp_option
));
702 argp_
= (argp_option
*)tmp
;
708 void WvArgsData::add_required_arg()
714 void WvArgsData::subtract_required_arg()
720 const WvStringList
&WvArgsData::args() const
726 error_t
WvArgsData::parser(int key
, char *arg
, struct argp_state
*state
)
728 WvArgsData
*data
= (WvArgsData
*)state
->input
;
733 if (state
->arg_num
>= data
->maximum_args
)
735 // Too many arguments
738 data
->args_
.append(arg
);
741 case ARGP_KEY_NO_ARGS
:
743 if (state
->arg_num
< data
->required_args
)
751 WvArgsOption
*option
= data
->options_dict
[key
];
754 WvString error
= option
->process(arg
);
757 argp_failure(state
, argp_err_exit_status
, 0,
763 return ARGP_ERR_UNKNOWN
;
771 : data(new WvArgsData())
783 bool WvArgs::process(int argc
, char **argv
, WvStringList
*remaining_args
)
788 // Setup --help headers and footers
790 if (header
&& footer
)
791 prog_doc
= WvString("%s\v%s", header
, footer
);
793 prog_doc
= WvString("%s", header
);
795 prog_doc
= WvString(" \v%s", footer
);
797 // Setup the constant version number and e-mail address
798 argp_program_version
= version
;
799 argp_program_bug_address
= email
;
801 struct argp argp
= { data
->argp(), &WvArgsData::parser
, args_doc
, prog_doc
,
804 bool error
= argp_parse(&argp
, argc
, argv
, data
->flags
, 0, data
->self());
808 remaining_args
->zap();
809 WvStringList::Iter
i(data
->args());
810 for (i
.rewind(); i
.next(); )
811 remaining_args
->add(new WvString(*i
), true);
818 void WvArgs::set_version(WvStringParm version
)
820 this->version
= version
;
824 void WvArgs::set_email(WvStringParm email
)
830 void WvArgs::set_help_header(WvStringParm header
)
832 this->header
= header
;
836 void WvArgs::set_help_footer(WvStringParm footer
)
838 this->footer
= footer
;
842 void WvArgs::print_usage(int argc
, char **argv
)
844 struct argp argp
= { data
->argp(), 0, 0, 0, 0, 0, 0 };
845 argp_help(&argp
, stdout
, ARGP_HELP_STD_USAGE
, argv
[0]);
849 void WvArgs::print_help(int argc
, char **argv
)
851 struct argp argp
= { data
->argp(), 0, 0, 0, 0, 0, 0 };
852 argp_help(&argp
, stdout
, ARGP_HELP_STD_HELP
, argv
[0]);
855 void WvArgs::add_set_bool_option(char short_option
, WvStringParm long_option
,
856 WvStringParm desc
, bool &val
)
858 data
->remove(short_option
, long_option
);
859 data
->add(new WvArgsSetBoolOption(short_option
, long_option
, desc
, val
));
863 void WvArgs::add_reset_bool_option(char short_option
, WvStringParm long_option
,
864 WvStringParm desc
, bool &val
)
866 data
->remove(short_option
, long_option
);
867 data
->add(new WvArgsResetBoolOption(short_option
, long_option
, desc
, val
));
871 void WvArgs::add_flip_bool_option(char short_option
, WvStringParm long_option
,
872 WvStringParm desc
, bool &val
)
874 data
->remove(short_option
, long_option
);
875 data
->add(new WvArgsFlipBoolOption(short_option
, long_option
, desc
, val
));
879 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
880 WvStringParm desc
, NoArgCallback cb
, void *ud
)
882 data
->remove(short_option
, long_option
);
883 data
->add(new WvArgsNoArgCallbackOption(short_option
, long_option
, desc
,
887 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
888 WvStringParm desc
, WvStringParm arg_desc
, int &val
)
890 data
->remove(short_option
, long_option
);
891 data
->add(new WvArgsIntOption(short_option
, long_option
, desc
, arg_desc
,
895 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
896 WvStringParm desc
, WvStringParm arg_desc
, long &val
)
898 data
->remove(short_option
, long_option
);
899 data
->add(new WvArgsLongOption(short_option
, long_option
, desc
, arg_desc
,
903 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
904 WvStringParm desc
, WvStringParm arg_desc
, float &val
)
906 data
->remove(short_option
, long_option
);
907 data
->add(new WvArgsFloatOption(short_option
, long_option
, desc
, arg_desc
,
911 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
912 WvStringParm desc
, WvStringParm arg_desc
, double &val
)
914 data
->remove(short_option
, long_option
);
915 data
->add(new WvArgsDoubleOption(short_option
, long_option
, desc
,
919 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
920 WvStringParm desc
, WvStringParm arg_desc
,
923 data
->remove(short_option
, long_option
);
924 data
->add(new WvArgsStringOption(short_option
, long_option
, desc
,
928 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
929 WvStringParm desc
, WvStringParm arg_desc
,
932 data
->remove(short_option
, long_option
);
933 data
->add(new WvArgsStringListAppendOption(short_option
, long_option
,
934 desc
, arg_desc
, val
));
937 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
938 WvStringParm desc
, WvStringParm arg_desc
,
939 ArgCallback cb
, void *ud
)
941 data
->remove(short_option
, long_option
);
942 data
->add(new WvArgsArgCallbackOption(short_option
, long_option
, desc
,
947 void WvArgs::remove_option(char short_option
)
949 data
->remove(short_option
, WvString::null
);
953 void WvArgs::remove_option(WvStringParm long_option
)
955 data
->remove(0, long_option
);
959 void WvArgs::remove_all_options()
965 void WvArgs::add_required_arg(WvStringParm desc
, bool multiple
)
967 data
->add_required_arg();
969 args_doc
.append(" ");
970 args_doc
.append(desc
);
973 args_doc
.append("...");
974 data
->maximum_args
= LONG_MAX
;
976 else if (data
->maximum_args
< LONG_MAX
)
977 ++(data
->maximum_args
);
981 void WvArgs::add_optional_arg(WvStringParm desc
, bool multiple
)
983 // an optional arg is a required arg without the requirement :-)
984 add_required_arg(WvString("[%s]", desc
), multiple
);
985 data
->subtract_required_arg();
989 bool WvArgs::get_flag(const flags_t flag
) const
993 case NO_EXIT_ON_ERRORS
:
994 return data
->flags
& ARGP_NO_EXIT
;
1001 void WvArgs::set_flag(const flags_t flag
, const bool value
)
1003 printf("set_flag(%d, %d)\n", flag
, value
);
1007 case NO_EXIT_ON_ERRORS
:
1008 mask
= ARGP_NO_EXIT
;
1015 data
->flags
|= mask
;
1017 data
->flags
&= ~mask
;
1019 printf("set_flag(%d, %d) = %d\n", flag
, value
, data
->flags
);