diff --git a/argparser.c b/argparser.c new file mode 100644 index 0000000..51b4965 --- /dev/null +++ b/argparser.c @@ -0,0 +1,229 @@ +#include "argparser.h" +#include "aliases.h" +#include +#include +#include +#include +#include + +#define INITIAL_SIZE 30 + +struct argparser { + arg_t *args; + u64 size; + u64 args_count; + u64 positional_count; + u64 named_count; + u64 flag_count; +}; + +argparser_t *create_parser(void) { + 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, (arg_t){ARG_TYPE_FLAG, "h", "help"}); + + 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 + 1 == parser->size) { + arg_t *new_args = + (arg_t *)malloc((parser->size + INITIAL_SIZE) * sizeof(arg_t)); + + if (!new_args) { + return; + } + + memcpy(new_args, parser->args, parser->args_count); + + free(parser->args); + + parser->args = new_args; + } + + 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 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); + } +} diff --git a/argparser.h b/argparser.h new file mode 100644 index 0000000..8e379dc --- /dev/null +++ b/argparser.h @@ -0,0 +1,27 @@ +#ifndef ARGPARSER_H +#define ARGPARSER_H + +#include "aliases.h" + +typedef struct argparser argparser_t; + +typedef enum { + ARG_TYPE_POSITIONAL, + ARG_TYPE_NAMED, + ARG_TYPE_FLAG, + + COUNT_ARG_TYPE, +} arg_type_t; + +typedef struct { + arg_type_t type; + const char *short_name; + const char *long_name; +} arg_t; + +argparser_t *create_parser(void); +void destroy_parser(argparser_t **parser); +void add_argument(argparser_t *parser, arg_t arg); +void print_usage(argparser_t *parser); + +#endif // !ARGPARSER_H