#include "argparser.h" #include "aliases.h" #include #include #include #include #include #define INITIAL_SIZE 30 void print_usage(argparser_t *parser); typedef struct { arg_type_t type; const char *identifier; const char *short_name; } parser_arg_t; struct argparser { parser_arg_t *args; u64 size; u64 args_count; u64 positional_count; u64 named_count; u64 flag_count; }; INTERNAL parser_arg_t help_arg = {ARG_TYPE_FLAG, "help", "h"}; argparser_t *create_parser(parse_func_t parse_func) { argparser_t *parser = (argparser_t *)malloc(sizeof(argparser_t)); if (!parser) { return NULL; } parser->args = (parser_arg_t *)malloc(INITIAL_SIZE * sizeof(parser_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.identifier) == 0) { print_usage(parser); exit(0); } } if (argc - 1 < parser->positional_count) { print_usage(parser); exit(1); } } void print_usage(argparser_t *parser) { parser_arg_t positional[parser->positional_count]; memset(positional, 0, parser->positional_count); u64 positional_index = 0; parser_arg_t named[parser->named_count]; memset(named, 0, parser->named_count); u64 named_index = 0; parser_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 parser_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 parser_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->identifier); sprintf(&(flag_args[strlen(flag_args)]), "--%s\n", arg->identifier); } for (u64 i = 0; i < named_index; ++i) { const parser_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->identifier); char capitalised[name_length]; memset(capitalised, 0, name_length); strcpy(capitalised, arg->identifier); for (u64 i = 0; i < name_length; ++i) { capitalised[i] = toupper(capitalised[i]); } sprintf(&(usage_line[strlen(usage_line)]), "--%s %s] ", arg->identifier, capitalised); sprintf(&(named_args[strlen(named_args)]), "--%s\n", arg->identifier); } for (u64 i = 0; i < positional_index; ++i) { const parser_arg_t *arg = &(named[i]); sprintf(&(positional_args[strlen(positional_args)]), "\t"); u64 name_length = strlen(arg->identifier); char capitalised[name_length]; memset(capitalised, 0, name_length); strcpy(capitalised, arg->identifier); 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); } }