getopt.c
Go to the documentation of this file.
1 /* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2 All rights reserved.
3 
4 This software was developed in the APRIL Robotics Lab under the
5 direction of Edwin Olson, ebolson@umich.edu. This software may be
6 available under alternative licensing terms; contact the address above.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 
11 1. Redistributions of source code must retain the above copyright notice, this
12  list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 The views and conclusions contained in the software and documentation are those
29 of the authors and should not be interpreted as representing official policies,
30 either expressed or implied, of the Regents of The University of Michigan.
31 */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
39 
40 #include "zhash.h"
41 #include "zarray.h"
42 #include "getopt.h"
43 #include "common/math_util.h"
44 
45 #define GOO_BOOL_TYPE 1
46 #define GOO_STRING_TYPE 2
47 
49 
51 {
52  char *sname;
53  char *lname;
54  char *svalue;
55 
56  char *help;
57  int type;
58 
59  int spacer;
60 
62 };
63 
64 struct getopt
65 {
70 };
71 
73 {
74  getopt_t *gopt = (getopt_t*) calloc(1, sizeof(getopt_t));
75 
76  gopt->lopts = zhash_create(sizeof(char*), sizeof(getopt_option_t*), zhash_str_hash, zhash_str_equals);
77  gopt->sopts = zhash_create(sizeof(char*), sizeof(getopt_option_t*), zhash_str_hash, zhash_str_equals);
78  gopt->options = zarray_create(sizeof(getopt_option_t*));
79  gopt->extraargs = zarray_create(sizeof(char*));
80 
81  return gopt;
82 }
83 
85 {
86  free(goo->sname);
87  free(goo->lname);
88  free(goo->svalue);
89  free(goo->help);
90  memset(goo, 0, sizeof(getopt_option_t));
91  free(goo);
92 }
93 
95 {
96  // free the extra arguments and container
97  zarray_vmap(gopt->extraargs, free);
99 
100  // deep free of the getopt_option structs. Also frees key/values, so
101  // after this loop, hash tables will no longer work
103  zarray_destroy(gopt->options);
104 
105  // free tables
106  zhash_destroy(gopt->lopts);
107  zhash_destroy(gopt->sopts);
108 
109  memset(gopt, 0, sizeof(getopt_t));
110  free(gopt);
111 }
112 
113 static void getopt_modify_string(char **str, char *newvalue)
114 {
115  char *old = *str;
116  *str = newvalue;
117  if (old != NULL)
118  free(old);
119 }
120 
121 static char *get_arg_assignment(char *arg)
122 {
123  // not an arg starting with "--"?
124  if (!str_starts_with(arg, "--")) {
125  return NULL;
126  }
127 
128  int eq_index = str_indexof(arg, "=");
129 
130  // no assignment?
131  if (eq_index == -1) {
132  return NULL;
133  }
134 
135  // no quotes allowed before '=' in "--key=value" option specification.
136  // quotes can be used in value string, or by extra arguments
137  for (int i = 0; i < eq_index; i++) {
138  if (arg[i] == '\'' || arg[i] == '"') {
139  return NULL;
140  }
141  }
142 
143  return &arg[eq_index];
144 }
145 
146 // returns 1 if no error
147 int getopt_parse(getopt_t *gopt, int argc, char *argv[], int showErrors)
148 {
149  int okay = 1;
150  zarray_t *toks = zarray_create(sizeof(char*));
151 
152  // take the input stream and chop it up into tokens
153  for (int i = 1; i < argc; i++) {
154 
155  char *arg = strdup(argv[i]);
156  char *eq = get_arg_assignment(arg);
157 
158  // no equal sign? Push the whole thing.
159  if (eq == NULL) {
160  zarray_add(toks, &arg);
161  } else {
162  // there was an equal sign. Push the part
163  // before and after the equal sign
164  char *val = strdup(&eq[1]);
165  eq[0] = 0;
166  zarray_add(toks, &arg);
167 
168  // if the part after the equal sign is
169  // enclosed by quotation marks, strip them.
170  if (val[0]=='\"') {
171  size_t last = strlen(val) - 1;
172  if (val[last]=='\"')
173  val[last] = 0;
174  char *valclean = strdup(&val[1]);
175  zarray_add(toks, &valclean);
176  free(val);
177  } else {
178  zarray_add(toks, &val);
179  }
180  }
181  }
182 
183  // now loop over the elements and evaluate the arguments
184  unsigned int i = 0;
185 
186  char *tok = NULL;
187 
188  while (i < zarray_size(toks)) {
189 
190  // rather than free statement throughout this while loop
191  if (tok != NULL)
192  free(tok);
193 
194  zarray_get(toks, i, &tok);
195 
196  if (!strncmp(tok,"--", 2)) {
197  char *optname = &tok[2];
198  getopt_option_t *goo = NULL;
199  zhash_get(gopt->lopts, &optname, &goo);
200  if (goo == NULL) {
201  okay = 0;
202  if (showErrors)
203  printf("Unknown option --%s\n", optname);
204  i++;
205  continue;
206  }
207 
208  goo->was_specified = 1;
209 
210  if (goo->type == GOO_BOOL_TYPE) {
211  if ((i+1) < zarray_size(toks)) {
212  char *val = NULL;
213  zarray_get(toks, i+1, &val);
214 
215  if (!strcmp(val,"true")) {
216  i+=2;
217  getopt_modify_string(&goo->svalue, val);
218  continue;
219  }
220  if (!strcmp(val,"false")) {
221  i+=2;
222  getopt_modify_string(&goo->svalue, val);
223  continue;
224  }
225  }
226  getopt_modify_string(&goo->svalue, strdup("true"));
227  i++;
228  continue;
229  }
230 
231  if (goo->type == GOO_STRING_TYPE) {
232  // TODO: check whether next argument is an option, denoting missing argument
233  if ((i+1) < zarray_size(toks)) {
234  char *val = NULL;
235  zarray_get(toks, i+1, &val);
236  i+=2;
237  getopt_modify_string(&goo->svalue, val);
238  continue;
239  }
240 
241  okay = 0;
242  if (showErrors)
243  printf("Option %s requires a string argument.\n",optname);
244  }
245  }
246 
247  if (!strncmp(tok,"-",1) && strncmp(tok,"--",2)) {
248  size_t len = strlen(tok);
249  int pos;
250  for (pos = 1; pos < len; pos++) {
251  char sopt[2];
252  sopt[0] = tok[pos];
253  sopt[1] = 0;
254  char *sopt_ptr = (char*) &sopt;
255  getopt_option_t *goo = NULL;
256  zhash_get(gopt->sopts, &sopt_ptr, &goo);
257 
258  if (goo==NULL) {
259  // is the argument a numerical literal that happens to be negative?
260  if (pos==1 && isdigit(tok[pos])) {
261  zarray_add(gopt->extraargs, &tok);
262  tok = NULL;
263  break;
264  } else {
265  okay = 0;
266  if (showErrors)
267  printf("Unknown option -%c\n", tok[pos]);
268  i++;
269  continue;
270  }
271  }
272 
273  goo->was_specified = 1;
274 
275  if (goo->type == GOO_BOOL_TYPE) {
276  getopt_modify_string(&goo->svalue, strdup("true"));
277  continue;
278  }
279 
280  if (goo->type == GOO_STRING_TYPE) {
281  if ((i+1) < zarray_size(toks)) {
282  char *val = NULL;
283  zarray_get(toks, i+1, &val);
284  // TODO: allow negative numerical values for short-name options ?
285  if (val[0]=='-')
286  {
287  okay = 0;
288  if (showErrors)
289  printf("Ran out of arguments for option block %s\n", tok);
290  }
291  i++;
292  getopt_modify_string(&goo->svalue, val);
293  continue;
294  }
295 
296  okay = 0;
297  if (showErrors)
298  printf("Option -%c requires a string argument.\n", tok[pos]);
299  }
300  }
301  i++;
302  continue;
303  }
304 
305  // it's not an option-- it's an argument.
306  zarray_add(gopt->extraargs, &tok);
307  tok = NULL;
308  i++;
309  }
310  if (tok != NULL)
311  free(tok);
312 
313  zarray_destroy(toks);
314 
315  return okay;
316 }
317 
318 void getopt_add_spacer(getopt_t *gopt, const char *s)
319 {
320  getopt_option_t *goo = (getopt_option_t*) calloc(1, sizeof(getopt_option_t));
321  goo->spacer = 1;
322  goo->help = strdup(s);
323  zarray_add(gopt->options, &goo);
324 }
325 
326 void getopt_add_bool(getopt_t *gopt, char sopt, const char *lname, int def, const char *help)
327 {
328  char sname[2];
329  sname[0] = sopt;
330  sname[1] = 0;
331  char *sname_ptr = (char*) &sname;
332 
333  if (strlen(lname) < 1) { // must have long name
334  fprintf (stderr, "getopt_add_bool(): must supply option name\n");
335  exit (EXIT_FAILURE);
336  }
337 
338  if (sopt == '-') { // short name cannot be '-' (no way to reference)
339  fprintf (stderr, "getopt_add_bool(): invalid option character: '%c'\n", sopt);
340  exit (EXIT_FAILURE);
341  }
342 
343  if (zhash_contains(gopt->lopts, &lname)) {
344  fprintf (stderr, "getopt_add_bool(): duplicate option name: --%s\n", lname);
345  exit (EXIT_FAILURE);
346  }
347 
348  if (sopt != '\0' && zhash_contains(gopt->sopts, &sname_ptr)) {
349  fprintf (stderr, "getopt_add_bool(): duplicate option: -%s ('%s')\n", sname, lname);
350  exit (EXIT_FAILURE);
351  }
352 
353  getopt_option_t *goo = (getopt_option_t*) calloc(1, sizeof(getopt_option_t));
354  goo->sname=strdup(sname);
355  goo->lname=strdup(lname);
356  goo->svalue=strdup(def ? "true" : "false");
357  goo->type=GOO_BOOL_TYPE;
358  goo->help=strdup(help);
359 
360  zhash_put(gopt->lopts, &goo->lname, &goo, NULL, NULL);
361  zhash_put(gopt->sopts, &goo->sname, &goo, NULL, NULL);
362  zarray_add(gopt->options, &goo);
363 }
364 
365 void getopt_add_int(getopt_t *gopt, char sopt, const char *lname, const char *def, const char *help)
366 {
367  getopt_add_string(gopt, sopt, lname, def, help);
368 }
369 
370 void
371 getopt_add_double (getopt_t *gopt, char sopt, const char *lname, const char *def, const char *help)
372 {
373  getopt_add_string (gopt, sopt, lname, def, help);
374 }
375 
376 void getopt_add_string(getopt_t *gopt, char sopt, const char *lname, const char *def, const char *help)
377 {
378  char sname[2];
379  sname[0] = sopt;
380  sname[1] = 0;
381  char *sname_ptr = (char*) &sname;
382 
383  if (strlen(lname) < 1) { // must have long name
384  fprintf (stderr, "getopt_add_string(): must supply option name\n");
385  exit (EXIT_FAILURE);
386  }
387 
388  if (sopt == '-') { // short name cannot be '-' (no way to reference)
389  fprintf (stderr, "getopt_add_string(): invalid option character: '%c'\n", sopt);
390  exit (EXIT_FAILURE);
391  }
392 
393  if (zhash_contains(gopt->lopts, &lname)) {
394  fprintf (stderr, "getopt_add_string(): duplicate option name: --%s\n", lname);
395  exit (EXIT_FAILURE);
396  }
397 
398  if (sopt != '\0' && zhash_contains(gopt->sopts, &sname_ptr)) {
399  fprintf (stderr, "getopt_add_string(): duplicate option: -%s ('%s')\n", sname, lname);
400  exit (EXIT_FAILURE);
401  }
402 
403  getopt_option_t *goo = (getopt_option_t*) calloc(1, sizeof(getopt_option_t));
404  goo->sname=strdup(sname);
405  goo->lname=strdup(lname);
406  goo->svalue=strdup(def);
407  goo->type=GOO_STRING_TYPE;
408  goo->help=strdup(help);
409 
410  zhash_put(gopt->lopts, &goo->lname, &goo, NULL, NULL);
411  zhash_put(gopt->sopts, &goo->sname, &goo, NULL, NULL);
412  zarray_add(gopt->options, &goo);
413 }
414 
415 const char *getopt_get_string(getopt_t *gopt, const char *lname)
416 {
417  getopt_option_t *goo = NULL;
418  zhash_get(gopt->lopts, &lname, &goo);
419  // could return null, but this would be the only
420  // method that doesn't assert on a missing key
421  assert (goo != NULL);
422  return goo->svalue;
423 }
424 
426 {
427  const char *v = getopt_get_string(getopt, lname);
428  assert(v != NULL);
429 
430  errno = 0;
431  char *endptr = (char *) v;
432  long val = strtol(v, &endptr, 10);
433 
434  if (errno != 0) {
435  fprintf (stderr, "--%s argument: strtol failed: %s\n", lname, strerror(errno));
436  exit (EXIT_FAILURE);
437  }
438 
439  if (endptr == v) {
440  fprintf (stderr, "--%s argument cannot be parsed as an int\n", lname);
441  exit (EXIT_FAILURE);
442  }
443 
444  return (int) val;
445 }
446 
448 {
449  const char *v = getopt_get_string(getopt, lname);
450  assert (v!=NULL);
451  int val = !strcmp(v, "true");
452  return val;
453 }
454 
455 double getopt_get_double (getopt_t *getopt, const char *lname)
456 {
457  const char *v = getopt_get_string (getopt, lname);
458  assert (v!=NULL);
459 
460  errno = 0;
461  char *endptr = (char *) v;
462  double d = strtod (v, &endptr);
463 
464  if (errno != 0) {
465  fprintf (stderr, "--%s argument: strtod failed: %s\n", lname, strerror(errno));
466  exit (EXIT_FAILURE);
467  }
468 
469  if (endptr == v) {
470  fprintf (stderr, "--%s argument cannot be parsed as a double\n", lname);
471  exit (EXIT_FAILURE);
472  }
473 
474  return d;
475 }
476 
478 {
479  getopt_option_t *goo = NULL;
480  zhash_get(getopt->lopts, &lname, &goo);
481  if (goo == NULL)
482  return 0;
483 
484  return goo->was_specified;
485 }
486 
488 {
489  return gopt->extraargs;
490 }
491 
493 {
494  char * usage = getopt_get_usage(gopt);
495  printf("%s", usage);
496  free(usage);
497 }
498 
500 {
502 
503  int leftmargin=2;
504  int longwidth=12;
505  int valuewidth=10;
506 
507  for (unsigned int i = 0; i < zarray_size(gopt->options); i++) {
508  getopt_option_t *goo = NULL;
509  zarray_get(gopt->options, i, &goo);
510 
511  if (goo->spacer)
512  continue;
513 
514  longwidth = max(longwidth, (int) strlen(goo->lname));
515 
516  if (goo->type == GOO_STRING_TYPE)
517  valuewidth = max(valuewidth, (int) strlen(goo->svalue));
518  }
519 
520  for (unsigned int i = 0; i < zarray_size(gopt->options); i++) {
521  getopt_option_t *goo = NULL;
522  zarray_get(gopt->options, i, &goo);
523 
524  if (goo->spacer)
525  {
526  if (goo->help==NULL || strlen(goo->help)==0)
527  string_buffer_appendf(sb,"\n");
528  else
529  string_buffer_appendf(sb,"\n%*s%s\n\n", leftmargin, "", goo->help);
530  continue;
531  }
532 
533  string_buffer_appendf(sb,"%*s", leftmargin, "");
534 
535  if (goo->sname[0]==0)
536  string_buffer_appendf(sb," ");
537  else
538  string_buffer_appendf(sb,"-%c | ", goo->sname[0]);
539 
540  string_buffer_appendf(sb,"--%*s ", -longwidth, goo->lname);
541 
542  string_buffer_appendf(sb," [ %s ]", goo->svalue); // XXX: displays current value rather than default value
543 
544  string_buffer_appendf(sb,"%*s", (int) (valuewidth-strlen(goo->svalue)), "");
545 
546  string_buffer_appendf(sb," %s ", goo->help);
547  string_buffer_appendf(sb,"\n");
548  }
549 
550  char * usage = string_buffer_to_string(sb);
552  return usage;
553 }
zarray_t * options
Definition: getopt.c:69
int zhash_str_equals(const void *a, const void *b)
Definition: zhash.c:529
int spacer
Definition: getopt.c:59
double getopt_get_double(getopt_t *getopt, const char *lname)
Definition: getopt.c:455
void getopt_add_int(getopt_t *gopt, char sopt, const char *lname, const char *def, const char *help)
Definition: getopt.c:365
void getopt_add_double(getopt_t *gopt, char sopt, const char *lname, const char *def, const char *help)
Definition: getopt.c:371
static int zarray_size(const zarray_t *za)
Definition: zarray.h:136
int getopt_parse(getopt_t *gopt, int argc, char *argv[], int showErrors)
Definition: getopt.c:147
void getopt_add_string(getopt_t *gopt, char sopt, const char *lname, const char *def, const char *help)
Definition: getopt.c:376
void getopt_option_destroy(getopt_option_t *goo)
Definition: getopt.c:84
Definition: zhash.c:46
int zhash_contains(const zhash_t *zh, const void *key)
Definition: zhash.c:271
bool str_starts_with(const char *haystack, const char *needle)
Definition: string_util.c:580
static void zarray_destroy(zarray_t *za)
Definition: zarray.h:76
char * getopt_get_usage(getopt_t *gopt)
Definition: getopt.c:499
void getopt_add_bool(getopt_t *gopt, char sopt, const char *lname, int def, const char *help)
Definition: getopt.c:326
zarray_t * extraargs
Definition: getopt.c:68
#define str(s)
int type
Definition: getopt.c:57
getopt_t * getopt_create()
Definition: getopt.c:72
static zarray_t * zarray_create(size_t el_sz)
Definition: zarray.h:63
string_buffer_t * string_buffer_create()
Definition: string_util.c:323
const char * getopt_get_string(getopt_t *gopt, const char *lname)
Definition: getopt.c:415
int getopt_was_specified(getopt_t *getopt, const char *lname)
Definition: getopt.c:477
char * sname
Definition: getopt.c:52
#define GOO_BOOL_TYPE
Definition: getopt.c:45
void zhash_destroy(zhash_t *zh)
Definition: zhash.c:103
void string_buffer_appendf(string_buffer_t *sb, const char *fmt,...) __attribute__((format(printf
char * lname
Definition: getopt.c:53
zhash_t * lopts
Definition: getopt.c:66
void getopt_add_spacer(getopt_t *gopt, const char *s)
Definition: getopt.c:318
uint32_t zhash_str_hash(const void *a)
Definition: zhash.c:540
const zarray_t * getopt_get_extra_args(getopt_t *gopt)
Definition: getopt.c:487
int zhash_put(zhash_t *zh, const void *key, const void *value, void *oldkey, void *oldvalue)
Definition: zhash.c:152
char * help
Definition: getopt.c:56
static void zarray_get(const zarray_t *za, int idx, void *p)
Definition: zarray.h:201
zhash_t * sopts
Definition: getopt.c:67
Definition: zarray.h:49
static void getopt_modify_string(char **str, char *newvalue)
Definition: getopt.c:113
int zhash_get(const zhash_t *zh, const void *key, void *out_value)
Definition: zhash.c:141
int getopt_get_bool(getopt_t *getopt, const char *lname)
Definition: getopt.c:447
int getopt_get_int(getopt_t *getopt, const char *lname)
Definition: getopt.c:425
void getopt_destroy(getopt_t *gopt)
Definition: getopt.c:94
void zarray_vmap(zarray_t *za, void(*f)())
Definition: zarray.c:51
int str_indexof(const char *haystack, const char *needle)
Definition: string_util.c:259
char * svalue
Definition: getopt.c:54
void string_buffer_destroy(string_buffer_t *sb)
Definition: string_util.c:332
Definition: getopt.c:64
int was_specified
Definition: getopt.c:61
char * string_buffer_to_string(string_buffer_t *sb)
Definition: string_util.c:424
static char * get_arg_assignment(char *arg)
Definition: getopt.c:121
#define GOO_STRING_TYPE
Definition: getopt.c:46
void getopt_do_usage(getopt_t *gopt)
Definition: getopt.c:492
#define max(A, B)
Definition: math_util.h:58
static void zarray_add(zarray_t *za, const void *p)
Definition: zarray.h:185
zhash_t * zhash_create(size_t keysz, size_t valuesz, uint32_t(*hash)(const void *a), int(*equals)(const void *a, const void *b))
Definition: zhash.c:97


apriltags2
Author(s): Danylo Malyuta
autogenerated on Fri Oct 19 2018 04:02:32