Last active
October 15, 2024 10:15
-
-
Save cynthia2006/b06c3bb42067e2e60ab538116d88fe59 to your computer and use it in GitHub Desktop.
Simple argument parser - a cheap GNU argparse clone
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stddef.h> | |
| #include <stdbool.h> | |
| #include <string.h> | |
| typedef int(*arg_type)(void*, int, const char*); | |
| #define arg_type_flag NULL | |
| // TODO Use something better than atoi() which reports parsing error. | |
| int arg_type_int (void* args, int offset, const char* arg) | |
| { | |
| *(int*)(args + offset) = atoi(arg); | |
| return 0; | |
| } | |
| int arg_type_float (void* args, int offset, const char* arg) | |
| { | |
| return !sscanf(arg, "%f", (float*)(args + offset)); | |
| } | |
| int arg_type_str (void* args, int offset, const char* arg) | |
| { | |
| *(const char**)(args + offset) = arg; | |
| return 0; | |
| } | |
| struct argspec | |
| { | |
| const char* name; | |
| arg_type type; | |
| int offset; | |
| const char* help; | |
| }; | |
| static | |
| struct argspec* get_argspec_from_raw_opt (struct argspec* specs, | |
| char* opt) | |
| { | |
| struct argspec* spec; | |
| int i; | |
| if(strncmp(opt, "-", 1) == 0) | |
| { | |
| for (i = 0; specs[i].name != NULL; ++i) | |
| { | |
| spec = &specs[i]; | |
| if(spec->name && strcmp(spec->name, opt + 1) == 0) | |
| return spec; | |
| } | |
| } | |
| return NULL; | |
| } | |
| static | |
| int parse_args (struct argspec* specs, | |
| void* args, | |
| int argc, | |
| char** argv) | |
| { | |
| int i = 1; | |
| // FIXME Null-pointer dereference | |
| // TODO Detect duplicate argument specifications | |
| while (i < argc) | |
| { | |
| struct argspec* spec = get_argspec_from_raw_opt (specs, argv[i]); | |
| if (spec) | |
| { | |
| if (spec->type == arg_type_flag) { | |
| *(bool*)(args + spec->offset) = true; | |
| } else { | |
| if (spec->type(args, spec->offset, argv[i + 1])) | |
| return 1; | |
| ++i; | |
| } | |
| } | |
| ++i; | |
| } | |
| return 0; | |
| } | |
| static | |
| void dump_argspec (struct argspec *specs) | |
| { | |
| int i; | |
| for (i = 0; specs[i].name != NULL; ++i) | |
| { | |
| struct argspec *spec = &specs[i]; | |
| printf ("-%s\t%s\n", spec->name, spec->help); | |
| } | |
| } | |
| typedef struct { | |
| bool help; | |
| int number; | |
| float decimal; | |
| const char* string; | |
| } arguments; | |
| int main(int argc, char** argv) { | |
| int i; | |
| arguments args; | |
| struct argspec spec[] = { | |
| {"help", arg_type_flag, offsetof(arguments, help), "show a help message"}, | |
| {"int", arg_type_int, offsetof(arguments, number), "an integer value"}, | |
| {"float", arg_type_float, offsetof(arguments, decimal), "a floating point value"}, | |
| {"str", arg_type_str, offsetof(arguments, string), "a string value"}, | |
| {NULL} | |
| }; | |
| if (parse_args(spec, &args, argc, argv)) | |
| { | |
| fprintf(stderr, "argument parsing failed\n"); | |
| exit(1); | |
| } | |
| if (args.help) { | |
| dump_argspec(spec); | |
| exit(0); | |
| } | |
| printf("int = %d\n" | |
| "float = %f\n" | |
| "str = %s\n", | |
| args.number, args.decimal, args.string); | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A small demo.