The Lean Mean C++ Option Parser
testparse.cpp
Go to the documentation of this file.
1 /* Written 2012 by Matthias S. Benkmann
2  *
3  * The author hereby waives all copyright and related rights to the contents
4  * of this example file (testparse.cpp) to the extent possible under the law.
5  */
6 
22 #include <assert.h>
23 #include <stdio.h>
24 
25 #include "optionparser.h"
26 
27 using option::Option;
28 using option::Descriptor;
29 using option::Parser;
30 using option::Stats;
31 using option::ArgStatus;
32 
33 struct Arg: public option::Arg
34 {
35  static ArgStatus Required(const Option& option, bool)
36  {
37  return option.arg == 0 ? option::ARG_ILLEGAL : option::ARG_OK;
38  }
39  static ArgStatus Empty(const Option& option, bool)
40  {
41  return (option.arg == 0 || option.arg[0] == 0) ? option::ARG_OK : option::ARG_IGNORE;
42  }
43 };
44 
45 char* gettext(const char * msgid)
46 {
47  return (char*) msgid;
48 }
49 
50 const Descriptor empty_usage[] = { { 0, 0, 0, 0, 0, 0 } };
51 
52 const Descriptor minimal_usage[] = //
53  { { 0, 0, "x", "", Arg::None, 0 }, //
54  { 0, 0, 0, 0, 0, 0 } };
55 
56 const Descriptor optional_usage[] = //
57  { { 0, 0, "f", "", Arg::Required, 0 }, //
58  { 0, 0, 0, 0, 0, 0 } };
59 
60 const Descriptor gettext_usage[] = //
61  { { 0, 0, "f", "", Arg::Required, gettext("This is a test") }, //
62  { 0, 0, 0, 0, 0, 0 } };
63 
64 enum OptionIndex
65 {
66  UNKNOWN, FOO, VERBOSE, X, ABBREVIATE, EMPTY
67 };
68 enum OptionType
69 {
70  UNUSED = 0, DISABLED = 1, ENABLED = 2
71 };
72 
73 const Descriptor multi_usage[] = //
74  { { UNKNOWN, 0, "", "", Arg::None, 0 }, // UNKNOWN option catch-all
75  { FOO, ENABLED, "", "enable-foo", Arg::None, 0 }, // FOO enable
76  { FOO, DISABLED, "", "disable-foo", Arg::None, 0 }, // FOO disable
77  { VERBOSE, 0, "v", "verbose", Arg::None, 0 }, // VERBOSE (counted option)
78  { X, 0, "X", "X", Arg::Required, 0 }, // -X<arg>, -X <arg>, -X=<arg>, --X=<arg>
79  { ABBREVIATE, 0, "", "abbreviate-me", Arg::None, 0 }, // ABBREVIATE
80  { EMPTY, 0, "", "emptyarg", Arg::Empty, 0 }, // EMPTY (ignores arguments that are not "")
81  { 0, 0, 0, 0, 0, 0 } };
82 
83 const char* empty_args[] = { 0 };
84 const char* non_options[] = { "1", "2", "3", (const char*) -1 };
85 const char* unknown_option[] = { "--unknown", "nonoption", 0 };
86 const char* lone_minus[] = { "-f", "-", "-", 0 };
87 const char* lone_doubleminus[] = { "--", 0 };
88 
89 // NOTE: (const char*) -1 is used to cause a segfault should this element be dereferenced.
90 // 0 is not used here, because the parser explicitly checks for 0 which could mask bugs.
91 // If a number of arguments >= 0 is passed, the parser is supposed to honor that and never
92 // dereference an element beyond the last.
93 const char* multi1[] =
94  { "--enable-foo", "--unknown1", "-u", "-vX", "xyzzy", "--", "--strangefilename", (const char*) -1 };
95 const char* multi2[] = { "-vvXfoo", "-X", "bar", "-X=foobar", "-X", "", "--disable-foo", "-v", (const char*) -1 };
96 const char* multi3[] = { "-abbr", "-abb", "--emptyarg", "-verbose", "--emptyarg", "", "--emptyarg=", "nonoption1",
97  "nonoption2", (const char*) -1 };
98 
99 const char* illegal[] = { "-X", 0 };
100 const char* reorder[] = { "-X", "--", "-", "-X", "--", "foo", "-v", "--", "bar", "--", 0 };
101 const char* reorder2[] = { "-X", "--", "-", "-X", "--", "-", 0 };
102 
103 int count(const char** args)
104 {
105  for (int c = 0;; ++c)
106  if (args[c] == (const char*) -1)
107  return c;
108 }
109 
110 bool eq(const char* s1, const char* s2)
111 {
112  if (s1 == s2)
113  return true;
114 
115  if (s1 == 0 || s2 == 0)
116  return false;
117 
118  while (*s1 != 0 && *s2 != 0)
119  {
120  ++s1;
121  ++s2;
122  }
123 
124  return *s1 == *s2;
125 }
126 
127 int main()
128 {
129  {
130  Stats stats(empty_usage, -1, empty_args);
131  stats.add(empty_usage, 0, empty_args);
132  assert(stats.buffer_max == 1);
133  assert(stats.options_max == 1);
134  Option buffer[stats.buffer_max];
135  Option options[stats.options_max];
136  Parser parse(empty_usage, 99, empty_args, options, buffer);
137  parse.parse(empty_usage, -1, empty_args, options, buffer);
138  assert(parse.optionsCount() == 0);
139  assert(parse.nonOptionsCount() == 0);
140  assert(!buffer[0]);
141  assert(!options[0]);
142  assert(buffer[0].count()==0);
143  assert(parse.nonOptions()==0);
144 
145  stats.add(empty_usage, 3, non_options);
146  assert(stats.buffer_max == 1);
147  assert(stats.options_max == 1);
148  parse.parse(empty_usage, 3, non_options, options, buffer);
149  assert(parse.optionsCount() == 0);
150  assert(parse.nonOptionsCount() == 3);
151  assert(!buffer[0]);
152  assert(!options[0]);
153  assert(parse.nonOptions()==&non_options[0]);
154 
155  stats.add(minimal_usage, -1, unknown_option);
156  assert(stats.buffer_max == 1);
157  assert(stats.options_max == 2);
158  parse.parse(minimal_usage, -1, unknown_option, options, buffer);
159  assert(parse.optionsCount() == 0);
160  assert(parse.nonOptionsCount() == 1);
161  assert(!buffer[0]);
162  assert(!options[0]);
163  assert(parse.nonOptions()==&unknown_option[1]);
164  }
165  {
166  Stats stats(gettext_usage, -1, lone_minus);
167  Stats stats2;
168  stats2.add(gettext_usage, -1, lone_minus);
169  assert(stats.buffer_max == 2);
170  assert(stats.options_max == 2);
171  assert(stats2.buffer_max == 2);
172  assert(stats2.options_max == 2);
173  Option buffer[stats.buffer_max];
174  Option options[stats.options_max];
175  Parser parse;
176  parse.parse(gettext_usage, -1, lone_minus, options, buffer);
177  assert(parse.optionsCount() == 1);
178  assert(parse.nonOptionsCount() == 1);
179  assert(parse.nonOptions()==&lone_minus[2]);
180  assert(options[0]);
181  assert(buffer[0]);
182  assert(options[0].count()==1);
183  assert(options[0].isFirst());
184  assert(options[0].isLast());
185  assert(options[0].first() == options[0]);
186  assert(options[0].last() == options[0]);
187  assert(options[0].prevwrap() == &options[0]);
188  assert(options[0].nextwrap() == &options[0]);
189  assert(options[0].prev() == 0);
190  assert(options[0].next() == 0);
191  assert(options[0].desc == &gettext_usage[0]);
192  assert(eq(options[0].name, "f"));
193  assert(eq(options[0].arg, "-"));
194  }
195  {
196  Stats stats(optional_usage, -1, lone_minus);
197  Stats stats2;
198  stats2.add(optional_usage, -1, lone_minus);
199  assert(stats.buffer_max == 2);
200  assert(stats.options_max == 2);
201  assert(stats2.buffer_max == 2);
202  assert(stats2.options_max == 2);
203  Option buffer[stats.buffer_max];
204  Option options[stats.options_max];
205  Parser parse;
206  parse.parse(optional_usage, -1, lone_minus, options, buffer);
207  assert(parse.optionsCount() == 1);
208  assert(parse.nonOptionsCount() == 1);
209  assert(parse.nonOptions()==&lone_minus[2]);
210  assert(options[0]);
211  assert(buffer[0]);
212  assert(options[0].count()==1);
213  assert(options[0].isFirst());
214  assert(options[0].isLast());
215  assert(options[0].first() == options[0]);
216  assert(options[0].last() == options[0]);
217  assert(options[0].prevwrap() == &options[0]);
218  assert(options[0].nextwrap() == &options[0]);
219  assert(options[0].prev() == 0);
220  assert(options[0].next() == 0);
221  assert(options[0].desc == &optional_usage[0]);
222  assert(eq(options[0].name, "f"));
223  assert(eq(options[0].arg, "-"));
224  }
225  {
226  Stats stats;
227  stats.add(minimal_usage, -1, lone_doubleminus);
228  assert(stats.buffer_max == 1);
229  assert(stats.options_max == 2);
230  Option buffer[stats.buffer_max];
231  Option options[stats.options_max];
232  Parser parse(minimal_usage, -1, lone_doubleminus, options, buffer);
233  assert(parse.optionsCount() == 0);
234  assert(parse.nonOptionsCount() == 0);
235  assert(!buffer[0]);
236  assert(!options[0]);
237  assert(parse.nonOptions()==0);
238  }
239  {
240  Stats stats;
241  stats.add(multi_usage, count(multi1), multi1, 4, true);
242  assert(stats.buffer_max == 6);
243  assert(stats.options_max == 7);
244  stats.add(multi_usage, count(multi2), multi2, 4, true);
245  assert(stats.buffer_max == 14);
246  assert(stats.options_max == 7);
247  stats.add(multi_usage, count(multi3), multi3, 4, true);
248  assert(stats.buffer_max == 22);
249  assert(stats.options_max == 7);
250  Option buffer[stats.buffer_max];
251  Option options[stats.options_max];
252  assert(options[FOO].last()->type() == UNUSED);
253  assert(options[ABBREVIATE].count()==0);
254  Parser parse;
255  assert(!parse.error());
256 
257  parse.parse(multi_usage, count(multi1), multi1, options, buffer, 4, true);
258  assert(!parse.error());
259  assert(parse.optionsCount() == 5);
260  assert(parse.nonOptionsCount() == 1);
261  assert(eq(parse.nonOptions()[0],"--strangefilename"));
262  assert(options[FOO].last()->type() == ENABLED);
263  assert(eq(options[FOO].last()->name, "--enable-foo"));
264  assert(options[FOO].last()->arg == 0);
265  assert(options[UNKNOWN].count() == 2);
266  assert(eq(options[UNKNOWN].first()->name,"--unknown1"));
267  assert(eq(options[UNKNOWN].last()->name,"u"));
268  assert(options[UNKNOWN].first()->arg == 0);
269  assert(options[UNKNOWN].last()->arg == 0);
270  assert(options[VERBOSE].count()==1);
271  assert(options[VERBOSE].arg==0);
272  assert(options[VERBOSE].name[0] == 'v' && options[VERBOSE].namelen == 1);
273  assert(eq(options[X].arg,"xyzzy"));
274  assert(eq(options[X].name,"X"));
275 
276  parse.parse(multi_usage, count(multi2), multi2, options, buffer, 4, true);
277  assert(!parse.error());
278  assert(parse.optionsCount() == 13);
279  assert(parse.nonOptionsCount() == 1);
280  assert(eq(parse.nonOptions()[0],"--strangefilename"));
281  assert(options[FOO].last()->type() == DISABLED);
282  assert(options[FOO].last()->arg == 0);
283  assert(options[UNKNOWN].count() == 2);
284  assert(eq(options[UNKNOWN].first()->name,"--unknown1"));
285  assert(eq(options[UNKNOWN].last()->name,"u"));
286  assert(options[VERBOSE].count()==4);
287  assert(options[X].count()==5);
288  const char* Xargs[] = { "xyzzy", "foo", "bar", "foobar", "", "sentinel" };
289  const char** Xarg = &Xargs[0];
290  for (Option* Xiter = options[X]; Xiter != 0; Xiter = Xiter->next())
291  assert(eq(Xiter->arg, *Xarg++));
292 
293  assert(!options[ABBREVIATE]);
294  parse.parse(multi_usage, count(multi3), multi3, options, buffer, 4, true);
295  assert(!parse.error());
296  assert(parse.optionsCount() == 21);
297  assert(parse.nonOptionsCount() == 2);
298  assert(eq(parse.nonOptions()[0],"nonoption1"));
299  assert(eq(parse.nonOptions()[1],"nonoption2"));
300  assert(options[ABBREVIATE]);
301  assert(options[EMPTY].count()==3);
302  assert(options[EMPTY].first()->arg==0);
303  assert(eq(options[EMPTY].last()->arg,""));
304  assert(eq(options[EMPTY].last()->prev()->arg,""));
305  assert(options[FOO].last()->type() == DISABLED);
306  assert(options[UNKNOWN].count() == 5);
307  assert(eq(options[UNKNOWN].first()->name,"--unknown1"));
308  assert(options[UNKNOWN].first()->arg == 0);
309  assert(eq(options[UNKNOWN].last()->name,"b"));
310  assert(options[VERBOSE].count()==5);
311  assert(options[X].count()==5);
312  Xarg = &Xargs[0];
313  for (Option* Xiter = options[X]; Xiter != 0; Xiter = Xiter->next())
314  assert(eq(Xiter->arg, *Xarg++));
315 
316  for (Option* opt = buffer[0]; *opt; ++opt)
317  if (opt->desc->check_arg != Arg::Required && opt->desc->check_arg != Arg::Empty)
318  assert(opt->arg == 0);
319  }
320  {
321  Option buffer[2];
322  Option options[20];
323  Parser parse;
324  assert(!parse.error());
325  parse.parse(multi_usage, -1, illegal, options, buffer, 0, false, 2);
326  assert(parse.error());
327  }
328  {
329  Stats stats(multi_usage, count(multi3), multi3, 0, true);
330  const int bufmax = 3;
331  Option buffer[bufmax];
332  Option options[stats.options_max];
333  assert(!options[ABBREVIATE]);
334  Parser parse(multi_usage, count(multi3), multi3, options, buffer, 4, true, bufmax);
335  assert(!parse.error());
336  assert(parse.optionsCount() == bufmax);
337  assert(parse.nonOptionsCount() == 2);
338  assert(eq(parse.nonOptions()[0],"nonoption1"));
339  assert(eq(parse.nonOptions()[1],"nonoption2"));
340  assert(options[ABBREVIATE]);
341  assert(options[UNKNOWN].count() == 2); // because of buxmax the 2nd 'b' cannot be stored
342  assert(options[UNKNOWN].first()->name[0] == 'a' && options[UNKNOWN].first()->namelen == 1);
343  assert(options[UNKNOWN].first()->arg == 0);
344  assert(eq(options[UNKNOWN].last()->name,"bb"));
345  }
346  {
347  Stats stats(true, multi_usage, -1, reorder);
348  Option buffer[stats.buffer_max];
349  Option options[stats.options_max];
350  Parser parse(true, multi_usage, -1, reorder, options, buffer);
351  assert(!parse.error());
352  assert(parse.optionsCount() == 3);
353  assert(parse.nonOptionsCount() == 4);
354  assert(parse.nonOptions() == &reorder[6]);
355  }
356  {
357  Option buffer[10];
358  Option options[10];
359  Parser parse(true, multi_usage, 666, reorder2, options, buffer, 0, false, 10);
360  assert(!parse.error());
361  assert(parse.optionsCount() == 2);
362  assert(parse.nonOptionsCount() == 2);
363  }
364 
365  fprintf(stdout, "All tests passed.\n");
366  return 0;
367 }
368 
Determines the minimum lengths of the buffer and options arrays used for Parser.
Definition: optionparser.h:950
This is the only file required to use The Lean Mean C++ Option Parser. Just #include it and you&#39;re se...
unsigned options_max
Number of elements needed for an options[] array to be used for parsing the same argument vectors tha...
Definition: optionparser.h:974
bool error()
Returns true if an unrecoverable error occurred while parsing options.
static ArgStatus None(const Option &, bool)
For options that don&#39;t take an argument: Returns ARG_NONE.
Definition: optionparser.h:926
int nonOptionsCount()
Returns the number of non-option arguments that remained at the end of the most recent parse() that a...
void add(bool gnu, const Descriptor usage[], int argc, const char **argv, int min_abbr_len=0, bool single_minus_longopt=false)
Updates this Stats object for the given usage and argument vector. You may pass 0 for argc and/or arg...
A parsed option from the command line together with its argument if it has one.
Definition: optionparser.h:442
Functions for checking the validity of option arguments.
Definition: optionparser.h:923
Option * next()
Returns a pointer to the next element of the linked list or NULL if called on last().
Definition: optionparser.h:695
Checks argument vectors for validity and parses them into data structures that are easier to work wit...
const char ** nonOptions()
Returns a pointer to an array of non-option arguments (only valid if nonOptionsCount() >0 )...
Describes an option, its help text (usage) and how it should be parsed.
Definition: optionparser.h:315
The argument is acceptable for the option.
Definition: optionparser.h:256
The namespace of The Lean Mean C++ Option Parser.
Definition: optionparser.h:227
const char * arg
Pointer to this Option&#39;s argument (if any).
Definition: optionparser.h:489
int optionsCount()
Returns the number of valid Option objects in buffer[].
unsigned buffer_max
Number of elements needed for a buffer[] array to be used for parsing the same argument vectors that ...
Definition: optionparser.h:961
The argument is not acceptable and that&#39;s fatal.
Definition: optionparser.h:260
The argument is not acceptable but that&#39;s non-fatal because the option&#39;s argument is optional...
Definition: optionparser.h:258
ArgStatus
Possible results when checking if an argument is valid for a certain option.
Definition: optionparser.h:251
void parse(bool gnu, const Descriptor usage[], int argc, const char **argv, Option options[], Option buffer[], int min_abbr_len=0, bool single_minus_longopt=false, int bufmax=-1)
Parses the given argument vector.