Make WvStreams compile with gcc 4.4.
[wvstreams.git] / utils / wvargs.cc
blob8c6ed26c4aeaa97e9040b4b0a7a022c7909b87c6
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>
19 #include <limits.h>
22 class WvArgsOption
24 public:
26 int short_option;
27 WvString long_option;
28 WvString desc;
30 WvArgsOption(int _short_option,
31 WvStringParm _long_option,
32 WvStringParm _desc)
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);
53 class WvArgsData
55 public:
56 WvArgsData();
57 ~WvArgsData();
59 argp_option *argp() const;
60 void *self() const;
62 void add(WvArgsOption *option);
63 void remove(char short_option, WvStringParm long_option);
64 void zap();
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);
72 unsigned int flags;
74 protected:
75 friend class WvArgsOption;
76 friend class WvArgsArgOption;
77 friend class WvArgs;
79 void argp_build();
80 bool argp_add(const char *name, int key, const char *arg, int flags,
81 const char *doc, int group);
82 private:
83 void argp_init(size_t size = 0);
85 bool argp_add(const argp_option &option);
86 bool argp_double();
88 argp_option *argp_;
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
116 public:
118 WvArgsNoArgOption(int _short_option,
119 WvStringParm _long_option,
120 WvStringParm _desc)
121 : WvArgsOption(_short_option, _long_option, _desc)
127 class WvArgsSetBoolOption : public WvArgsNoArgOption
130 private:
132 bool &flag;
134 public:
136 WvArgsSetBoolOption(int _short_option,
137 WvStringParm _long_option,
138 WvStringParm _desc,
139 bool &_flag)
140 : WvArgsNoArgOption(_short_option, _long_option, _desc),
141 flag(_flag)
145 virtual WvString process(WvStringParm arg)
147 flag = true;
148 return WvString::null;
153 class WvArgsResetBoolOption : public WvArgsNoArgOption
156 private:
158 bool &flag;
160 public:
162 WvArgsResetBoolOption(int _short_option,
163 WvStringParm _long_option,
164 WvStringParm _desc,
165 bool &_flag)
166 : WvArgsNoArgOption(_short_option, _long_option, _desc),
167 flag(_flag)
171 virtual WvString process(WvStringParm arg)
173 flag = false;
174 return WvString::null;
179 class WvArgsFlipBoolOption : public WvArgsNoArgOption
182 private:
184 bool &flag;
186 public:
188 WvArgsFlipBoolOption(int _short_option,
189 WvStringParm _long_option,
190 WvStringParm _desc,
191 bool &_flag)
192 : WvArgsNoArgOption(_short_option, _long_option, _desc),
193 flag(_flag)
197 virtual WvString process(WvStringParm arg)
199 flag = !flag;
200 return WvString::null;
205 class WvArgsIncIntOption : public WvArgsNoArgOption
207 private:
208 int &val;
210 public:
211 WvArgsIncIntOption(int _short_option,
212 WvStringParm _long_option,
213 WvStringParm _desc,
214 int &_val)
215 : WvArgsNoArgOption(_short_option, _long_option, _desc),
216 val(_val)
220 virtual WvString process(WvStringParm arg)
222 val++;
223 return WvString::null;
228 class WvArgsNoArgCallbackOption : public WvArgsNoArgOption
231 private:
233 WvArgs::NoArgCallback cb;
234 void *ud;
236 public:
238 WvArgsNoArgCallbackOption(int _short_option,
239 WvStringParm _long_option,
240 WvStringParm _desc,
241 WvArgs::NoArgCallback _cb,
242 void *_ud)
243 : WvArgsNoArgOption(_short_option, _long_option, _desc),
244 cb(_cb), ud(_ud)
248 virtual WvString process(WvStringParm arg)
250 if (cb(ud))
251 return WvString::null;
252 else
253 return WvString("invalid option `%s'", arg);
258 class WvArgsArgOption : public WvArgsOption
260 private:
262 WvString arg_desc;
264 public:
266 WvArgsArgOption(int _short_option,
267 WvStringParm _long_option,
268 WvStringParm _desc,
269 WvStringParm _arg_desc)
270 : WvArgsOption(_short_option, _long_option, _desc),
271 arg_desc(_arg_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
284 private:
286 int &val;
288 public:
290 WvArgsIntOption(int _short_option,
291 WvStringParm _long_option,
292 WvStringParm _desc,
293 WvStringParm _arg_desc,
294 int &_val)
295 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
296 val(_val)
300 virtual WvString process(WvStringParm arg)
302 char *tailptr = NULL;
303 errno = 0;
304 long int tmp = strtol(arg, &tailptr, 10);
305 if (errno == ERANGE || tmp > INT_MAX || tmp < INT_MIN )
307 // Out of range
308 return WvString("`%s': invalid number.", arg);
310 else if (*tailptr)
312 // Invalid number
313 return WvString("`%s': invalid number.", arg);
315 else
317 val = tmp;
318 return WvString::null;
324 class WvArgsLongOption : public WvArgsArgOption
326 private:
328 long &val;
330 public:
332 WvArgsLongOption(int _short_option,
333 WvStringParm _long_option,
334 WvStringParm _desc,
335 WvStringParm _arg_desc,
336 long &_val)
337 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
338 val(_val)
342 virtual WvString process(WvStringParm arg)
344 char *tailptr = NULL;
345 errno = 0;
346 long int tmp = strtol(arg, &tailptr, 10);
347 if (errno == ERANGE)
349 // Out of range
350 return WvString("`%s': invalid number.", arg);
352 else if (*tailptr)
354 // Invalid number
355 return WvString("`%s': invalid number.", arg);
357 else
359 val = tmp;
360 return WvString::null;
366 class WvArgsFloatOption : public WvArgsArgOption
368 private:
370 float &val;
372 public:
374 WvArgsFloatOption(int _short_option,
375 WvStringParm _long_option,
376 WvStringParm _desc,
377 WvStringParm _arg_desc,
378 float &_val)
379 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
380 val(_val)
384 virtual WvString process(WvStringParm arg)
386 char *tailptr = NULL;
387 errno = 0;
388 float tmp = strtof(arg, &tailptr);
389 if (errno == ERANGE)
391 // Out of range
392 return WvString("`%s': invalid number.", arg);
394 else if (*tailptr)
396 // Invalid number
397 return WvString("`%s': invalid number.", arg);
399 else
401 val = tmp;
402 return WvString::null;
408 class WvArgsDoubleOption : public WvArgsArgOption
410 private:
412 double &val;
414 public:
416 WvArgsDoubleOption(int _short_option,
417 WvStringParm _long_option,
418 WvStringParm _desc,
419 WvStringParm _arg_desc,
420 double &_val)
421 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
422 val(_val)
426 virtual WvString process(WvStringParm arg)
428 char *tailptr = NULL;
429 errno = 0;
430 double tmp = strtod(arg, &tailptr);
431 if (errno == ERANGE)
433 // Out of range
434 return WvString("`%s': invalid number.", arg);
436 else if (*tailptr)
438 // Invalid number
439 return WvString("`%s': invalid number.", arg);
441 else
443 val = tmp;
444 return WvString::null;
450 class WvArgsStringOption : public WvArgsArgOption
452 private:
454 WvString &val;
456 public:
458 WvArgsStringOption(int _short_option,
459 WvStringParm _long_option,
460 WvStringParm _desc,
461 WvStringParm _arg_desc,
462 WvString &_val)
463 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
464 val(_val)
468 virtual WvString process(WvStringParm arg)
470 val = arg;
471 return WvString::null;
476 class WvArgsStringListAppendOption : public WvArgsArgOption
478 private:
480 WvStringList &val;
482 public:
484 WvArgsStringListAppendOption(int _short_option,
485 WvStringParm _long_option,
486 WvStringParm _desc,
487 WvStringParm _arg_desc,
488 WvStringList &_val)
489 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
490 val(_val)
494 virtual WvString process(WvStringParm arg)
496 val.append(arg);
497 return WvString::null;
502 class WvArgsArgCallbackOption : public WvArgsArgOption
504 private:
506 WvArgs::ArgCallback cb;
507 void *ud;
509 public:
511 WvArgsArgCallbackOption(int _short_option,
512 WvStringParm _long_option,
513 WvStringParm _desc,
514 WvStringParm _arg_desc,
515 WvArgs::ArgCallback _cb,
516 void *_ud)
517 : WvArgsArgOption(_short_option, _long_option, _desc, _arg_desc),
518 cb(_cb), ud(_ud)
522 virtual WvString process(WvStringParm arg)
524 if (cb(arg, ud))
525 return WvString::null;
526 else
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()
541 if (argp_)
542 free(argp_);
546 argp_option *WvArgsData::argp() const
548 return argp_;
552 void *WvArgsData::self() const
554 return (void *)this;
558 void WvArgsData::add(WvArgsOption *option)
560 if (!option)
561 return;
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)
586 matches_long = true;
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());
594 i.xunlink();
595 if (argp_)
597 free(argp_);
598 argp_ = NULL;
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()
620 options_dict.zap();
621 options_list.zap();
623 if (argp_)
625 free(argp_);
626 argp_ = NULL;
631 void WvArgsData::argp_init(size_t size)
633 argp_size = size;
634 if (argp_size < 1)
635 argp_size = 1;
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()
646 if (!argp_)
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))
659 if (!argp_double())
660 return false;
663 // Make a copy of the option that we're building.
664 memcpy(argp_ + argp_index, &option, sizeof(argp_option));
665 // Terminate the array.
666 ++argp_index;
667 memset(argp_ + argp_index, 0, sizeof(argp_option));
668 return true;
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))
677 if (!argp_double())
678 return false;
681 // Set the elements.
682 argp_option *option = argp_ + argp_index;
683 option->name = name;
684 option->key = key;
685 option->arg = arg;
686 option->flags = flags;
687 option->doc = doc;
688 option->group = group;
689 // Terminate the array.
690 ++argp_index;
691 memset(argp_ + argp_index, 0, sizeof(argp_option));
692 return true;
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));
700 if (!tmp)
701 return false;
703 argp_ = (argp_option *)tmp;
704 argp_size *= 2;
705 return true;
709 void WvArgsData::add_required_arg()
711 ++required_args;
715 void WvArgsData::subtract_required_arg()
717 --required_args;
721 const WvStringList &WvArgsData::args() const
723 return args_;
727 error_t WvArgsData::parser(int key, char *arg, struct argp_state *state)
729 WvArgsData *data = (WvArgsData *)state->input;
731 switch (key)
733 case ARGP_KEY_ARG:
734 if (state->arg_num >= data->maximum_args)
736 // Too many arguments
737 argp_usage(state);
739 data->args_.append(arg);
740 break;
742 case ARGP_KEY_NO_ARGS:
743 case ARGP_KEY_END:
744 if (state->arg_num < data->required_args)
746 // Too few arguments
747 argp_usage(state);
749 break;
751 default:
752 WvArgsOption *option = data->options_dict[key];
753 if (option)
755 WvString error = option->process(arg);
756 if (!error.isnull())
758 argp_failure(state, argp_err_exit_status, 0,
759 "%s", error.cstr());
760 return EINVAL;
763 else
764 return ARGP_ERR_UNKNOWN;
767 return 0;
771 WvArgs::WvArgs()
772 : data(new WvArgsData())
777 WvArgs::~WvArgs()
779 if (data)
780 delete data;
784 bool WvArgs::process(int argc, char **argv, WvStringList *remaining_args)
786 if (!data->argp())
787 data->argp_build();
789 // Setup --help headers and footers
790 WvString prog_doc;
791 if (header && footer)
792 prog_doc = WvString("%s\v%s", header, footer);
793 else if (header)
794 prog_doc = WvString("%s", header);
795 else if (footer)
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,
803 0, 0, 0 };
805 bool error = argp_parse(&argp, argc, argv, data->flags, 0, data->self());
807 if (remaining_args)
809 remaining_args->zap();
810 WvStringList::Iter i(data->args());
811 for (i.rewind(); i.next(); )
812 remaining_args->add(new WvString(*i), true);
815 return !error;
819 void WvArgs::set_version(WvStringParm version)
821 this->version = version;
825 void WvArgs::set_email(WvStringParm email)
827 this->email = 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,
885 cb, ud));
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,
893 val));
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,
901 val));
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,
909 val));
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,
917 arg_desc, val));
920 void WvArgs::add_option(char short_option, WvStringParm long_option,
921 WvStringParm desc, WvStringParm arg_desc,
922 WvString &val)
924 data->remove(short_option, long_option);
925 data->add(new WvArgsStringOption(short_option, long_option, desc,
926 arg_desc, val));
929 void WvArgs::add_option(char short_option, WvStringParm long_option,
930 WvStringParm desc, WvStringParm arg_desc,
931 WvStringList &val)
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,
944 arg_desc, cb, ud));
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()
962 data->zap();
966 void WvArgs::add_required_arg(WvStringParm desc, bool multiple)
968 data->add_required_arg();
969 if (!!args_doc)
970 args_doc.append(" ");
971 args_doc.append(desc);
972 if (multiple)
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
992 switch (flag)
994 case NO_EXIT_ON_ERRORS:
995 return data->flags & ARGP_NO_EXIT;
996 default:
997 return false;
1002 void WvArgs::set_flag(const flags_t flag, const bool value)
1004 printf("set_flag(%d, %d)\n", flag, value);
1005 unsigned int mask;
1006 switch (flag)
1008 case NO_EXIT_ON_ERRORS:
1009 mask = ARGP_NO_EXIT;
1010 break;
1011 default:
1012 return;
1015 if (value)
1016 data->flags |= mask;
1017 else
1018 data->flags &= ~mask;
1020 printf("set_flag(%d, %d) = %d\n", flag, value, data->flags);