argparse.c Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/common/argparse.c Source File
argparse.c
Go to the documentation of this file.
1 /**
2  * Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
3  * All rights reserved.
4  *
5  * Use of this source code is governed by a MIT-style license that can be found
6  * in the LICENSE file.
7  *
8  * Note: Some small changes has been made by Natatech.
9  */
10 
11 #include "argparse.h"
12 
13 #define OPT_UNSET 1
14 
15 static const char *
16 prefix_skip(const char *str, const char *prefix)
17 {
18  size_t len = strlen(prefix);
19  return strncmp(str, prefix, len) ? NULL : str + len;
20 }
21 
22 static int
23 prefix_cmp(const char *str, const char *prefix)
24 {
25  for (;; str++, prefix++)
26  if (!*prefix)
27  return 0;
28  else if (*str != *prefix)
29  return (unsigned char)*prefix - (unsigned char)*str;
30 }
31 
32 static void
33 argparse_error(struct argparse *This, const struct argparse_option *opt,
34  const char *reason)
35 {
36  if (!strncmp(This->argv[0], "--", 2)) {
37  fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason);
38  exit(-1);
39  } else {
40  fprintf(stderr, "error: option `%c` %s\n", opt->short_name, reason);
41  exit(-1);
42  }
43 }
44 
45 static int
46 argparse_getvalue(struct argparse *This, const struct argparse_option *opt,
47  int flags)
48 {
49  char *s;
50  if (!opt->value)
51  goto skipped;
52  switch (opt->type) {
54  if (flags & OPT_UNSET) {
55  *(int *)opt->value = *(int *)opt->value - 1;
56  } else {
57  *(int *)opt->value = *(int *)opt->value + 1;
58  }
59  if (*(int *)opt->value < 0) {
60  *(int *)opt->value = 0;
61  }
62  break;
63  case ARGPARSE_OPT_BIT:
64  if (flags & OPT_UNSET) {
65  *(int *)opt->value &= (int)~(1 << opt->data);
66  } else {
67  *(int *)opt->value |= (int)(1 << opt->data);
68  }
69  break;
70  case ARGPARSE_OPT_BIT64:
71  if (flags & OPT_UNSET) {
72  *(uint64_t *)opt->value &= (uint64_t)~(1 << opt->data);
73  } else {
74  *(uint64_t *)opt->value |= (uint64_t)(1 << opt->data);
75  }
76  break;
78  if (This->optvalue) {
79  *(const char **)opt->value = This->optvalue;
80  This->optvalue = NULL;
81  } else if (This->argc > 1) {
82  This->argc--;
83  *(const char **)opt->value = *++This->argv;
84  } else {
85  argparse_error(This, opt, "requires a value");
86  }
87  break;
89  if (This->optvalue) {
90  *(int *)opt->value = (int)strtol(This->optvalue, (char **)&s, 0);
91  This->optvalue = NULL;
92  } else if (This->argc > 1) {
93  This->argc--;
94  *(int *)opt->value = (int)strtol(*++This->argv, (char **)&s, 0);
95  } else {
96  argparse_error(This, opt, "requires a value");
97  break;
98  }
99  if (*s)
100  argparse_error(This, opt, "expects a numerical value");
101  break;
102  case ARGPARSE_OPT_UINT64:
103  if (This->optvalue) {
104 #ifdef WIN32
105  *(uint64_t *)opt->value = (uint64_t)_strtoui64(This->optvalue, (char **)&s, 0);
106 #else
107  *(uint64_t *)opt->value = (uint64_t)strtoull(This->optvalue, (char **)&s, 0);
108 #endif
109  This->optvalue = NULL;
110  } else if (This->argc > 1) {
111  This->argc--;
112 #ifdef WIN32
113  *(uint64_t *)opt->value = (uint64_t)_strtoui64(*++This->argv, (char **)&s, 0);
114 #else
115  *(uint64_t *)opt->value = (uint64_t)strtoull(*++This->argv, (char **)&s, 0);
116 #endif
117  } else {
118  argparse_error(This, opt, "requires a value");
119  break;
120  }
121  if (*s)
122  argparse_error(This, opt, "expects a numerical value");
123  break;
124  default:
125  assert(0);
126  }
127 
128 skipped:
129  if (opt->callback) {
130  return opt->callback(This, opt, 1);
131  }
132 
133  return 0;
134 }
135 
136 static void
138 {
139  for (; options->type != ARGPARSE_OPT_END; options++) {
140  switch (options->type) {
141  case ARGPARSE_OPT_END:
143  case ARGPARSE_OPT_BIT:
144  case ARGPARSE_OPT_BIT64:
146  case ARGPARSE_OPT_UINT64:
147  case ARGPARSE_OPT_STRING:
148  continue;
149  default:
150  fprintf(stderr, "wrong option type: %d", options->type);
151  break;
152  }
153  }
154 }
155 
156 static int
157 argparse_short_opt(struct argparse *This, const struct argparse_option *options)
158 {
159  for (; options->type != ARGPARSE_OPT_END; options++) {
160  if (options->short_name == *This->optvalue) {
161  This->optvalue = This->optvalue[1] ? This->optvalue + 1 : NULL;
162  return argparse_getvalue(This, options, 0);
163  }
164  }
165  return -2;
166 }
167 
168 static int
169 argparse_long_opt(struct argparse *This, const struct argparse_option *options)
170 {
171  for (; options->type != ARGPARSE_OPT_END; options++) {
172  const char *rest;
173  int opt_flags = 0;
174  if (!options->long_name)
175  continue;
176 
177  rest = prefix_skip(This->argv[0] + 2, options->long_name);
178  if (!rest) {
179  // Negation allowed?
180  if (options->flags & OPT_NONEG) {
181  continue;
182  }
183  // Only boolean/bit allow negation.
184  if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT && options->type != ARGPARSE_OPT_BIT64) {
185  continue;
186  }
187 
188  if (!prefix_cmp(This->argv[0] + 2, "no-")) {
189  rest = prefix_skip(This->argv[0] + 2 + 3, options->long_name);
190  if (!rest)
191  continue;
192  opt_flags |= OPT_UNSET;
193  } else {
194  continue;
195  }
196  }
197  if (*rest) {
198  if (*rest != '=')
199  continue;
200  This->optvalue = rest + 1;
201  }
202  return argparse_getvalue(This, options, opt_flags);
203  }
204  return -2;
205 }
206 
207 int
208 argparse_init(struct argparse *This, struct argparse_option *options,
209  const char *const *usage, int flags)
210 {
211  memset(This, 0, sizeof(*This));
212  This->options = options;
213  This->usage = usage;
214  This->flags = flags;
215  return 0;
216 }
217 
218 int
219 argparse_parse(struct argparse *This, int argc, const char **argv)
220 {
221  This->argc = argc - 1;
222  This->argv = argv + 1;
223  This->out = argv;
224 
226 
227  for (; This->argc; This->argc--, This->argv++) {
228  const char *arg = This->argv[0];
229  if (arg[0] != '-' || !arg[1]) {
230  if (This->flags & ARGPARSE_STOP_AT_NON_OPTION) {
231  goto end;
232  }
233  // if it's not option or is a single char '-', copy verbatimly
234  This->out[This->cpidx++] = This->argv[0];
235  continue;
236  }
237  // short option
238  if (arg[1] != '-') {
239  This->optvalue = arg + 1;
240  switch (argparse_short_opt(This, This->options)) {
241  case -1:
242  break;
243  case -2:
244  goto unknown;
245  }
246  while (This->optvalue) {
247  switch (argparse_short_opt(This, This->options)) {
248  case -1:
249  break;
250  case -2:
251  goto unknown;
252  }
253  }
254  continue;
255  }
256  // if '--' presents
257  if (!arg[2]) {
258  This->argc--;
259  This->argv++;
260  break;
261  }
262  // long option
263  switch (argparse_long_opt(This, This->options)) {
264  case -1:
265  break;
266  case -2:
267  goto unknown;
268  }
269  continue;
270 
271 unknown:
272  fprintf(stderr, "error: unknown option `%s`\n", This->argv[0]);
273  argparse_usage(This);
274  exit(1);
275  }
276 
277 end:
278  if (This->argc >= 0) {
279  memmove((void *)(This->out + This->cpidx), This->argv,
280  (size_t)This->argc * sizeof(*This->out));
281  This->out[This->cpidx + This->argc] = NULL;
282  }
283 
284  return This->cpidx + This->argc;
285 }
286 
287 void
289 {
290  const struct argparse_option *options;
291 
292  // figure out best width
293  int usage_opts_width = 0;
294  size_t len;
295 
296  fprintf(stdout, "%s\n", *This->usage++);
297 
298  options = This->options;
299  for (; options->type != ARGPARSE_OPT_END; options++) {
300  len = 0;
301  if ((options)->short_name) {
302  len += 2;
303  }
304  if ((options)->short_name && (options)->long_name) {
305  len += 2; // separator ", "
306  }
307  if ((options)->long_name) {
308  len += strlen((options)->long_name) + 2;
309  }
310  if (options->name_value != NULL) {
311  len += strlen(options->name_value) + 3;
312  }
313  else {
314  if (options->type == ARGPARSE_OPT_INTEGER) {
315  len += strlen(" <int>");
316  } else if (options->type == ARGPARSE_OPT_UINT64) {
317  len += strlen(" <int64>");
318  } else if (options->type == ARGPARSE_OPT_STRING) {
319  len += strlen(" <str>");
320  }
321  }
322  len = (size_t)ceil((float)len / 4) * 4;
323  if (usage_opts_width < (int)len) {
324  usage_opts_width = (int)len;
325  }
326  }
327  usage_opts_width += 4; // 4 spaces prefix
328 
329  options = This->options;
330  for (; options->type != ARGPARSE_OPT_END; options++) {
331  int pos;
332  int pad;
333 
334  if (options->flags & ARGPARSE_INTERNAL_OPTION)
335  continue;
336 
337  pos = fprintf(stdout, " ");
338  if (options->short_name) {
339  pos += fprintf(stdout, "-%c", options->short_name);
340  }
341  if (options->long_name && options->short_name) {
342  pos += fprintf(stdout, ", ");
343  }
344  if (options->long_name) {
345  pos += fprintf(stdout, "--%s", options->long_name);
346  }
347  if (options->name_value != NULL) {
348  pos += fprintf(stdout, " <%s>", options->name_value);
349  }
350  else {
351  if (options->type == ARGPARSE_OPT_INTEGER) {
352  pos += fprintf(stdout, " <int>");
353  } else if (options->type == ARGPARSE_OPT_UINT64) {
354  pos += fprintf(stdout, " <int64>");
355  } else if (options->type == ARGPARSE_OPT_STRING) {
356  pos += fprintf(stdout, " <str>");
357  }
358  }
359  if (pos <= usage_opts_width) {
360  pad = (int)(usage_opts_width - pos);
361  } else {
362  fputc('\n', stdout);
363  pad = (int)usage_opts_width;
364  }
365 #if 0
366  fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
367 #else
368  {
369  size_t l = strlen(options->help);
370  size_t i;
371  fprintf(stdout, "%*s: ", pad, "");
372  for (i = 0; i < l; i++)
373  {
374  fputc(options->help[i], stdout);
375  if (options->help[i] == '\n') {
376  /* A newline in the text. We must indent the next text */
377  fprintf(stdout, "%*s", (int)(usage_opts_width + 2), "");
378  }
379  }
380  fputc('\n', stdout);
381  }
382 #endif
383  }
384  while (*This->usage && **This->usage)
385  fprintf(stdout, "%s\n", *This->usage++);
386  fputc('\n', stdout);
387 }
388 
389 int
390 argparse_help_cb(struct argparse *This, const struct argparse_option *option, int terminate)
391 {
392  (void)option;
393  argparse_usage(This);
394  if (terminate == 1)
395  exit(0);
396  return 0;
397 }