Basic test implementation of an argument parser

This commit is contained in:
Abdelrahman Said 2023-06-02 00:20:15 +01:00
parent b063f60235
commit d625d4a99c
2 changed files with 256 additions and 0 deletions

229
argparser.c Normal file
View File

@ -0,0 +1,229 @@
#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
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);
}
}

27
argparser.h Normal file
View File

@ -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