summaryrefslogtreecommitdiff
path: root/src/lib9/flag.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib9/flag.c')
-rw-r--r--src/lib9/flag.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/lib9/flag.c b/src/lib9/flag.c
new file mode 100644
index 000000000..7c79c1a6d
--- /dev/null
+++ b/src/lib9/flag.c
@@ -0,0 +1,307 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+
+// Flag hash.
+typedef struct Flag Flag;
+
+struct Flag
+{
+ char *name;
+ int namelen;
+ char *desc;
+ int iscount;
+ void (*set)(char*, void*);
+ void (*set2)(char*, char*, void*);
+ void *arg;
+ Flag *next;
+ Flag *allnext;
+};
+
+static Flag *curflag;
+
+static Flag *fhash[512];
+static Flag *first, *last;
+
+char *argv0;
+
+/*
+ * Mac OS can't deal with files that only declare data.
+ * ARGBEGIN mentions this function so that this file gets pulled in.
+ */
+void __fixargv0(void) { }
+
+// FNV-1 hash. http://isthe.com/chongo/tech/comp/fnv/
+static uint32
+fnv(char *p, int n)
+{
+ uint32 h;
+
+ h = 2166136261U;
+ while(n-- > 0)
+ h = (h*16777619) ^ (uchar)*p++;
+ return h;
+}
+
+static Flag*
+lookflag(char *name, int namelen, int creat)
+{
+ uint32 h;
+ Flag *f;
+
+ h = fnv(name, namelen) & (nelem(fhash)-1);
+ for(f=fhash[h]; f; f=f->next) {
+ if(f->namelen == namelen && memcmp(f->name, name, namelen) == 0) {
+ if(creat)
+ sysfatal("multiple definitions of flag -%s", name);
+ return f;
+ }
+ }
+
+ if(!creat)
+ return nil;
+
+ f = malloc(sizeof *f);
+ if(f == nil)
+ sysfatal("out of memory");
+ memset(f, 0, sizeof *f);
+ f->name = name;
+ f->namelen = namelen;
+ f->next = fhash[h];
+ if(first == nil)
+ first = f;
+ else
+ last->allnext = f;
+ last = f;
+ fhash[h] = f;
+ return f;
+}
+
+static void
+count(char *arg, void *p)
+{
+ int *ip;
+
+ ip = p;
+ if(arg != nil)
+ *ip = atoi(arg);
+ else
+ (*ip)++;
+}
+
+void
+flagcount(char *name, char *desc, int *p)
+{
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->iscount = 1;
+ f->set = count;
+ f->arg = p;
+}
+
+static void
+atollwhex(char *s, void *p)
+{
+ char *t;
+
+ *(int64*)p = strtoll(s, &t, 0);
+ if(*s == '\0' || *t != '\0')
+ sysfatal("invalid numeric argument -%s=%s", curflag->name, s);
+}
+
+void
+flagint64(char *name, char *desc, int64 *p)
+{
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->set = atollwhex;
+ f->arg = p;
+}
+
+static void
+atolwhex(char *s, void *p)
+{
+ char *t;
+
+ *(int32*)p = strtol(s, &t, 0);
+ if(*s == '\0' || *t != '\0')
+ sysfatal("invalid numeric argument -%s=%s", curflag->name, s);
+}
+
+void
+flagint32(char *name, char *desc, int32 *p)
+{
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->set = atolwhex;
+ f->arg = p;
+}
+
+static void
+string(char *s, void *p)
+{
+ *(char**)p = s;
+}
+
+void
+flagstr(char *name, char *desc, char **p)
+{
+
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->set = string;
+ f->arg = p;
+}
+
+static void
+fn0(char *s, void *p)
+{
+ USED(s);
+ ((void(*)(void))p)();
+}
+
+void
+flagfn0(char *name, char *desc, void (*fn)(void))
+{
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->set = fn0;
+ f->arg = fn;
+ f->iscount = 1;
+}
+
+static void
+fn1(char *s, void *p)
+{
+ ((void(*)(char*))p)(s);
+}
+
+void
+flagfn1(char *name, char *desc, void (*fn)(char*))
+{
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->set = fn1;
+ f->arg = fn;
+}
+
+static void
+fn2(char *s, char *t, void *p)
+{
+ ((void(*)(char*, char*))p)(s, t);
+}
+
+void
+flagfn2(char *name, char *desc, void (*fn)(char*, char*))
+{
+ Flag *f;
+
+ f = lookflag(name, strlen(name), 1);
+ f->desc = desc;
+ f->set2 = fn2;
+ f->arg = fn;
+}
+
+void
+flagparse(int *argcp, char ***argvp, void (*usage)(void))
+{
+ int argc;
+ char **argv, *p, *q;
+ char *name;
+ int namelen;
+ Flag *f;
+
+ argc = *argcp;
+ argv = *argvp;
+
+ argv0 = argv[0];
+ argc--;
+ argv++;
+
+ while(argc > 0) {
+ p = *argv;
+ // stop before non-flag or -
+ if(*p != '-' || p[1] == '\0')
+ break;
+ argc--;
+ argv++;
+ // stop after --
+ if(p[1] == '-' && p[2] == '\0') {
+ break;
+ }
+
+ // turn --foo into -foo
+ if(p[1] == '-' && p[2] != '-')
+ p++;
+
+ // allow -flag=arg if present
+ name = p+1;
+ q = strchr(name, '=');
+ if(q != nil)
+ namelen = q++ - name;
+ else
+ namelen = strlen(name);
+ f = lookflag(name, namelen, 0);
+ if(f == nil) {
+ if(strcmp(p, "-h") == 0 || strcmp(p, "-help") == 0 || strcmp(p, "-?") == 0)
+ usage();
+ sysfatal("unknown flag %s", p);
+ }
+ curflag = f;
+
+ // otherwise consume next argument if non-boolean
+ if(!f->iscount && q == nil) {
+ if(argc-- == 0)
+ sysfatal("missing argument to flag %s", p);
+ q = *argv++;
+ }
+
+ // and another if we need two
+ if(f->set2 != nil) {
+ if(argc-- == 0)
+ sysfatal("missing second argument to flag %s", p);
+ f->set2(q, *argv++, f->arg);
+ continue;
+ }
+
+ f->set(q, f->arg);
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+}
+
+void
+flagprint(int fd)
+{
+ Flag *f;
+ char *p, *q;
+
+ for(f=first; f; f=f->allnext) {
+ p = f->desc;
+ if(p == nil || *p == '\0') // undocumented flag
+ continue;
+ q = strstr(p, ": ");
+ if(q)
+ fprint(fd, " -%s %.*s\n \t%s\n", f->name, utfnlen(p, q-p), p, q+2);
+ else if(f->namelen > 1)
+ fprint(fd, " -%s\n \t%s\n", f->name, p);
+ else
+ fprint(fd, " -%s\t%s\n", f->name, p);
+ }
+}