github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/lib9/flag.c (about)

     1  // Copyright 2012 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  #include <u.h>
     6  #include <libc.h>
     7  
     8  // Flag hash.
     9  typedef struct Flag Flag;
    10  
    11  struct Flag
    12  {
    13  	char *name;
    14  	int namelen;
    15  	char *desc;
    16  	int iscount;
    17  	void (*set)(char*, void*);
    18  	void (*set2)(char*, char*, void*);
    19  	void *arg;
    20  	Flag *next;
    21  	Flag *allnext;
    22  };
    23  
    24  static Flag *curflag;
    25  
    26  static Flag *fhash[512];
    27  static Flag *first, *last;
    28  
    29  char *argv0;
    30  
    31  /*
    32   * Mac OS can't deal with files that only declare data.
    33   * ARGBEGIN mentions this function so that this file gets pulled in.
    34   */
    35  void __fixargv0(void) { }
    36  
    37  // FNV-1 hash. http://isthe.com/chongo/tech/comp/fnv/
    38  static uint32
    39  fnv(char *p, int n)
    40  {
    41  	uint32 h;
    42  	
    43  	h = 2166136261U;
    44  	while(n-- > 0)
    45  		h = (h*16777619) ^ (uchar)*p++;
    46  	return h;
    47  }
    48  
    49  static Flag*
    50  lookflag(char *name, int namelen, int creat)
    51  {
    52  	uint32 h;
    53  	Flag *f;
    54  
    55  	h = fnv(name, namelen) & (nelem(fhash)-1);
    56  	for(f=fhash[h]; f; f=f->next) {
    57  		if(f->namelen == namelen && memcmp(f->name, name, (size_t)namelen) == 0) {
    58  			if(creat)
    59  				sysfatal("multiple definitions of flag -%s", name);
    60  			return f;
    61  		}
    62  	}
    63  	
    64  	if(!creat)
    65  		return nil;
    66  
    67  	f = malloc(sizeof *f);
    68  	if(f == nil)
    69  		sysfatal("out of memory");
    70  	memset(f, 0, sizeof *f);
    71  	f->name = name;
    72  	f->namelen = namelen;
    73  	f->next = fhash[h];
    74  	if(first == nil)
    75  		first = f;
    76  	else
    77  		last->allnext = f;
    78  	last = f;
    79  	fhash[h] = f;
    80  	return f;
    81  }
    82  
    83  static void
    84  count(char *arg, void *p)
    85  {
    86  	int *ip;
    87  	
    88  	ip = p;
    89  	if(arg != nil)
    90  		*ip = atoi(arg);
    91  	else
    92  		(*ip)++;
    93  }
    94  
    95  void
    96  flagcount(char *name, char *desc, int *p)
    97  {
    98  	Flag *f;
    99  	
   100  	f = lookflag(name, (int)strlen(name), 1);
   101  	f->desc = desc;
   102  	f->iscount = 1;
   103  	f->set = count;
   104  	f->arg = p;
   105  }
   106  
   107  static void
   108  atollwhex(char *s, void *p)
   109  {
   110  	char *t;
   111  
   112  	*(int64*)p = strtoll(s, &t, 0);
   113  	if(*s == '\0' || *t != '\0')
   114  		sysfatal("invalid numeric argument -%s=%s", curflag->name, s);
   115  }
   116  
   117  void
   118  flagint64(char *name, char *desc, int64 *p)
   119  {
   120  	Flag *f;
   121  	
   122  	f = lookflag(name, (int)strlen(name), 1);
   123  	f->desc = desc;
   124  	f->set = atollwhex;
   125  	f->arg = p;
   126  }
   127  
   128  static void
   129  atolwhex(char *s, void *p)
   130  {
   131  	char *t;
   132  
   133  	*(int32*)p = (int32)strtol(s, &t, 0);
   134  	if(*s == '\0' || *t != '\0')
   135  		sysfatal("invalid numeric argument -%s=%s", curflag->name, s);
   136  }
   137  
   138  void
   139  flagint32(char *name, char *desc, int32 *p)
   140  {
   141  	Flag *f;
   142  	
   143  	f = lookflag(name, (int)strlen(name), 1);
   144  	f->desc = desc;
   145  	f->set = atolwhex;
   146  	f->arg = p;
   147  }
   148  
   149  static void
   150  string(char *s, void *p)
   151  {
   152  	*(char**)p = s;
   153  }
   154  
   155  void
   156  flagstr(char *name, char *desc, char **p)
   157  {
   158  
   159  	Flag *f;
   160  	
   161  	f = lookflag(name, (int)strlen(name), 1);
   162  	f->desc = desc;
   163  	f->set = string;
   164  	f->arg = p;
   165  }	
   166  
   167  static void
   168  fn0(char *s, void *p)
   169  {
   170  	USED(s);
   171  	((void(*)(void))p)();
   172  }
   173  
   174  void
   175  flagfn0(char *name, char *desc, void (*fn)(void))
   176  {
   177  	Flag *f;
   178  	
   179  	f = lookflag(name, (int)strlen(name), 1);
   180  	f->desc = desc;
   181  	f->set = fn0;
   182  	f->arg = fn;
   183  	f->iscount = 1;
   184  }
   185  
   186  static void
   187  fn1(char *s, void *p)
   188  {
   189  	((void(*)(char*))p)(s);
   190  }
   191  
   192  void
   193  flagfn1(char *name, char *desc, void (*fn)(char*))
   194  {
   195  	Flag *f;
   196  	
   197  	f = lookflag(name, (int)strlen(name), 1);
   198  	f->desc = desc;
   199  	f->set = fn1;
   200  	f->arg = fn;
   201  }
   202  
   203  static void
   204  fn2(char *s, char *t, void *p)
   205  {
   206  	((void(*)(char*, char*))p)(s, t);
   207  }
   208  
   209  void
   210  flagfn2(char *name, char *desc, void (*fn)(char*, char*))
   211  {
   212  	Flag *f;
   213  	
   214  	f = lookflag(name, (int)strlen(name), 1);
   215  	f->desc = desc;
   216  	f->set2 = fn2;
   217  	f->arg = fn;
   218  }
   219  
   220  void
   221  flagparse(int *argcp, char ***argvp, void (*usage)(void))
   222  {
   223  	int argc;
   224  	char **argv, *p, *q;
   225  	char *name;
   226  	int namelen;
   227  	Flag *f;
   228  	
   229  	argc = *argcp;
   230  	argv = *argvp;
   231  
   232  	argv0 = argv[0];
   233  	argc--;
   234  	argv++;
   235  	
   236  	while(argc > 0) {
   237  		p = *argv;
   238  		// stop before non-flag or -
   239  		if(*p != '-' || p[1] == '\0')
   240  			break;
   241  		argc--;
   242  		argv++;
   243  		// stop after --
   244  		if(p[1] == '-' && p[2] == '\0') {
   245  			break;
   246  		}
   247  		
   248  		// turn --foo into -foo
   249  		if(p[1] == '-' && p[2] != '-')
   250  			p++;
   251  		
   252  		// allow -flag=arg if present
   253  		name = p+1;
   254  		q = strchr(name, '=');
   255  		if(q != nil)
   256  			namelen = (int)(q++ - name);
   257  		else
   258  			namelen = (int)strlen(name);
   259  		f = lookflag(name, namelen, 0);
   260  		if(f == nil) {
   261  			if(strcmp(p, "-h") == 0 || strcmp(p, "-help") == 0 || strcmp(p, "-?") == 0)
   262  				usage();
   263  			sysfatal("unknown flag %s", p);
   264  		}
   265  		curflag = f;
   266  
   267  		// otherwise consume next argument if non-boolean
   268  		if(!f->iscount && q == nil) {
   269  			if(argc-- == 0)
   270  				sysfatal("missing argument to flag %s", p);
   271  			q = *argv++;
   272  		}
   273  		
   274  		// and another if we need two
   275  		if(f->set2 != nil) {
   276  			if(argc-- == 0)
   277  				sysfatal("missing second argument to flag %s", p);
   278  			f->set2(q, *argv++, f->arg);
   279  			continue;
   280  		}
   281  
   282  		f->set(q, f->arg);			
   283  	}
   284  	
   285  	*argcp = argc;
   286  	*argvp = argv;		
   287  }
   288  
   289  void
   290  flagprint(int fd)
   291  {
   292  	Flag *f;
   293  	char *p, *q;
   294  	
   295  	for(f=first; f; f=f->allnext) {
   296  		p = f->desc;
   297  		if(p == nil || *p == '\0') // undocumented flag
   298  			continue;
   299  		q = strstr(p, ": ");
   300  		if(q)
   301  			fprint(fd, "  -%s %.*s\n    \t%s\n", f->name, utfnlen(p, q-p), p, q+2);
   302  		else if(f->namelen > 1)
   303  			fprint(fd, "  -%s\n    \t%s\n", f->name, p);
   304  		else
   305  			fprint(fd, "  -%s\t%s\n", f->name, p);
   306  	}
   307  }