Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Created December 8, 2021 05:17
Show Gist options
  • Save fowlmouth/40ac4ac274df8495722c74f23e331189 to your computer and use it in GitHub Desktop.
Save fowlmouth/40ac4ac274df8495722c74f23e331189 to your computer and use it in GitHub Desktop.
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
struct cli;
typedef void(*cli_cb)(struct cli*, const char***, const char**, void*);
struct cli_option
{
const char* key;
void* value;
cli_cb cb;
};
struct cli
{
int opt_count, opt_capacity;
struct cli_option* options;
};
bool cli_init(struct cli* cli, int capacity)
{
if(capacity < 1)
{
capacity = 8;
}
void* opts = calloc(sizeof(struct cli_option), capacity);
if(!opts)
{
return false;
}
cli->opt_count = 0;
cli->opt_capacity = capacity;
cli->options = (struct cli_option*)opts;
return true;
}
void cli_deinit(struct cli* cli)
{
if(cli->options)
{
free(cli->options);
}
memset(cli, 0, sizeof(struct cli));
}
struct cli_option* cli_next_opt(struct cli* cli)
{
if(cli->opt_count == cli->opt_capacity)
{
int new_capacity = cli->opt_capacity * 2;
if(!new_capacity) // cant happen if cli_init is used
{
new_capacity = 4;
}
struct cli_option* new_opts = calloc(sizeof(struct cli_option), new_capacity);
memcpy(new_opts, cli->options, sizeof(struct cli_option) * cli->opt_capacity);
if(cli->options)
{
free(cli->options);
}
cli->opt_capacity = new_capacity;
cli->options = new_opts;
}
return cli->options + cli->opt_count++;
}
void cli_bool_cb(struct cli* cli, const char*** input, const char** end, void* value)
{
++*input;
bool* bool_value = (bool*)value;
*bool_value = true;
}
void cli_bool(struct cli* cli, const char* key, bool* value)
{
struct cli_option* opt = cli_next_opt(cli);
opt->key = key;
opt->value = (void*)value;
opt->cb = cli_bool_cb;
}
void cli_string_cb(struct cli* cli, const char*** input, const char** end, void* value)
{
const char** str_value = (const char**)value;
*str_value = *++*input;
++*input;
}
void cli_string(struct cli* cli, const char* key, const char** value)
{
struct cli_option* opt = cli_next_opt(cli);
opt->key = key;
opt->value = (void*)value;
opt->cb = cli_string_cb;
}
bool cli_match_opt(struct cli* cli, const char* arg, struct cli_option** out)
{
printf("cli match opt count=%d\n", cli->opt_count);
for(int i = 0; i < cli->opt_count; ++i)
{
struct cli_option* opt = cli->options + i;
printf("comparing '%s' and '%s'\n", arg, opt->key);
if(!strcmp(arg, opt->key))
{
*out = opt;
return true;
}
}
return false;
}
bool cli_parse_arg(struct cli* cli, const char*** input, const char** input_end)
{
const char* str = **input;
int len = strlen(str);
if(len && str[0] == '-')
{
struct cli_option* opt;
if(cli_match_opt(cli, str, &opt))
{
if(opt->cb)
{
const char** old_input = *input;
opt->cb(cli, input, input_end, opt->value);
return *input != old_input;
}
}
}
return false;
}
void cli_parse(struct cli* cli, int argc, const char** argv)
{
const char** argv_end = argv + argc;
++argv;
while(argv != argv_end)
{
printf("argv= '%s'\n", *argv);
if(!cli_parse_arg(cli, &argv, argv_end))
{
printf("breaking from cli_parse\n");
break;
}
}
}
int main(int argc, const char** argv)
{
bool run_tests = false;
const char* file = NULL;
struct cli cli;
cli_init(&cli, 1);
cli_bool(&cli, "-test", &run_tests);
cli_string(&cli, "-file", &file);
printf("cli count= %d capacity= %d\n", cli.opt_count, cli.opt_capacity);
cli_parse(&cli, argc, argv);
cli_deinit(&cli);
printf("run_tests: %d\n", (int)run_tests);
printf("file: '%s'\n", file);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment