blob: db46b980950667ff4ef34652a8336b9687dd33c6 [file] [log] [blame] [edit]
// 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);
}
}