254 lines
5.6 KiB
C
254 lines
5.6 KiB
C
#include "argparser.h"
|
|
#include "aliases.h"
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define INITIAL_SIZE 30
|
|
|
|
INTERNAL arg_t help_arg = {ARG_TYPE_FLAG, "h", "help"};
|
|
|
|
void print_usage(argparser_t *parser);
|
|
|
|
struct argparser {
|
|
arg_t *args;
|
|
u64 size;
|
|
u64 args_count;
|
|
u64 positional_count;
|
|
u64 named_count;
|
|
u64 flag_count;
|
|
};
|
|
|
|
argparser_t *create_parser(parse_func_t parse_func) {
|
|
argparser_t *parser = (argparser_t *)malloc(sizeof(argparser_t));
|
|
|
|
if (!parser) {
|
|
return NULL;
|
|
}
|
|
|
|
parser->args = (arg_t *)malloc(INITIAL_SIZE * sizeof(arg_t));
|
|
if (!(parser->args)) {
|
|
free(parser);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
parser->size = INITIAL_SIZE;
|
|
parser->args_count = parser->positional_count = parser->named_count =
|
|
parser->flag_count = 0;
|
|
|
|
add_argument(parser, help_arg);
|
|
|
|
return parser;
|
|
}
|
|
|
|
void destroy_parser(argparser_t **parser) {
|
|
if (*parser) {
|
|
if ((*parser)->args) {
|
|
free((*parser)->args);
|
|
}
|
|
|
|
free(*parser);
|
|
|
|
*parser = NULL;
|
|
}
|
|
}
|
|
|
|
void add_argument(argparser_t *parser, arg_t arg) {
|
|
bool valid_arg_type = false;
|
|
|
|
switch (arg.type) {
|
|
case ARG_TYPE_POSITIONAL:
|
|
case ARG_TYPE_NAMED:
|
|
case ARG_TYPE_FLAG:
|
|
valid_arg_type = true;
|
|
break;
|
|
default:
|
|
valid_arg_type = false;
|
|
}
|
|
|
|
if (!valid_arg_type) {
|
|
return;
|
|
}
|
|
|
|
if (parser->args_count == parser->size) {
|
|
parser->size += INITIAL_SIZE;
|
|
|
|
// clang-format off
|
|
parser->args = (arg_t *)realloc(
|
|
(void *)(parser->args),
|
|
parser->size * sizeof(arg_t)
|
|
);
|
|
// clang-format on
|
|
|
|
if (!(parser->args)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
parser->args[(parser->args_count)++] = arg;
|
|
|
|
switch (arg.type) {
|
|
case ARG_TYPE_POSITIONAL:
|
|
++(parser->positional_count);
|
|
break;
|
|
case ARG_TYPE_NAMED:
|
|
++(parser->named_count);
|
|
break;
|
|
case ARG_TYPE_FLAG:
|
|
++(parser->flag_count);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void parse_args(argparser_t *parser, i32 argc, char *argv[]) {
|
|
for (u32 i = 0; i < argc; ++i) {
|
|
const char *short_name = &(argv[i][1]);
|
|
const char *long_name = &(argv[i][2]);
|
|
|
|
if (strcmp(short_name, help_arg.short_name) == 0 ||
|
|
strcmp(long_name, help_arg.long_name) == 0) {
|
|
print_usage(parser);
|
|
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
if (argc - 1 < parser->positional_count) {
|
|
print_usage(parser);
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void print_usage(argparser_t *parser) {
|
|
arg_t positional[parser->positional_count];
|
|
memset(positional, 0, parser->positional_count);
|
|
u64 positional_index = 0;
|
|
|
|
arg_t named[parser->named_count];
|
|
memset(named, 0, parser->named_count);
|
|
u64 named_index = 0;
|
|
|
|
arg_t flag[parser->flag_count];
|
|
memset(flag, 0, parser->flag_count);
|
|
u64 flag_index = 0;
|
|
|
|
for (u64 i = 0; i < parser->args_count; ++i) {
|
|
const arg_t *arg = &(parser->args[i]);
|
|
|
|
switch (arg->type) {
|
|
case ARG_TYPE_POSITIONAL:
|
|
positional[positional_index++] = *arg;
|
|
break;
|
|
case ARG_TYPE_NAMED:
|
|
named[named_index++] = *arg;
|
|
break;
|
|
case ARG_TYPE_FLAG:
|
|
flag[flag_index++] = *arg;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
char usage_line[2048] = {0};
|
|
char positional_args[2048] = {0};
|
|
char named_args[2048] = {0};
|
|
char flag_args[2048] = {0};
|
|
|
|
sprintf(usage_line, "Usage: program ");
|
|
|
|
if (positional_index > 0) {
|
|
sprintf(positional_args, "Positional arguments:\n");
|
|
}
|
|
|
|
if (named_index > 0) {
|
|
sprintf(named_args, "Named arguments:\n");
|
|
}
|
|
|
|
if (flag_index > 0) {
|
|
sprintf(flag_args, "Flag arguments:\n");
|
|
}
|
|
|
|
for (u64 i = 0; i < flag_index; ++i) {
|
|
const arg_t *arg = &(flag[i]);
|
|
|
|
sprintf(&(usage_line[strlen(usage_line)]), "[");
|
|
sprintf(&(flag_args[strlen(flag_args)]), "\t");
|
|
|
|
if (strlen(arg->short_name) > 0) {
|
|
sprintf(&(usage_line[strlen(usage_line)]), "-%s | ", arg->short_name);
|
|
sprintf(&(flag_args[strlen(flag_args)]), "-%s, ", arg->short_name);
|
|
}
|
|
|
|
sprintf(&(usage_line[strlen(usage_line)]), "--%s] ", arg->long_name);
|
|
sprintf(&(flag_args[strlen(flag_args)]), "--%s\n", arg->long_name);
|
|
}
|
|
|
|
for (u64 i = 0; i < named_index; ++i) {
|
|
const arg_t *arg = &(named[i]);
|
|
|
|
sprintf(&(usage_line[strlen(usage_line)]), "[");
|
|
sprintf(&(named_args[strlen(named_args)]), "\t");
|
|
|
|
if (strlen(arg->short_name) > 0) {
|
|
sprintf(&(usage_line[strlen(usage_line)]), "-%s | ", arg->short_name);
|
|
sprintf(&(named_args[strlen(named_args)]), "-%s, ", arg->short_name);
|
|
}
|
|
|
|
u64 name_length = strlen(arg->long_name);
|
|
|
|
char capitalised[name_length];
|
|
memset(capitalised, 0, name_length);
|
|
|
|
strcpy(capitalised, arg->long_name);
|
|
|
|
for (u64 i = 0; i < name_length; ++i) {
|
|
capitalised[i] = toupper(capitalised[i]);
|
|
}
|
|
|
|
sprintf(&(usage_line[strlen(usage_line)]), "--%s %s] ", arg->long_name,
|
|
capitalised);
|
|
sprintf(&(named_args[strlen(named_args)]), "--%s\n", arg->long_name);
|
|
}
|
|
|
|
for (u64 i = 0; i < positional_index; ++i) {
|
|
const arg_t *arg = &(named[i]);
|
|
|
|
sprintf(&(positional_args[strlen(positional_args)]), "\t");
|
|
|
|
u64 name_length = strlen(arg->long_name);
|
|
|
|
char capitalised[name_length];
|
|
memset(capitalised, 0, name_length);
|
|
|
|
strcpy(capitalised, arg->long_name);
|
|
|
|
for (u64 i = 0; i < name_length; ++i) {
|
|
capitalised[i] = toupper(capitalised[i]);
|
|
}
|
|
|
|
sprintf(&(usage_line[strlen(usage_line)]), "%s ", capitalised);
|
|
sprintf(&(positional_args[strlen(positional_args)]), "%s\n", capitalised);
|
|
}
|
|
|
|
printf("%s\n\n", usage_line);
|
|
|
|
if (strlen(positional_args) > 0) {
|
|
printf("%s\n", positional_args);
|
|
}
|
|
|
|
if (strlen(named_args) > 0) {
|
|
printf("%s\n", named_args);
|
|
}
|
|
|
|
if (strlen(flag_args) > 0) {
|
|
printf("%s\n", flag_args);
|
|
}
|
|
}
|