argparse.c

Reference Documentation

product_line_custom
Napatech SmartNIC
category
Reference Information

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 15static const char * 16prefix_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 22static int 23prefix_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 32static void 33argparse_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 45static int 46argparse_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) { 53 case ARGPARSE_OPT_BOOLEAN: 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; 77 case ARGPARSE_OPT_STRING: 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; 88 case ARGPARSE_OPT_INTEGER: 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 128skipped: 129 if (opt->callback) { 130 return opt->callback(This, opt, 1); 131 } 132 133 return 0; 134} 135 136static void 137argparse_options_check(const struct argparse_option *options) 138{ 139 for (; options->type != ARGPARSE_OPT_END; options++) { 140 switch (options->type) { 141 case ARGPARSE_OPT_END: 142 case ARGPARSE_OPT_BOOLEAN: 143 case ARGPARSE_OPT_BIT: 144 case ARGPARSE_OPT_BIT64: 145 case ARGPARSE_OPT_INTEGER: 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 156static int 157argparse_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 168static int 169argparse_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 207int 208argparse_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 218int 219argparse_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 225 argparse_options_check(This->options); 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 271unknown: 272 fprintf(stderr, "error: unknown option `%s`\n", This->argv[0]); 273 argparse_usage(This); 274 exit(1); 275 } 276 277end: 278 memmove((void *)(This->out + This->cpidx), This->argv, 279 This->argc * sizeof(*This->out)); 280 This->out[This->cpidx + This->argc] = NULL; 281 282 return This->cpidx + This->argc; 283} 284 285void 286argparse_usage(struct argparse *This) 287{ 288 const struct argparse_option *options; 289 290 // figure out best width 291 size_t usage_opts_width = 0; 292 size_t len; 293 294 fprintf(stdout, "%s\n", *This->usage++); 295 296 options = This->options; 297 for (; options->type != ARGPARSE_OPT_END; options++) { 298 len = 0; 299 if ((options)->short_name) { 300 len += 2; 301 } 302 if ((options)->short_name && (options)->long_name) { 303 len += 2; // separator ", " 304 } 305 if ((options)->long_name) { 306 len += strlen((options)->long_name) + 2; 307 } 308 if (options->name_value != NULL) { 309 len += strlen(options->name_value) + 3; 310 } 311 else { 312 if (options->type == ARGPARSE_OPT_INTEGER) { 313 len += strlen(" <int>"); 314 } else if (options->type == ARGPARSE_OPT_UINT64) { 315 len += strlen(" <int64>"); 316 } else if (options->type == ARGPARSE_OPT_STRING) { 317 len += strlen(" <str>"); 318 } 319 } 320 len = (size_t)ceil((float)len / 4) * 4; 321 if (usage_opts_width < len) { 322 usage_opts_width = len; 323 } 324 } 325 usage_opts_width += 4; // 4 spaces prefix 326 327 options = This->options; 328 for (; options->type != ARGPARSE_OPT_END; options++) { 329 size_t pos; 330 int pad; 331 332 if (options->flags & ARGPARSE_INTERNAL_OPTION) 333 continue; 334 335 pos = fprintf(stdout, " "); 336 if (options->short_name) { 337 pos += fprintf(stdout, "-%c", options->short_name); 338 } 339 if (options->long_name && options->short_name) { 340 pos += fprintf(stdout, ", "); 341 } 342 if (options->long_name) { 343 pos += fprintf(stdout, "--%s", options->long_name); 344 } 345 if (options->name_value != NULL) { 346 pos += fprintf(stdout, " <%s>", options->name_value); 347 } 348 else { 349 if (options->type == ARGPARSE_OPT_INTEGER) { 350 pos += fprintf(stdout, " <int>"); 351 } else if (options->type == ARGPARSE_OPT_UINT64) { 352 pos += fprintf(stdout, " <int64>"); 353 } else if (options->type == ARGPARSE_OPT_STRING) { 354 pos += fprintf(stdout, " <str>"); 355 } 356 } 357 if (pos <= usage_opts_width) { 358 pad = (int)(usage_opts_width - pos); 359 } else { 360 fputc('\n', stdout); 361 pad = (int)usage_opts_width; 362 } 363#if 0 364 fprintf(stdout, "%*s%s\n", pad + 2, "", options->help); 365#else 366 { 367 size_t l = strlen(options->help); 368 size_t i; 369 fprintf(stdout, "%*s: ", pad, ""); 370 for (i = 0; i < l; i++) 371 { 372 fputc(options->help[i], stdout); 373 if (options->help[i] == '\n') { 374 /* A newline in the text. We must indent the next text */ 375 fprintf(stdout, "%*s", (int)(usage_opts_width + 2), ""); 376 } 377 } 378 fputc('\n', stdout); 379 } 380#endif 381 } 382 while (*This->usage && **This->usage) 383 fprintf(stdout, "%s\n", *This->usage++); 384 fputc('\n', stdout); 385} 386 387int 388argparse_help_cb(struct argparse *This, const struct argparse_option *option, int terminate) 389{ 390 (void)option; 391 argparse_usage(This); 392 if (terminate == 1) 393 exit(0); 394 return 0; 395}