Fix compilation with old g++ 3.3.5 and debian-sarge.
[wvstreams.git] / utils / wvargs.cc
bloba408358ce32ca3052f84e103ab4071bded0e7619
1 /* -*- Mode: C++ -*-
2 * Copyright (C) 2004-2005 Net Integration Technologies, Inc.
4 * WvStreams interface for command-line argument processing
5 */
7 #include "wvargs.h"
8 #include "wvscatterhash.h"
10 // Some screwy defines that show up in _WIN32 and cause problems
11 #undef error_t
12 #undef __error_t_defined
13 #undef argc
14 #undef argv
15 #undef __argc
16 #undef __argv
18 #include <argp.h>
21 class WvArgsOption
23 public:
25 int short_option;
26 WvString long_option;
27 WvString desc;
29 WvArgsOption(int _short_option,
30 WvStringParm _long_option,
31 WvStringParm _desc)
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);
52 class WvArgsData
54 public:
55 WvArgsData();
56 ~WvArgsData();
58 argp_option *argp() const;
59 void *self() const;
61 void add(WvArgsOption *option);
62 void remove(char short_option, WvStringParm long_option);
63 void zap();
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);
71 unsigned int flags;
73 protected:
74 friend class WvArgsOption;
75 friend class WvArgsArgOption;
76 friend class WvArgs;
78 void argp_build();
79 bool argp_add(const char *name, int key, const char *arg, int flags,
80 const char *doc, int group);
81 private:
82 void argp_init(size_t size = 0);
84 bool argp_add(const argp_option &option);
85 bool argp_double();
87 argp_option *argp_;
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
115 public:
117 WvArgsNoArgOption(int _short_option,
118 WvStringParm _long_option,
119 WvStringParm _desc)
120 : WvArgsOption(_short_option, _long_option, _desc)
126 class WvArgsSetBoolOption : public WvArgsNoArgOption
129 private:
131 bool &flag;
133 public:
135 WvArgsSetBoolOption(int _short_option,
136 WvStringParm _long_option,
137 WvStringParm _desc,
138 bool &_flag)
139 : WvArgsNoArgOption(_short_option, _long_option, _desc),
140 flag(_flag)
144 virtual WvString process(WvStringParm arg)
146 flag = true;
147 return WvString::null;
152 class WvArgsResetBoolOption : public WvArgsNoArgOption
155 private:
157 bool &flag;
159 public:
161 WvArgsResetBoolOption(int _short_option,
162 WvStringParm _long_option,
163 WvStringParm _desc,
164 bool &_flag)
165 : WvArgsNoArgOption(_short_option, _long_option, _desc),
166 flag(_flag)
170 virtual WvString process(WvStringParm arg)
172 flag = false;
173 return WvString::null;
178 class WvArgsFlipBoolOption : public WvArgsNoArgOption
181 private:
183 bool &flag;
185 public:
187 WvArgsFlipBoolOption(int _short_option,
188 WvStringParm _long_option,
189 WvStringParm _desc,
190 bool &_flag)
191 : WvArgsNoArgOption(_short_option, _long_option, _desc),
192 flag(_flag)
196 virtual WvString process(WvStringParm arg)
198 flag = !flag;
199 return WvString::null;
204 class WvArgsIncIntOption : public WvArgsNoArgOption
206 private:
207 int &val;
209 public:
210 WvArgsIncIntOption(int _short_option,
211 WvStringParm _long_option,
212 WvStringParm _desc,
213 int &_val)
214 : WvArgsNoArgOption(_short_option, _long_option, _desc),
215 val(_val)
219 virtual WvString process(WvStringParm arg)
221 val++;
222 return WvString::null;
227 class WvArgsNoArgCallbackOption : public WvArgsNoArgOption
230 private:
232 WvArgs::NoArgCallback cb;
233 void *ud;
235 public:
237 WvArgsNoArgCallbackOption(int _short_option,
238 WvStringParm _long_option,
239 WvStringParm _desc,
240 WvArgs::NoArgCallback _cb,
241 void *_ud)
242 : WvArgsNoArgOption(_short_option, _long_option, _desc),
243 cb(_cb), ud(_ud)
247 virtual WvString process(WvStringParm arg)
249 if (cb(ud))
250 return WvString::null;
251 else
252 return WvString("invalid option `%s'", arg);
257 class WvArgsArgOption : public WvArgsOption
259 private:
261 WvString arg_desc;
263 public:
265 WvArgsArgOption(int _short_option,
266 WvStringParm _long_option,
267 WvStringParm _desc,
268 WvStringParm _arg_desc)
269 : WvArgsOption(_short_option, _long_option, _desc),
270 arg_desc(_arg_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
283 private:
285 int &val;
287 public:
289 WvArgsIntOption(int _short_option,
290 WvStringParm _long_option,
291 WvStringParm _desc,
292 WvStringParm _arg_desc,
293 int &_val)
294 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
295 val(_val)
299 virtual WvString process(WvStringParm arg)
301 char *tailptr = NULL;
302 errno = 0;
303 long int tmp = strtol(arg, &tailptr, 10);
304 if (errno == ERANGE || tmp > INT_MAX || tmp < INT_MIN )
306 // Out of range
307 return WvString("`%s': invalid number.", arg);
309 else if (*tailptr)
311 // Invalid number
312 return WvString("`%s': invalid number.", arg);
314 else
316 val = tmp;
317 return WvString::null;
323 class WvArgsLongOption : public WvArgsArgOption
325 private:
327 long &val;
329 public:
331 WvArgsLongOption(int _short_option,
332 WvStringParm _long_option,
333 WvStringParm _desc,
334 WvStringParm _arg_desc,
335 long &_val)
336 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
337 val(_val)
341 virtual WvString process(WvStringParm arg)
343 char *tailptr = NULL;
344 errno = 0;
345 long int tmp = strtol(arg, &tailptr, 10);
346 if (errno == ERANGE)
348 // Out of range
349 return WvString("`%s': invalid number.", arg);
351 else if (*tailptr)
353 // Invalid number
354 return WvString("`%s': invalid number.", arg);
356 else
358 val = tmp;
359 return WvString::null;
365 class WvArgsFloatOption : public WvArgsArgOption
367 private:
369 float &val;
371 public:
373 WvArgsFloatOption(int _short_option,
374 WvStringParm _long_option,
375 WvStringParm _desc,
376 WvStringParm _arg_desc,
377 float &_val)
378 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
379 val(_val)
383 virtual WvString process(WvStringParm arg)
385 char *tailptr = NULL;
386 errno = 0;
387 float tmp = strtof(arg, &tailptr);
388 if (errno == ERANGE)
390 // Out of range
391 return WvString("`%s': invalid number.", arg);
393 else if (*tailptr)
395 // Invalid number
396 return WvString("`%s': invalid number.", arg);
398 else
400 val = tmp;
401 return WvString::null;
407 class WvArgsDoubleOption : public WvArgsArgOption
409 private:
411 double &val;
413 public:
415 WvArgsDoubleOption(int _short_option,
416 WvStringParm _long_option,
417 WvStringParm _desc,
418 WvStringParm _arg_desc,
419 double &_val)
420 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
421 val(_val)
425 virtual WvString process(WvStringParm arg)
427 char *tailptr = NULL;
428 errno = 0;
429 double tmp = strtod(arg, &tailptr);
430 if (errno == ERANGE)
432 // Out of range
433 return WvString("`%s': invalid number.", arg);
435 else if (*tailptr)
437 // Invalid number
438 return WvString("`%s': invalid number.", arg);
440 else
442 val = tmp;
443 return WvString::null;
449 class WvArgsStringOption : public WvArgsArgOption
451 private:
453 WvString &val;
455 public:
457 WvArgsStringOption(int _short_option,
458 WvStringParm _long_option,
459 WvStringParm _desc,
460 WvStringParm _arg_desc,
461 WvString &_val)
462 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
463 val(_val)
467 virtual WvString process(WvStringParm arg)
469 val = arg;
470 return WvString::null;
475 class WvArgsStringListAppendOption : public WvArgsArgOption
477 private:
479 WvStringList &val;
481 public:
483 WvArgsStringListAppendOption(int _short_option,
484 WvStringParm _long_option,
485 WvStringParm _desc,
486 WvStringParm _arg_desc,
487 WvStringList &_val)
488 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
489 val(_val)
493 virtual WvString process(WvStringParm arg)
495 val.append(arg);
496 return WvString::null;
501 class WvArgsArgCallbackOption : public WvArgsArgOption
503 private:
505 WvArgs::ArgCallback cb;
506 void *ud;
508 public:
510 WvArgsArgCallbackOption(int _short_option,
511 WvStringParm _long_option,
512 WvStringParm _desc,
513 WvStringParm _arg_desc,
514 WvArgs::ArgCallback _cb,
515 void *_ud)
516 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
517 cb(_cb), ud(_ud)
521 virtual WvString process(WvStringParm arg)
523 if (cb(arg, ud))
524 return WvString::null;
525 else
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()
540 if (argp_)
541 free(argp_);
545 argp_option *WvArgsData::argp() const
547 return argp_;
551 void *WvArgsData::self() const
553 return (void *)this;
557 void WvArgsData::add(WvArgsOption *option)
559 if (!option)
560 return;
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)
585 matches_long = true;
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());
593 i.xunlink();
594 if (argp_)
596 free(argp_);
597 argp_ = NULL;
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()
619 options_dict.zap();
620 options_list.zap();
622 if (argp_)
624 free(argp_);
625 argp_ = NULL;
630 void WvArgsData::argp_init(size_t size)
632 argp_size = size;
633 if (argp_size < 1)
634 argp_size = 1;
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()
645 if (!argp_)
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))
658 if (!argp_double())
659 return false;
662 // Make a copy of the option that we're building.
663 memcpy(argp_ + argp_index, &option, sizeof(argp_option));
664 // Terminate the array.
665 ++argp_index;
666 memset(argp_ + argp_index, 0, sizeof(argp_option));
667 return true;
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))
676 if (!argp_double())
677 return false;
680 // Set the elements.
681 argp_option *option = argp_ + argp_index;
682 option->name = name;
683 option->key = key;
684 option->arg = arg;
685 option->flags = flags;
686 option->doc = doc;
687 option->group = group;
688 // Terminate the array.
689 ++argp_index;
690 memset(argp_ + argp_index, 0, sizeof(argp_option));
691 return true;
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));
699 if (!tmp)
700 return false;
702 argp_ = (argp_option *)tmp;
703 argp_size *= 2;
704 return true;
708 void WvArgsData::add_required_arg()
710 ++required_args;
714 void WvArgsData::subtract_required_arg()
716 --required_args;
720 const WvStringList &WvArgsData::args() const
722 return args_;
726 error_t WvArgsData::parser(int key, char *arg, struct argp_state *state)
728 WvArgsData *data = (WvArgsData *)state->input;
730 switch (key)
732 case ARGP_KEY_ARG:
733 if (state->arg_num >= data->maximum_args)
735 // Too many arguments
736 argp_usage(state);
738 data->args_.append(arg);
739 break;
741 case ARGP_KEY_NO_ARGS:
742 case ARGP_KEY_END:
743 if (state->arg_num < data->required_args)
745 // Too few arguments
746 argp_usage(state);
748 break;
750 default:
751 WvArgsOption *option = data->options_dict[key];
752 if (option)
754 WvString error = option->process(arg);
755 if (!error.isnull())
757 argp_failure(state, argp_err_exit_status, 0,
758 "%s", error.cstr());
759 return EINVAL;
762 else
763 return ARGP_ERR_UNKNOWN;
766 return 0;
770 WvArgs::WvArgs()
771 : data(new WvArgsData())
776 WvArgs::~WvArgs()
778 if (data)
779 delete data;
783 bool WvArgs::process(int argc, char **argv, WvStringList *remaining_args)
785 if (!data->argp())
786 data->argp_build();
788 // Setup --help headers and footers
789 WvString prog_doc;
790 if (header && footer)
791 prog_doc = WvString("%s\v%s", header, footer);
792 else if (header)
793 prog_doc = WvString("%s", header);
794 else if (footer)
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,
802 0, 0, 0 };
804 bool error = argp_parse(&argp, argc, argv, data->flags, 0, data->self());
806 if (remaining_args)
808 remaining_args->zap();
809 WvStringList::Iter i(data->args());
810 for (i.rewind(); i.next(); )
811 remaining_args->add(new WvString(*i), true);
814 return !error;
818 void WvArgs::set_version(WvStringParm version)
820 this->version = version;
824 void WvArgs::set_email(WvStringParm email)
826 this->email = 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,
884 cb, ud));
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,
892 val));
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,
900 val));
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,
908 val));
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,
916 arg_desc, val));
919 void WvArgs::add_option(char short_option, WvStringParm long_option,
920 WvStringParm desc, WvStringParm arg_desc,
921 WvString &val)
923 data->remove(short_option, long_option);
924 data->add(new WvArgsStringOption(short_option, long_option, desc,
925 arg_desc, val));
928 void WvArgs::add_option(char short_option, WvStringParm long_option,
929 WvStringParm desc, WvStringParm arg_desc,
930 WvStringList &val)
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,
943 arg_desc, cb, ud));
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()
961 data->zap();
965 void WvArgs::add_required_arg(WvStringParm desc, bool multiple)
967 data->add_required_arg();
968 if (!!args_doc)
969 args_doc.append(" ");
970 args_doc.append(desc);
971 if (multiple)
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
991 switch (flag)
993 case NO_EXIT_ON_ERRORS:
994 return data->flags & ARGP_NO_EXIT;
995 default:
996 return false;
1001 void WvArgs::set_flag(const flags_t flag, const bool value)
1003 printf("set_flag(%d, %d)\n", flag, value);
1004 unsigned int mask;
1005 switch (flag)
1007 case NO_EXIT_ON_ERRORS:
1008 mask = ARGP_NO_EXIT;
1009 break;
1010 default:
1011 return;
1014 if (value)
1015 data->flags |= mask;
1016 else
1017 data->flags &= ~mask;
1019 printf("set_flag(%d, %d) = %d\n", flag, value, data->flags);