From 8e08c38af2962fdd7ae2abac5e428ff52bde69fc Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Mon, 14 Dec 2020 22:15:42 -0500 Subject: [PATCH] implemented logic to parse command line specs and print pretty help text --- inc/liba.h | 35 ++++++++++++++-------------- lib/a/opts_parsespec.c | 32 ++++++++++++++++++++++++++ lib/a/opts_printhelp.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 lib/a/opts_parsespec.c create mode 100644 lib/a/opts_printhelp.c diff --git a/inc/liba.h b/inc/liba.h index 5a5ebe3..52e3867 100644 --- a/inc/liba.h +++ b/inc/liba.h @@ -34,37 +34,34 @@ Basic Runtime Facilities */ -/* rename user's main routine so GC is auto-initialized */ +/* rename user's main routine so GC is auto-initialized and options parsed */ extern int usermain(int, char**); #define main usermain -/* This variable contains the value of argv[0] so that it can be referenced - * again once the option parsing is done. This variable must be defined by the - * program. */ -extern char* ARGV0; - +/* + Garbage Collector Interface +*/ void* gc_alloc(size_t sz); void gc_addref(void* p); void gc_delref(void* p); +/* + Option Parsing +*/ typedef struct { char* name; char* desc; } Option_T; -/* - { .name = "v" } // Error - { .name = "v," } - { .name = ",foo" } - { .name = "b,bar" } - { .name = ":b,bar" } - { .name = ":,bar" } - { .name = ":b," } -*/ - -extern Option_T Options[]; +typedef struct { + char* lname; + char sname; + int hasarg : 1; +} OptionDescriptor_T; void opts_parse(int argc, char** argv); +void opts_parsespec(char* spec, OptionDescriptor_T* opt); +void opts_printhelp(void); /* Standard Library Helpers @@ -82,3 +79,7 @@ char* efreadline(FILE* input); char* estrdup(const char *s); int forkexec(char** cmd); char* strmcat(char* first, ...); + +extern char* ARGV0; +extern char* Usage; +extern Option_T Options[]; diff --git a/lib/a/opts_parsespec.c b/lib/a/opts_parsespec.c new file mode 100644 index 0000000..91c4d23 --- /dev/null +++ b/lib/a/opts_parsespec.c @@ -0,0 +1,32 @@ +#include + +void opts_parsespec(char* spec, OptionDescriptor_T* opt) +{ + char* fullspec = spec; + memset(opt, 0, sizeof(OptionDescriptor_T)); + + /* parse optional arg specifier */ + if (*spec == ':') + { + opt->hasarg = 1; + spec++; + } + + /* parse optional short name */ + if (*spec != ',') + { + opt->sname = *(spec++); + } + + /* we need a comma at a minimum and if no short name was parsed,a + long name must be provided. bail if we don't meet these constraints */ + if (*spec != ',' || (!opt->sname && spec[1] == '\0')) + { + fatal("opts_parsespec(): bad option specification '%s'", fullspec); + } + + if (*(++spec)) + { + opt->lname = spec; + } +} diff --git a/lib/a/opts_printhelp.c b/lib/a/opts_printhelp.c new file mode 100644 index 0000000..5b8096a --- /dev/null +++ b/lib/a/opts_printhelp.c @@ -0,0 +1,52 @@ +#include + +void opts_printhelp(void) +{ + Option_T* opts = Options; + OptionDescriptor_T od = {0}; + size_t padding = 0; + + /* calculate padding */ + for (Option_T* curr = opts; curr->name; curr++) + { + size_t pad = 4; + opts_parsespec(curr->name, &od); + if (od.sname) + { + pad += 2; + } + if (od.lname) + { + pad += (od.sname ? 2 : 0) + 2 + strlen(od.lname); + } + if (od.hasarg) + { + pad += 4; + } + if (pad > padding) + { + padding = pad; + } + } + + /* print option help messages */ + printf("usage: %s\n\n", Usage); + for (Option_T* curr = opts; curr->name; curr++) + { + int remain = padding; + opts_parsespec(curr->name, &od); + if (od.sname) + { + remain -= printf("-%c", od.sname); + } + if (od.lname) + { + remain -= printf("%s--%s", (od.sname ? ", " : ""), od.lname); + } + if (od.hasarg) + { + remain -= printf(" ARG"); + } + printf("%*c%s\n", remain, ' ', (curr->desc ? curr->desc : "")); + } +} -- 2.52.0