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


apriltag
Author(s): Edwin Olson , Max Krogius
autogenerated on Mon Jun 26 2023 02:26:12