Skip to content

Instantly share code, notes, and snippets.

@cynthia2006
Last active October 15, 2024 10:15
Show Gist options
  • Select an option

  • Save cynthia2006/b06c3bb42067e2e60ab538116d88fe59 to your computer and use it in GitHub Desktop.

Select an option

Save cynthia2006/b06c3bb42067e2e60ab538116d88fe59 to your computer and use it in GitHub Desktop.
Simple argument parser - a cheap GNU argparse clone
#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);
}
@cynthia2006
Copy link
Author

A small demo.

$ ./parse-args -help 
-help   show a help message
-int    an integer value
-float  a floating point value
-str    a string value
$ ./parse-args -int 42 -float 3.14 -str "Hello World"
int   = 42
float = 3.140000
str   = Hello World

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment