1 /*
2  * (c) 2008-2009 Frank Mehnert <fm3@os.inf.tu-dresden.de>,
3  *               Torsten Frenzel <frenzel@os.inf.tu-dresden.de>,
4  *               Jork Löser <jork@os.inf.tu-dresden.de>
5  *     economic rights: Technische Universität Dresden (Germany)
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU Lesser General Public License 2.1.
8  * Please see the COPYING-LGPL-2.1 file for details.
9  */
10 /*
11  * Parse the command-line for specified arguments and store the values into
12  * variables.
13  *
14  * For a more detailed documentation, see parse_cmd.h in the include dir.
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <alloca.h>
21 #include <string.h>
22 #include <l4/util/getopt.h>
23 #include <l4/util/parse_cmd.h>
24 
25 struct parse_cmdline_struct{
26     enum parse_cmd_type type;		// which type (int, switch, string)
27     char		shortform;	// short symbol
28     const char		*longform;	// long name
29     union{
30 	void		*argptr;	// ptr to variable getting the value
31 	parse_cmd_fn_t	fn;		// function to call
32 	parse_cmd_fn_arg_t fn_arg;	// function to call with args
33     } arg;
34     union{
35     	int		switch_to;	// value a switch sets
36     	const char*	default_string;	// default string value
37     	unsigned	default_int;	// default int value
38     	int		id;		// identifier to pass to function
39     }val;
40     const char		*comment;	// a description for the generated help
41 };
42 
43 #define TRASH(type, val) { type dummy __attribute__ ((unused)) = (val); }
44 
parse_cmdline(int * argc,const char *** argv,char arg0,...)45 L4_CV int parse_cmdline(int *argc, const char***argv, char arg0, ...){
46     va_list va;
47     int err;
48 
49     /* calculate the number of argument-descriptors */
50     va_start(va, arg0);
51     err = parse_cmdlinev(argc, argv, arg0, va);
52     va_end(va);
53     return err;
54 }
55 
parse_cmdlinev(int * argc,const char *** argv,char arg0,va_list va0)56 L4_CV int parse_cmdlinev(int *argc, const char***argv, char arg0, va_list va0){
57     va_list va;
58     int c, count, shortform, cur_longopt;
59     struct option *longopts, *longptr;
60     char *optstring, *optptr;
61     struct parse_cmdline_struct *pa;
62     int err;
63 
64     va_copy(va, va0);
65     /* calculate the number of argument-descriptors */
66     shortform = arg0;
67     for(count=0; shortform; count++){
68 	int type;
69 	int standard_int, *int_p;
70 	const char *standard_string, **string_p;
71 
72  	va_arg(va, const char*); /* skip long form */
73 	va_arg(va, const char*); /* skip comment */
74   	type = va_arg(va, int);
75   	switch(type){
76 	case PARSE_CMD_INT:
77 	    standard_int = va_arg(va, int);
78 	    int_p = va_arg(va, int*);
79 	    *int_p = standard_int;
80 	    break;
81 	case PARSE_CMD_SWITCH:
82 	    TRASH(int, va_arg(va, int));
83 	    TRASH(int*, va_arg(va, int*));
84 	    break;
85 	case PARSE_CMD_STRING:
86 	    standard_string = va_arg(va, char*);
87 	    string_p  = va_arg(va, const char**);
88 	    *string_p = standard_string;
89 	    break;
90 	case PARSE_CMD_FN:
91 	case PARSE_CMD_FN_ARG:
92 	    TRASH(int, va_arg(va, int));
93 	    TRASH(parse_cmd_fn_t, va_arg(va, parse_cmd_fn_t));
94 	    break;
95 	case PARSE_CMD_INC:
96 	    standard_int = va_arg(va, int);
97 	    int_p = va_arg(va, int*);
98 	    *int_p = standard_int;
99 	    break;
100 	case PARSE_CMD_DEC:
101 	    standard_int = va_arg(va, int);
102 	    int_p = va_arg(va, int*);
103 	    *int_p = standard_int;
104 	    break;
105 	default:
106 	    return -1;
107   	}
108   	shortform = va_arg(va, int);
109     }
110 
111     /* consider the --help and -h */
112     count++;
113 
114     /* allocate the fields for short options, long options and parse args */
115     longopts = (struct option*)alloca(sizeof(struct option)*(count+1));
116     if(longopts==0) return -2;
117 
118     optstring = (char*)alloca(count*2+1);
119     if(optstring==0) return -2;
120 
121     pa = (struct parse_cmdline_struct*)
122     	 alloca(count * sizeof(struct parse_cmdline_struct));
123     if(pa==0) return -2;
124 
125     /* fill in the short options field, longopts and parse args */
126     va_copy(va, va0);
127     shortform = arg0;
128     optptr    = optstring;
129     longptr   = longopts;
130 
131     /* Prefill the 'help' switches. We know it is the first entry, so
132        we can check for idx 0 when parsing the table. */
133     *optptr++='h';
134     pa->shortform = 'h';
135     pa->longform = "help";
136     pa->comment = "this help";
137     pa->type = PARSE_CMD_SWITCH;
138     longptr->name = pa->longform;
139     longptr->flag = &cur_longopt;
140     longptr->val = 0;
141     longptr->has_arg = 0;
142     longptr++;
143 
144     for(c=1;shortform; c++){
145 	if(shortform!=' ') *optptr++ = shortform;
146 	pa[c].shortform = shortform;
147 	pa[c].longform = va_arg(va, const char*);
148 	pa[c].comment = va_arg(va, const char*);
149 	pa[c].type = va_arg(va, int);
150 
151 	/* prefill a few of the longoptions fields */
152 	if(pa[c].longform){
153 	    longptr->name = pa[c].longform;
154 	    longptr->flag = &cur_longopt;
155 	    longptr->val = c;
156 	}
157 	switch(pa[c].type){
158 	case PARSE_CMD_INT:
159 	    if(shortform!=' ') *optptr++ = ':';
160 	    if(pa[c].longform) longptr->has_arg = 1;
161 
162 	    pa[c].val.default_int = va_arg(va, int);
163 	    pa[c].arg.argptr = va_arg(va, int*);
164 	    break;
165 
166 	case PARSE_CMD_SWITCH:
167 	    if(pa[c].longform) longptr->has_arg = 0;
168 
169 	    pa[c].val.switch_to = va_arg(va, int);
170 	    pa[c].arg.argptr = va_arg(va, int*);
171 	    break;
172 	case PARSE_CMD_STRING:
173 	    if(shortform!=' ') *optptr++ = ':';
174 	    if(pa[c].longform) longptr->has_arg = 1;
175 
176 	    pa[c].val.default_string = va_arg(va, char*);
177 	    pa[c].arg.argptr = va_arg(va, char**);
178 	    break;
179 	case PARSE_CMD_FN:
180 	    if(pa[c].longform) longptr->has_arg = 0;
181 
182 	    pa[c].val.id = va_arg(va, int);
183 	    pa[c].arg.fn = va_arg(va, parse_cmd_fn_t);
184 	    break;
185 	case PARSE_CMD_FN_ARG:
186 	    if(shortform!=' ') *optptr++ = ':';
187 	    if(pa[c].longform) longptr->has_arg = 1;
188 
189 	    pa[c].val.id = va_arg(va, int);
190 	    pa[c].arg.fn_arg = va_arg(va, parse_cmd_fn_arg_t);
191 	    break;
192 	case PARSE_CMD_INC:
193 	case PARSE_CMD_DEC:
194 	    if(pa[c].longform) longptr->has_arg = 0;
195 
196 	    TRASH(int, va_arg(va, int));
197 	    pa[c].arg.argptr = va_arg(va, int*);
198 	    break;
199   	}
200 
201 	if(pa[c].longform) longptr++;
202 	// next short form
203   	shortform = va_arg(va, int);
204     }
205 
206     // end the optstring string
207     *optptr=0;
208 
209     // end the longopt field
210     longptr->name=0;
211     longptr->has_arg=0;
212     longptr->flag=0;
213     longptr->val=0;
214 
215     err = -3;
216 
217     /* now, parse the arguments */
218     do{
219 	int val;
220 	int idx;
221 
222 	val = getopt_long_only(*argc, (char**)*argv, optstring, longopts, &idx);
223 	switch(val){
224 	case ':':
225 	    printf("Option -%c requires an argument\n",optopt);
226 	    goto e_help;
227 	case '?':
228 	    if(opterr){
229 	        printf("Unrecognized option: - %c\n", optopt ? optopt : '?');
230 		goto e_help;
231 	    }
232 	    break;
233 	case -1:
234 	    *argc-=optind;
235 	    *argv+=optind;
236 	    optind=1;
237 	    return 0;
238 	default:
239 	    /* we got an option. If it is a short option (val!=0),
240 	       lookup the index. */
241 	    if(val!=0){
242 		for(idx = 0; idx < count; idx++){
243 		    if(pa[idx].shortform == val) break;
244 		}
245 	    } else {
246 		/* it was a long option. We are lucky, the pa-element is
247 		   stored in the cur_longopt variable. */
248 		idx = cur_longopt;
249 	    }
250 	    if(idx == 0){
251 		err = -4;
252 		goto e_help;
253 	    }
254 	    if(idx<count){
255 		switch(pa[idx].type){
256 		case PARSE_CMD_INT:
257 		    *((int*)pa[idx].arg.argptr) = strtol(optarg, 0, 0);
258 		    break;
259 		case PARSE_CMD_SWITCH:
260 		    *((int*)pa[idx].arg.argptr) = pa[idx].val.switch_to;
261 		    break;
262 		case PARSE_CMD_STRING:
263 		    *((const char**)pa[idx].arg.argptr) = optarg;
264 		    break;
265 		case PARSE_CMD_FN:
266 		    pa[idx].arg.fn(pa[idx].val.id);
267 		    break;
268 		case PARSE_CMD_FN_ARG:
269 		    pa[idx].arg.fn_arg(pa[idx].val.id,
270 				       optarg, strtol(optarg, 0, 0));
271 		    break;
272 		case PARSE_CMD_INC:
273 		    (*((int*)pa[idx].arg.argptr))++;
274 		    break;
275 		case PARSE_CMD_DEC:
276 		    (*((int*)pa[idx].arg.argptr))--;
277 		    break;
278 		break;
279 		}
280 	    }
281 	    break;
282 	} // switch val
283     } while(1);
284 
285   e_help:
286     printf("Usage: %s <options>. Option list:\n", *argv[0]);
287     for(c=0;c<count;c++){
288 	int l;
289 	char buf[3];
290 
291 	if(pa[c].shortform!=' '){
292 		buf[0]='-';buf[1]=pa[c].shortform;buf[2]=0;
293 	} else {
294 		buf[0]=0;
295 	}
296 
297 	l = printf(" [ %s%s%s%s%s ]",
298 		   buf,
299 		   (buf[0] && pa[c].longform) ? " | " : "",
300 		   pa[c].longform ? "--" : "",
301 		   pa[c].longform ? pa[c].longform : "",
302 		   pa[c].type==PARSE_CMD_INT ? " num" :
303 		   pa[c].type==PARSE_CMD_STRING ? " string" : "");
304 	if(pa[c].comment) printf(" %*s- %s", l<25?25-l:0,
305 				 "", pa[c].comment);
306 	if(pa[c].type == PARSE_CMD_STRING)
307 		printf(" (\"%s\")", pa[c].val.default_string);
308 	if(pa[c].type == PARSE_CMD_INT)
309 		printf(" (%#x)", pa[c].val.default_int);
310 	printf("\n");
311     }
312   optind=1;
313   return err;
314 }
315 
parse_cmdline_extra(const char * argv0,const char * line,char delim,char arg0,...)316 L4_CV int parse_cmdline_extra(const char*argv0, const char*line, char delim,
317 			char arg0,...){
318     int i, argc_=1;
319     char*s, *line_=0;
320     const char**argv_;
321     va_list va;
322 
323     if(line && *line){
324 	if((line_ = strdupa(line))==0) return -2;
325 	argc_++;
326 	for(s=line_;*s;s++)if(*s==delim) argc_++;
327     }
328     argv_ = alloca(sizeof(char*)*argc_);
329     argv_[0]=argv0;
330     argc_=1;
331     s=line_;
332     if(line) while(*line){
333 	argv_[argc_]=s;
334 	while((*s=*line)!=0 && *s!=delim){line++; s++;}
335 	*s++=0;
336 	if(*line)line++;
337 	argc_++;
338     }
339     va_start(va, arg0);
340     i = parse_cmdlinev(&argc_, &argv_, arg0, va);
341     va_end(va);
342     return i;
343 }
344