]> git.mdlowis.com Git - proto/aos.git/commitdiff
implemented logic to parse command line specs and print pretty help text
authorMichael D. Lowis <mike@mdlowis.com>
Tue, 15 Dec 2020 03:15:42 +0000 (22:15 -0500)
committerMichael D. Lowis <mike@mdlowis.com>
Tue, 15 Dec 2020 03:15:42 +0000 (22:15 -0500)
inc/liba.h
lib/a/opts_parsespec.c [new file with mode: 0644]
lib/a/opts_printhelp.c [new file with mode: 0644]

index 5a5ebe3ff7b5e928a433e12258214c4429ba6791..52e3867601471e35bf6c0006d373989c59dd410c 100644 (file)
     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 (file)
index 0000000..91c4d23
--- /dev/null
@@ -0,0 +1,32 @@
+#include <liba.h>
+
+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 (file)
index 0000000..5b8096a
--- /dev/null
@@ -0,0 +1,52 @@
+#include <liba.h>
+
+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 : ""));
+    }
+}