| // 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, (size_t)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, (int)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, (int)strlen(name), 1); | |
| f->desc = desc; | |
| f->set = atollwhex; | |
| f->arg = p; | |
| } | |
| static void | |
| atolwhex(char *s, void *p) | |
| { | |
| char *t; | |
| *(int32*)p = (int32)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, (int)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, (int)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, (int)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, (int)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, (int)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 = (int)(q++ - name); | |
| else | |
| namelen = (int)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); | |
| } | |
| } |