]> git.mdlowis.com Git - projs/opts.git/commitdiff
Added suckless/plan9 inspired opt.h for lightweight traditional option parsing
authorMichael D. Lowis <mike@mdlowis.com>
Mon, 7 Dec 2015 01:59:54 +0000 (20:59 -0500)
committerMichael D. Lowis <mike@mdlowis.com>
Mon, 7 Dec 2015 01:59:54 +0000 (20:59 -0500)
Makefile
source/opt.h [new file with mode: 0644]
tests/main.c
tests/test_opt.c [new file with mode: 0644]

index 511fb89abac72484d54b194ae3654ac08690d331..466e757875a456dd2abd2384f439bdb9819c0019 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ AR = ar
 # flags
 INCS      = -Isource/ -Itests/
 CPPFLAGS  = -D_XOPEN_SOURCE=700
-CFLAGS   += ${INCS} ${CPPFLAGS}
+CFLAGS   += -g ${INCS} ${CPPFLAGS}
 LDFLAGS  += ${LIBS}
 ARFLAGS   = rcs
 
@@ -20,7 +20,7 @@ ARFLAGS   = rcs
 #------------------------------------------------------------------------------
 SRCS = source/opts.c
 OBJS = ${SRCS:.c=.o}
-TEST_SRCS = tests/atf.c tests/main.c tests/test_opts.c
+TEST_SRCS = tests/atf.c tests/main.c tests/test_opts.c tests/test_opt.c
 TEST_OBJS = ${TEST_SRCS:.c=.o}
 
 all: options libopts.a testopts
diff --git a/source/opt.h b/source/opt.h
new file mode 100644 (file)
index 0000000..5e64c3f
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef OPT_H
+#define OPT_H
+
+extern char* ARGV0;
+
+static inline char* getarg(int* p_argc, char*** p_argv) {
+    if (!(*p_argv)[0][1] && !(*p_argv)[1]) {
+        return (char*)0;
+    } else if ((*p_argv)[0][1]) {
+        return &(*p_argv)[0][1];
+    } else {
+        *p_argv = *p_argv + 1;
+        *p_argc = *p_argc - 1;
+        return (*p_argv)[0];
+    }
+}
+
+/* use main(int argc, char *argv[]) */
+#define OPTBEGIN                                                              \
+    for (                                                                     \
+        ARGV0 = *argv, argc--, argv++;                                        \
+        argv[0] && argv[0][1] && argv[0][0] == '-';                           \
+        argc--, argv++                                                        \
+    ) {                                                                       \
+        char argc_ , **argv_, *optarg_;                                       \
+        int brk_;                                                             \
+        if (argv[0][1] == '-' && !argv[0][2]) {                               \
+            argv++, argc--; break;                                            \
+        }                                                                     \
+        for (brk_=0, argv[0]++, argv_=argv; argv[0][0] && !brk_; argv[0]++) { \
+            if (argv_ != argv) break;                                         \
+            argc_ = argv[0][0];                                               \
+            switch (argc_)
+
+#define OPTEND }}
+
+#define OPTC() (argc_)
+
+#define OPTARG() \
+    (optarg_ = getarg(&argc,&argv), brk_ = (optarg_!=0), optarg_)
+
+#define EOPTARG(code) \
+    (optarg_ = getarg(&argc,&argv), \
+     (!optarg_ ? ((code), abort(), (char*)0) : (brk_ = 1, optarg_)))
+
+#define OPTNUM \
+    case '0':  \
+    case '1':  \
+    case '2':  \
+    case '3':  \
+    case '4':  \
+    case '5':  \
+    case '6':  \
+    case '7':  \
+    case '8':  \
+    case '9'
+
+#define OPTLONG \
+    case '-'
+
+#endif
index c47450f3c2a689f4a0ded3d585e7138634512da3..85d6c81bca32a50d7831c5f8988215286653ac73 100644 (file)
@@ -5,5 +5,6 @@ int main(int argc, char** argv)
     (void)argc;
     (void)argv;
     RUN_EXTERN_TEST_SUITE(Opts);
+    RUN_EXTERN_TEST_SUITE(Arg);
     return PRINT_TEST_RESULTS();
 }
diff --git a/tests/test_opt.c b/tests/test_opt.c
new file mode 100644 (file)
index 0000000..b81a10d
--- /dev/null
@@ -0,0 +1,184 @@
+// Unit Test Framework Includes
+#include "atf.h"
+#include <stdbool.h>
+#include <string.h>
+
+// File To Test
+#include "opt.h"
+
+char* ARGV0;
+int argc;
+char** argv;
+
+bool Aborted = false;
+void abort(void) {
+    Aborted = true;
+}
+
+void dummy(){}
+
+//-----------------------------------------------------------------------------
+// Begin Unit Tests
+//-----------------------------------------------------------------------------
+TEST_SUITE(Arg) {
+    //-------------------------------------------------------------------------
+    // Short Option Parsing
+    //-------------------------------------------------------------------------
+    TEST(should parse an option with no argument)
+    {
+        char* args[] = { "prog", "-a", NULL };
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case 'a': CHECK(true); break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+    TEST(should parse two short options grouped together)
+    {
+        char* args[] = { "prog", "-ab", NULL };
+        bool a = false, b = false;
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case 'a': a = true; break;
+            case 'b': b = true; break;
+            default:  CHECK(false);
+        } OPTEND;
+        CHECK(a && b);
+    }
+
+    TEST(should parse three short options grouped together)
+    {
+        char* args[] = { "prog", "-abc", NULL };
+        bool a = false, b = false, c = false;
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case 'a': a = true; break;
+            case 'b': b = true; break;
+            case 'c': c = true; break;
+            default:  CHECK(false);
+        } OPTEND;
+        CHECK(a && b && c);
+    }
+
+    TEST(OPTARG should parse an option with an adjacent argument)
+    {
+        char* args[] = { "prog", "-afoo", NULL };
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case 'a':
+                CHECK('a' == OPTC());
+                CHECK(0 == strcmp("foo", OPTARG()));
+                break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+    TEST(OPTARG should parse an option with an argument)
+    {
+        char* args[] = { "prog", "-a", "foo", NULL };
+        argc = 3, argv = args;
+        OPTBEGIN {
+            case 'a':
+                CHECK('a' == OPTC());
+                CHECK(0 == strcmp("foo", OPTARG()));
+                break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+#if 1
+    TEST(OPTARG should return null if no argument found for option)
+    {
+        char* args[] = { "prog", "-a", NULL };
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case 'a':
+                CHECK('a' == OPTC());
+                CHECK(0 == OPTARG());
+                break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+
+    TEST(EOPTARG should parse an option with an adjacent argument)
+    {
+        char* args[] = { "prog", "-afoo", NULL };
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case 'a':
+                CHECK('a' == OPTC());
+                CHECK(0 == strcmp("foo", EOPTARG(dummy())));
+                break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+    TEST(EOPTARG should parse an option with an argument)
+    {
+        char* args[] = { "prog", "-a", "foo", NULL };
+        argc = 3, argv = args;
+        char* arg;
+        OPTBEGIN {
+            case 'a':
+                CHECK('a' == OPTC());
+                CHECK(0 == strcmp("foo", EOPTARG(dummy())));
+                break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+    TEST(EOPTARG should return abort if no argument found for option)
+    {
+        char* args[] = { "prog", "-a", NULL };
+        argc = 2, argv = args;
+        Aborted = false;
+        OPTBEGIN {
+            case 'a':
+                CHECK('a' == OPTC());
+                (void)EOPTARG(dummy());
+                goto done;
+                break;
+            default:  CHECK(false);
+        } OPTEND;
+        done: CHECK(Aborted);
+    }
+#endif
+
+    //-------------------------------------------------------------------------
+    // Miscellaneous Syntax
+    //-------------------------------------------------------------------------
+#if 1
+    TEST(Option processing should terminate at a --)
+    {
+        char* args[] = { "prog", "--", "-a", NULL };
+        argc = 3, argv = args;
+        OPTBEGIN {
+            default:  CHECK(false);
+        } OPTEND;
+        CHECK(0 == strcmp("-a", argv[0]));
+    }
+
+    TEST(Option processing should recognize long options)
+    {
+        char* args[] = { "prog", "--foo", NULL };
+        argc = 2, argv = args;
+        OPTBEGIN {
+            case '-': CHECK(0 == strcmp("foo", OPTARG())); break;
+            default:  CHECK(false);
+        } OPTEND;
+    }
+
+
+    TEST(Option processing should recognize - as a positional argument)
+    {
+        char* args[] = { "prog", "-", NULL };
+        argc = 2, argv = args;
+        OPTBEGIN {
+            default:  CHECK(false);
+        } OPTEND;
+        CHECK(0 == strcmp("-",argv[0]));
+    }
+#endif
+}