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
30 WvArgsOption(int _short_option
,
31 WvStringParm _long_option
,
33 : short_option(_short_option
), long_option(_long_option
), desc(_desc
)
37 virtual ~WvArgsOption()
41 virtual WvString
process(WvStringParm arg
)
43 return WvString::null
;
46 virtual void add_to_argp(WvArgsData
&data
);
50 DeclareWvList(WvArgsOption
);
51 DeclareWvScatterDict(WvArgsOption
, int, short_option
);
59 argp_option
*argp() const;
62 void add(WvArgsOption
*option
);
63 void remove(char short_option
, WvStringParm long_option
);
66 void add_required_arg();
67 void subtract_required_arg();
68 const WvStringList
&args() const;
70 static error_t
parser(int key
, char *arg
, argp_state
*state
);
75 friend class WvArgsOption
;
76 friend class WvArgsArgOption
;
80 bool argp_add(const char *name
, int key
, const char *arg
, int flags
,
81 const char *doc
, int group
);
83 void argp_init(size_t size
= 0);
85 bool argp_add(const argp_option
&option
);
89 size_t argp_index
; // Last element in the options array
90 size_t argp_size
; // Size of the options array
92 // I create two data-structures, only one of them actually owning
93 // the objects, of course. The List is for ordered construction
94 // of argp_. The Dict is for constant-time lookups when
95 // process()ing options.
96 WvArgsOptionList options_list
; // An ordered list of WvArgsOptions
97 WvArgsOptionDict options_dict
; // A constant-time lookup of them
99 WvStringList args_
; // Arguments after all options have been parsed
100 size_t required_args
; // Number of these mandatory arguments.
101 size_t maximum_args
; // Number of maximum arguments.
103 int last_no_key
; // Last key for options with no short_option
107 void WvArgsOption::add_to_argp(WvArgsData
&data
)
109 data
.argp_add(long_option
, short_option
, 0, 0, desc
, 0);
113 class WvArgsNoArgOption
: public WvArgsOption
118 WvArgsNoArgOption(int _short_option
,
119 WvStringParm _long_option
,
121 : WvArgsOption(_short_option
, _long_option
, _desc
)
127 class WvArgsSetBoolOption
: public WvArgsNoArgOption
136 WvArgsSetBoolOption(int _short_option
,
137 WvStringParm _long_option
,
140 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
145 virtual WvString
process(WvStringParm arg
)
148 return WvString::null
;
153 class WvArgsResetBoolOption
: public WvArgsNoArgOption
162 WvArgsResetBoolOption(int _short_option
,
163 WvStringParm _long_option
,
166 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
171 virtual WvString
process(WvStringParm arg
)
174 return WvString::null
;
179 class WvArgsFlipBoolOption
: public WvArgsNoArgOption
188 WvArgsFlipBoolOption(int _short_option
,
189 WvStringParm _long_option
,
192 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
197 virtual WvString
process(WvStringParm arg
)
200 return WvString::null
;
205 class WvArgsIncIntOption
: public WvArgsNoArgOption
211 WvArgsIncIntOption(int _short_option
,
212 WvStringParm _long_option
,
215 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
220 virtual WvString
process(WvStringParm arg
)
223 return WvString::null
;
228 class WvArgsNoArgCallbackOption
: public WvArgsNoArgOption
233 WvArgs::NoArgCallback cb
;
238 WvArgsNoArgCallbackOption(int _short_option
,
239 WvStringParm _long_option
,
241 WvArgs::NoArgCallback _cb
,
243 : WvArgsNoArgOption(_short_option
, _long_option
, _desc
),
248 virtual WvString
process(WvStringParm arg
)
251 return WvString::null
;
253 return WvString("invalid option `%s'", arg
);
258 class WvArgsArgOption
: public WvArgsOption
266 WvArgsArgOption(int _short_option
,
267 WvStringParm _long_option
,
269 WvStringParm _arg_desc
)
270 : WvArgsOption(_short_option
, _long_option
, _desc
),
275 virtual void add_to_argp(WvArgsData
&data
)
277 data
.argp_add(long_option
, short_option
, arg_desc
, 0, desc
, 0);
282 class WvArgsIntOption
: public WvArgsArgOption
290 WvArgsIntOption(int _short_option
,
291 WvStringParm _long_option
,
293 WvStringParm _arg_desc
,
295 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
300 virtual WvString
process(WvStringParm arg
)
302 char *tailptr
= NULL
;
304 long int tmp
= strtol(arg
, &tailptr
, 10);
305 if (errno
== ERANGE
|| tmp
> INT_MAX
|| tmp
< INT_MIN
)
308 return WvString("`%s': invalid number.", arg
);
313 return WvString("`%s': invalid number.", arg
);
318 return WvString::null
;
324 class WvArgsLongOption
: public WvArgsArgOption
332 WvArgsLongOption(int _short_option
,
333 WvStringParm _long_option
,
335 WvStringParm _arg_desc
,
337 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
342 virtual WvString
process(WvStringParm arg
)
344 char *tailptr
= NULL
;
346 long int tmp
= strtol(arg
, &tailptr
, 10);
350 return WvString("`%s': invalid number.", arg
);
355 return WvString("`%s': invalid number.", arg
);
360 return WvString::null
;
366 class WvArgsFloatOption
: public WvArgsArgOption
374 WvArgsFloatOption(int _short_option
,
375 WvStringParm _long_option
,
377 WvStringParm _arg_desc
,
379 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
384 virtual WvString
process(WvStringParm arg
)
386 char *tailptr
= NULL
;
388 float tmp
= strtof(arg
, &tailptr
);
392 return WvString("`%s': invalid number.", arg
);
397 return WvString("`%s': invalid number.", arg
);
402 return WvString::null
;
408 class WvArgsDoubleOption
: public WvArgsArgOption
416 WvArgsDoubleOption(int _short_option
,
417 WvStringParm _long_option
,
419 WvStringParm _arg_desc
,
421 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
426 virtual WvString
process(WvStringParm arg
)
428 char *tailptr
= NULL
;
430 double tmp
= strtod(arg
, &tailptr
);
434 return WvString("`%s': invalid number.", arg
);
439 return WvString("`%s': invalid number.", arg
);
444 return WvString::null
;
450 class WvArgsStringOption
: public WvArgsArgOption
458 WvArgsStringOption(int _short_option
,
459 WvStringParm _long_option
,
461 WvStringParm _arg_desc
,
463 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
468 virtual WvString
process(WvStringParm arg
)
471 return WvString::null
;
476 class WvArgsStringListAppendOption
: public WvArgsArgOption
484 WvArgsStringListAppendOption(int _short_option
,
485 WvStringParm _long_option
,
487 WvStringParm _arg_desc
,
489 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
494 virtual WvString
process(WvStringParm arg
)
497 return WvString::null
;
502 class WvArgsArgCallbackOption
: public WvArgsArgOption
506 WvArgs::ArgCallback cb
;
511 WvArgsArgCallbackOption(int _short_option
,
512 WvStringParm _long_option
,
514 WvStringParm _arg_desc
,
515 WvArgs::ArgCallback _cb
,
517 : WvArgsArgOption(_short_option
, _long_option
, _desc
, _arg_desc
),
522 virtual WvString
process(WvStringParm arg
)
525 return WvString::null
;
527 return WvString("invalid option: `%s'", arg
);
532 WvArgsData::WvArgsData()
533 : flags(0), argp_(NULL
), argp_index(0), argp_size(0),
534 required_args(0), maximum_args(0), last_no_key(-1)
539 WvArgsData::~WvArgsData()
546 argp_option
*WvArgsData::argp() const
552 void *WvArgsData::self() const
558 void WvArgsData::add(WvArgsOption
*option
)
563 if (!option
->short_option
)
564 option
->short_option
= last_no_key
--;
566 options_list
.append(option
, true);
567 options_dict
.add(option
, false);
571 // This method removes both short_option and long_option from the
572 // options_* structures. Completely.
573 void WvArgsData::remove(char short_option
, WvStringParm long_option
)
575 // First, look through options_list, and remove them from
576 // options_dict once we find them.
577 WvArgsOptionList::Iter
i(options_list
);
578 for (i
.rewind(); i
.next(); )
580 bool matches_short
= false;
581 bool matches_long
= false;
583 if (short_option
!= '\0' && i
->short_option
== short_option
)
584 matches_short
= true;
585 if (!long_option
.isnull() && i
->long_option
== long_option
)
588 if ((matches_short
&& matches_long
)
589 || (matches_short
&& i
->long_option
.isnull())
590 || (matches_long
&& i
->short_option
== '\0'))
592 // Delete this item from the data-structures
593 options_dict
.remove(i
.ptr());
601 else if (matches_short
)
603 // Update the short description and change how it's filed
604 // in the dictionary.
605 i
->short_option
= '\0';
606 options_dict
.remove(i
.ptr());
607 options_dict
.add(i
.ptr(), false);
609 else if (matches_long
)
611 // Update the long description only
612 i
->long_option
= WvString::null
;
618 void WvArgsData::zap()
631 void WvArgsData::argp_init(size_t size
)
637 // I'm sorry to use malloc(), but this argp is a C library
638 argp_
= (argp_option
*)malloc(argp_size
* sizeof(argp_option
));
639 // Terminate the empty array
640 memset(argp_
, 0, sizeof(argp_option
));
644 void WvArgsData::argp_build()
647 argp_init(options_list
.count() + 2);
649 WvArgsOptionList::Iter
i(options_list
);
650 for (i
.rewind(); i
.next(); )
651 i
->add_to_argp(*this);
655 bool WvArgsData::argp_add(const argp_option
&option
)
657 if (argp_index
>= (argp_size
- 1))
663 // Make a copy of the option that we're building.
664 memcpy(argp_
+ argp_index
, &option
, sizeof(argp_option
));
665 // Terminate the array.
667 memset(argp_
+ argp_index
, 0, sizeof(argp_option
));
672 bool WvArgsData::argp_add(const char *name
, int key
, const char *arg
,
673 int flags
, const char *doc
, int group
)
675 if (argp_index
>= (argp_size
- 1))
682 argp_option
*option
= argp_
+ argp_index
;
686 option
->flags
= flags
;
688 option
->group
= group
;
689 // Terminate the array.
691 memset(argp_
+ argp_index
, 0, sizeof(argp_option
));
696 bool WvArgsData::argp_double()
698 // We won't be able to fit the next entry into the array
699 void *tmp
= realloc(argp_
, 2 * argp_size
* sizeof(argp_option
));
703 argp_
= (argp_option
*)tmp
;
709 void WvArgsData::add_required_arg()
715 void WvArgsData::subtract_required_arg()
721 const WvStringList
&WvArgsData::args() const
727 error_t
WvArgsData::parser(int key
, char *arg
, struct argp_state
*state
)
729 WvArgsData
*data
= (WvArgsData
*)state
->input
;
734 if (state
->arg_num
>= data
->maximum_args
)
736 // Too many arguments
739 data
->args_
.append(arg
);
742 case ARGP_KEY_NO_ARGS
:
744 if (state
->arg_num
< data
->required_args
)
752 WvArgsOption
*option
= data
->options_dict
[key
];
755 WvString error
= option
->process(arg
);
758 argp_failure(state
, argp_err_exit_status
, 0,
764 return ARGP_ERR_UNKNOWN
;
772 : data(new WvArgsData())
784 bool WvArgs::process(int argc
, char **argv
, WvStringList
*remaining_args
)
789 // Setup --help headers and footers
791 if (header
&& footer
)
792 prog_doc
= WvString("%s\v%s", header
, footer
);
794 prog_doc
= WvString("%s", header
);
796 prog_doc
= WvString(" \v%s", footer
);
798 // Setup the constant version number and e-mail address
799 argp_program_version
= version
;
800 argp_program_bug_address
= email
;
802 struct argp argp
= { data
->argp(), &WvArgsData::parser
, args_doc
, prog_doc
,
805 bool error
= argp_parse(&argp
, argc
, argv
, data
->flags
, 0, data
->self());
809 remaining_args
->zap();
810 WvStringList::Iter
i(data
->args());
811 for (i
.rewind(); i
.next(); )
812 remaining_args
->add(new WvString(*i
), true);
819 void WvArgs::set_version(WvStringParm version
)
821 this->version
= version
;
825 void WvArgs::set_email(WvStringParm email
)
831 void WvArgs::set_help_header(WvStringParm header
)
833 this->header
= header
;
837 void WvArgs::set_help_footer(WvStringParm footer
)
839 this->footer
= footer
;
843 void WvArgs::print_usage(int argc
, char **argv
)
845 struct argp argp
= { data
->argp(), 0, 0, 0, 0, 0, 0 };
846 argp_help(&argp
, stdout
, ARGP_HELP_STD_USAGE
, argv
[0]);
850 void WvArgs::print_help(int argc
, char **argv
)
852 struct argp argp
= { data
->argp(), 0, 0, 0, 0, 0, 0 };
853 argp_help(&argp
, stdout
, ARGP_HELP_STD_HELP
, argv
[0]);
856 void WvArgs::add_set_bool_option(char short_option
, WvStringParm long_option
,
857 WvStringParm desc
, bool &val
)
859 data
->remove(short_option
, long_option
);
860 data
->add(new WvArgsSetBoolOption(short_option
, long_option
, desc
, val
));
864 void WvArgs::add_reset_bool_option(char short_option
, WvStringParm long_option
,
865 WvStringParm desc
, bool &val
)
867 data
->remove(short_option
, long_option
);
868 data
->add(new WvArgsResetBoolOption(short_option
, long_option
, desc
, val
));
872 void WvArgs::add_flip_bool_option(char short_option
, WvStringParm long_option
,
873 WvStringParm desc
, bool &val
)
875 data
->remove(short_option
, long_option
);
876 data
->add(new WvArgsFlipBoolOption(short_option
, long_option
, desc
, val
));
880 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
881 WvStringParm desc
, NoArgCallback cb
, void *ud
)
883 data
->remove(short_option
, long_option
);
884 data
->add(new WvArgsNoArgCallbackOption(short_option
, long_option
, desc
,
888 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
889 WvStringParm desc
, WvStringParm arg_desc
, int &val
)
891 data
->remove(short_option
, long_option
);
892 data
->add(new WvArgsIntOption(short_option
, long_option
, desc
, arg_desc
,
896 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
897 WvStringParm desc
, WvStringParm arg_desc
, long &val
)
899 data
->remove(short_option
, long_option
);
900 data
->add(new WvArgsLongOption(short_option
, long_option
, desc
, arg_desc
,
904 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
905 WvStringParm desc
, WvStringParm arg_desc
, float &val
)
907 data
->remove(short_option
, long_option
);
908 data
->add(new WvArgsFloatOption(short_option
, long_option
, desc
, arg_desc
,
912 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
913 WvStringParm desc
, WvStringParm arg_desc
, double &val
)
915 data
->remove(short_option
, long_option
);
916 data
->add(new WvArgsDoubleOption(short_option
, long_option
, desc
,
920 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
921 WvStringParm desc
, WvStringParm arg_desc
,
924 data
->remove(short_option
, long_option
);
925 data
->add(new WvArgsStringOption(short_option
, long_option
, desc
,
929 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
930 WvStringParm desc
, WvStringParm arg_desc
,
933 data
->remove(short_option
, long_option
);
934 data
->add(new WvArgsStringListAppendOption(short_option
, long_option
,
935 desc
, arg_desc
, val
));
938 void WvArgs::add_option(char short_option
, WvStringParm long_option
,
939 WvStringParm desc
, WvStringParm arg_desc
,
940 ArgCallback cb
, void *ud
)
942 data
->remove(short_option
, long_option
);
943 data
->add(new WvArgsArgCallbackOption(short_option
, long_option
, desc
,
948 void WvArgs::remove_option(char short_option
)
950 data
->remove(short_option
, WvString::null
);
954 void WvArgs::remove_option(WvStringParm long_option
)
956 data
->remove(0, long_option
);
960 void WvArgs::remove_all_options()
966 void WvArgs::add_required_arg(WvStringParm desc
, bool multiple
)
968 data
->add_required_arg();
970 args_doc
.append(" ");
971 args_doc
.append(desc
);
974 args_doc
.append("...");
975 data
->maximum_args
= LONG_MAX
;
977 else if (data
->maximum_args
< LONG_MAX
)
978 ++(data
->maximum_args
);
982 void WvArgs::add_optional_arg(WvStringParm desc
, bool multiple
)
984 // an optional arg is a required arg without the requirement :-)
985 add_required_arg(WvString("[%s]", desc
), multiple
);
986 data
->subtract_required_arg();
990 bool WvArgs::get_flag(const flags_t flag
) const
994 case NO_EXIT_ON_ERRORS
:
995 return data
->flags
& ARGP_NO_EXIT
;
1002 void WvArgs::set_flag(const flags_t flag
, const bool value
)
1004 printf("set_flag(%d, %d)\n", flag
, value
);
1008 case NO_EXIT_ON_ERRORS
:
1009 mask
= ARGP_NO_EXIT
;
1016 data
->flags
|= mask
;
1018 data
->flags
&= ~mask
;
1020 printf("set_flag(%d, %d) = %d\n", flag
, value
, data
->flags
);