xref: /freebsd/crypto/heimdal/lib/roken/getarg.c (revision b50c40f67bae697957b86b2aebcd0a9df83444e0)
1 /*
2  * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 RCSID("$Id: getarg.c,v 1.34 2000/02/13 21:06:43 assar Exp $");
37 #endif
38 
39 #include <stdio.h>
40 #include <roken.h>
41 #include "getarg.h"
42 
43 #define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
44 
45 static size_t
46 print_arg (char *string, size_t len, int mdoc, int longp, struct getargs *arg)
47 {
48     const char *s;
49 
50     *string = '\0';
51 
52     if (ISFLAG(*arg) || (!longp && arg->type == arg_counter))
53 	return 0;
54 
55     if(mdoc){
56 	if(longp)
57 	    strlcat(string, "= Ns", len);
58 	strlcat(string, " Ar ", len);
59     }else
60 	if (longp)
61 	    strlcat (string, "=", len);
62 	else
63 	    strlcat (string, " ", len);
64 
65     if (arg->arg_help)
66 	s = arg->arg_help;
67     else if (arg->type == arg_integer || arg->type == arg_counter)
68 	s = "integer";
69     else if (arg->type == arg_string)
70 	s = "string";
71     else if (arg->type == arg_double)
72 	s = "float";
73     else
74 	s = "<undefined>";
75 
76     strlcat(string, s, len);
77     return 1 + strlen(s);
78 }
79 
80 static void
81 mandoc_template(struct getargs *args,
82 		size_t num_args,
83 		const char *progname,
84 		const char *extra_string)
85 {
86     int i;
87     char timestr[64], cmd[64];
88     char buf[128];
89     const char *p;
90     time_t t;
91 
92     printf(".\\\" Things to fix:\n");
93     printf(".\\\"   * correct section, and operating system\n");
94     printf(".\\\"   * remove Op from mandatory flags\n");
95     printf(".\\\"   * use better macros for arguments (like .Pa for files)\n");
96     printf(".\\\"\n");
97     t = time(NULL);
98     strftime(timestr, sizeof(timestr), "%B %e, %Y", localtime(&t));
99     printf(".Dd %s\n", timestr);
100     p = strrchr(progname, '/');
101     if(p) p++; else p = progname;
102     strlcpy(cmd, p, sizeof(cmd));
103     strupr(cmd);
104 
105     printf(".Dt %s SECTION\n", cmd);
106     printf(".Os OPERATING_SYSTEM\n");
107     printf(".Sh NAME\n");
108     printf(".Nm %s\n", p);
109     printf(".Nd\n");
110     printf("in search of a description\n");
111     printf(".Sh SYNOPSIS\n");
112     printf(".Nm\n");
113     for(i = 0; i < num_args; i++){
114 	/* we seem to hit a limit on number of arguments if doing
115            short and long flags with arguments -- split on two lines */
116 	if(ISFLAG(args[i]) ||
117 	   args[i].short_name == 0 || args[i].long_name == NULL) {
118 	    printf(".Op ");
119 
120 	    if(args[i].short_name) {
121 		print_arg(buf, sizeof(buf), 1, 0, args + i);
122 		printf("Fl %c%s", args[i].short_name, buf);
123 		if(args[i].long_name)
124 		    printf(" | ");
125 	    }
126 	    if(args[i].long_name) {
127 		print_arg(buf, sizeof(buf), 1, 1, args + i);
128 		printf("Fl -%s%s%s",
129 		       args[i].type == arg_negative_flag ? "no-" : "",
130 		       args[i].long_name, buf);
131 	    }
132 	    printf("\n");
133 	} else {
134 	    print_arg(buf, sizeof(buf), 1, 0, args + i);
135 	    printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf);
136 	    print_arg(buf, sizeof(buf), 1, 1, args + i);
137 	    printf(".Fl -%s%s Oc\n.Xc\n", args[i].long_name, buf);
138 	}
139     /*
140 	    if(args[i].type == arg_strings)
141 		fprintf (stderr, "...");
142 		*/
143     }
144     if (extra_string && *extra_string)
145 	printf (".Ar %s\n", extra_string);
146     printf(".Sh DESCRIPTION\n");
147     printf("Supported options:\n");
148     printf(".Bl -tag -width Ds\n");
149     for(i = 0; i < num_args; i++){
150 	printf(".It Xo\n");
151 	if(args[i].short_name){
152 	    printf(".Fl %c", args[i].short_name);
153 	    print_arg(buf, sizeof(buf), 1, 0, args + i);
154 	    printf("%s", buf);
155 	    if(args[i].long_name)
156 		printf(" Ns ,");
157 	    printf("\n");
158 	}
159 	if(args[i].long_name){
160 	    printf(".Fl -%s%s",
161 		   args[i].type == arg_negative_flag ? "no-" : "",
162 		   args[i].long_name);
163 	    print_arg(buf, sizeof(buf), 1, 1, args + i);
164 	    printf("%s\n", buf);
165 	}
166 	printf(".Xc\n");
167 	if(args[i].help)
168 	    printf("%s\n", args[i].help);
169     /*
170 	    if(args[i].type == arg_strings)
171 		fprintf (stderr, "...");
172 		*/
173     }
174     printf(".El\n");
175     printf(".\\\".Sh ENVIRONMENT\n");
176     printf(".\\\".Sh FILES\n");
177     printf(".\\\".Sh EXAMPLES\n");
178     printf(".\\\".Sh DIAGNOSTICS\n");
179     printf(".\\\".Sh SEE ALSO\n");
180     printf(".\\\".Sh STANDARDS\n");
181     printf(".\\\".Sh HISTORY\n");
182     printf(".\\\".Sh AUTHORS\n");
183     printf(".\\\".Sh BUGS\n");
184 }
185 
186 static int
187 check_column(FILE *f, int col, int len, int columns)
188 {
189     if(col + len > columns) {
190 	fprintf(f, "\n");
191 	col = fprintf(f, "  ");
192     }
193     return col;
194 }
195 
196 void
197 arg_printusage (struct getargs *args,
198 		size_t num_args,
199 		const char *progname,
200 		const char *extra_string)
201 {
202     int i;
203     size_t max_len = 0;
204     char buf[128];
205     int col = 0, columns;
206     struct winsize ws;
207 
208     if (progname == NULL)
209 	progname = __progname;
210 
211     if(getenv("GETARGMANDOC")){
212 	mandoc_template(args, num_args, progname, extra_string);
213 	return;
214     }
215     if(get_window_size(2, &ws) == 0)
216 	columns = ws.ws_col;
217     else
218 	columns = 80;
219     col = 0;
220     col += fprintf (stderr, "Usage: %s", progname);
221     for (i = 0; i < num_args; ++i) {
222 	size_t len = 0;
223 
224 	if (args[i].long_name) {
225 	    buf[0] = '\0';
226 	    strlcat(buf, "[--", sizeof(buf));
227 	    len += 2;
228 	    if(args[i].type == arg_negative_flag) {
229 		strlcat(buf, "no-", sizeof(buf));
230 		len += 3;
231 	    }
232 	    strlcat(buf, args[i].long_name, sizeof(buf));
233 	    len += strlen(args[i].long_name);
234 	    len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
235 			     0, 1, &args[i]);
236 	    strlcat(buf, "]", sizeof(buf));
237 	    if(args[i].type == arg_strings)
238 		strlcat(buf, "...", sizeof(buf));
239 	    col = check_column(stderr, col, strlen(buf) + 1, columns);
240 	    col += fprintf(stderr, " %s", buf);
241 	}
242 	if (args[i].short_name) {
243 	    snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
244 	    len += 2;
245 	    len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
246 			     0, 0, &args[i]);
247 	    strlcat(buf, "]", sizeof(buf));
248 	    if(args[i].type == arg_strings)
249 		strlcat(buf, "...", sizeof(buf));
250 	    col = check_column(stderr, col, strlen(buf) + 1, columns);
251 	    col += fprintf(stderr, " %s", buf);
252 	}
253 	if (args[i].long_name && args[i].short_name)
254 	    len += 2; /* ", " */
255 	max_len = max(max_len, len);
256     }
257     if (extra_string) {
258 	col = check_column(stderr, col, strlen(extra_string) + 1, columns);
259 	fprintf (stderr, " %s\n", extra_string);
260     } else
261 	fprintf (stderr, "\n");
262     for (i = 0; i < num_args; ++i) {
263 	if (args[i].help) {
264 	    size_t count = 0;
265 
266 	    if (args[i].short_name) {
267 		count += fprintf (stderr, "-%c", args[i].short_name);
268 		print_arg (buf, sizeof(buf), 0, 0, &args[i]);
269 		count += fprintf(stderr, "%s", buf);
270 	    }
271 	    if (args[i].short_name && args[i].long_name)
272 		count += fprintf (stderr, ", ");
273 	    if (args[i].long_name) {
274 		count += fprintf (stderr, "--");
275 		if (args[i].type == arg_negative_flag)
276 		    count += fprintf (stderr, "no-");
277 		count += fprintf (stderr, "%s", args[i].long_name);
278 		print_arg (buf, sizeof(buf), 0, 1, &args[i]);
279 		count += fprintf(stderr, "%s", buf);
280 	    }
281 	    while(count++ <= max_len)
282 		putc (' ', stderr);
283 	    fprintf (stderr, "%s\n", args[i].help);
284 	}
285     }
286 }
287 
288 static void
289 add_string(getarg_strings *s, char *value)
290 {
291     s->strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
292     s->strings[s->num_strings] = value;
293     s->num_strings++;
294 }
295 
296 static int
297 arg_match_long(struct getargs *args, size_t num_args,
298 	       char *argv, int argc, char **rargv, int *optind)
299 {
300     int i;
301     char *optarg = NULL;
302     int negate = 0;
303     int partial_match = 0;
304     struct getargs *partial = NULL;
305     struct getargs *current = NULL;
306     int argv_len;
307     char *p;
308 
309     argv_len = strlen(argv);
310     p = strchr (argv, '=');
311     if (p != NULL)
312 	argv_len = p - argv;
313 
314     for (i = 0; i < num_args; ++i) {
315 	if(args[i].long_name) {
316 	    int len = strlen(args[i].long_name);
317 	    char *p = argv;
318 	    int p_len = argv_len;
319 	    negate = 0;
320 
321 	    for (;;) {
322 		if (strncmp (args[i].long_name, p, p_len) == 0) {
323 		    if(p_len == len)
324 			current = &args[i];
325 		    else {
326 			++partial_match;
327 			partial = &args[i];
328 		    }
329 		    optarg  = p + p_len;
330 		} else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
331 		    negate = !negate;
332 		    p += 3;
333 		    p_len -= 3;
334 		    continue;
335 		}
336 		break;
337 	    }
338 	    if (current)
339 		break;
340 	}
341     }
342     if (current == NULL) {
343 	if (partial_match == 1)
344 	    current = partial;
345 	else
346 	    return ARG_ERR_NO_MATCH;
347     }
348 
349     if(*optarg == '\0'
350        && !ISFLAG(*current)
351        && current->type != arg_collect
352        && current->type != arg_counter)
353 	return ARG_ERR_NO_MATCH;
354     switch(current->type){
355     case arg_integer:
356     {
357 	int tmp;
358 	if(sscanf(optarg + 1, "%d", &tmp) != 1)
359 	    return ARG_ERR_BAD_ARG;
360 	*(int*)current->value = tmp;
361 	return 0;
362     }
363     case arg_string:
364     {
365 	*(char**)current->value = optarg + 1;
366 	return 0;
367     }
368     case arg_strings:
369     {
370 	add_string((getarg_strings*)current->value, optarg + 1);
371 	return 0;
372     }
373     case arg_flag:
374     case arg_negative_flag:
375     {
376 	int *flag = current->value;
377 	if(*optarg == '\0' ||
378 	   strcmp(optarg + 1, "yes") == 0 ||
379 	   strcmp(optarg + 1, "true") == 0){
380 	    *flag = !negate;
381 	    return 0;
382 	} else if (*optarg && strcmp(optarg + 1, "maybe") == 0) {
383 	    *flag = rand() & 1;
384 	} else {
385 	    *flag = negate;
386 	    return 0;
387 	}
388 	return ARG_ERR_BAD_ARG;
389     }
390     case arg_counter :
391     {
392 	int val;
393 
394 	if (*optarg == '\0')
395 	    val = 1;
396 	else {
397 	    char *endstr;
398 
399 	    val = strtol (optarg, &endstr, 0);
400 	    if (endstr == optarg)
401 		return ARG_ERR_BAD_ARG;
402 	}
403 	*(int *)current->value += val;
404 	return 0;
405     }
406     case arg_double:
407     {
408 	double tmp;
409 	if(sscanf(optarg + 1, "%lf", &tmp) != 1)
410 	    return ARG_ERR_BAD_ARG;
411 	*(double*)current->value = tmp;
412 	return 0;
413     }
414     case arg_collect:{
415 	struct getarg_collect_info *c = current->value;
416 	int o = argv - rargv[*optind];
417 	return (*c->func)(FALSE, argc, rargv, optind, &o, c->data);
418     }
419 
420     default:
421 	abort ();
422     }
423 }
424 
425 static int
426 arg_match_short (struct getargs *args, size_t num_args,
427 		 char *argv, int argc, char **rargv, int *optind)
428 {
429     int j, k;
430 
431     for(j = 1; j > 0 && j < strlen(rargv[*optind]); j++) {
432 	for(k = 0; k < num_args; k++) {
433 	    char *optarg;
434 
435 	    if(args[k].short_name == 0)
436 		continue;
437 	    if(argv[j] == args[k].short_name) {
438 		if(args[k].type == arg_flag) {
439 		    *(int*)args[k].value = 1;
440 		    break;
441 		}
442 		if(args[k].type == arg_negative_flag) {
443 		    *(int*)args[k].value = 0;
444 		    break;
445 		}
446 		if(args[k].type == arg_counter) {
447 		    ++*(int *)args[k].value;
448 		    break;
449 		}
450 		if(args[k].type == arg_collect) {
451 		    struct getarg_collect_info *c = args[k].value;
452 
453 		    if((*c->func)(TRUE, argc, rargv, optind, &j, c->data))
454 			return ARG_ERR_BAD_ARG;
455 		    break;
456 		}
457 
458 		if(argv[j + 1])
459 		    optarg = &argv[j + 1];
460 		else {
461 		    ++*optind;
462 		    optarg = rargv[*optind];
463 		}
464 		if(optarg == NULL)
465 		    return ARG_ERR_NO_ARG;
466 		if(args[k].type == arg_integer) {
467 		    int tmp;
468 		    if(sscanf(optarg, "%d", &tmp) != 1)
469 			return ARG_ERR_BAD_ARG;
470 		    *(int*)args[k].value = tmp;
471 		    return 0;
472 		} else if(args[k].type == arg_string) {
473 		    *(char**)args[k].value = optarg;
474 		    return 0;
475 		} else if(args[k].type == arg_strings) {
476 		    add_string((getarg_strings*)args[k].value, optarg);
477 		    return 0;
478 		} else if(args[k].type == arg_double) {
479 		    double tmp;
480 		    if(sscanf(optarg, "%lf", &tmp) != 1)
481 			return ARG_ERR_BAD_ARG;
482 		    *(double*)args[k].value = tmp;
483 		    return 0;
484 		}
485 		return ARG_ERR_BAD_ARG;
486 	    }
487 	}
488 	if (k == num_args)
489 	    return ARG_ERR_NO_MATCH;
490     }
491     return 0;
492 }
493 
494 int
495 getarg(struct getargs *args, size_t num_args,
496        int argc, char **argv, int *optind)
497 {
498     int i;
499     int ret = 0;
500 
501     srand (time(NULL));
502     (*optind)++;
503     for(i = *optind; i < argc; i++) {
504 	if(argv[i][0] != '-')
505 	    break;
506 	if(argv[i][1] == '-'){
507 	    if(argv[i][2] == 0){
508 		i++;
509 		break;
510 	    }
511 	    ret = arg_match_long (args, num_args, argv[i] + 2,
512 				  argc, argv, &i);
513 	} else {
514 	    ret = arg_match_short (args, num_args, argv[i],
515 				   argc, argv, &i);
516 	}
517 	if(ret)
518 	    break;
519     }
520     *optind = i;
521     return ret;
522 }
523 
524 #if TEST
525 int foo_flag = 2;
526 int flag1 = 0;
527 int flag2 = 0;
528 int bar_int;
529 char *baz_string;
530 
531 struct getargs args[] = {
532     { NULL, '1', arg_flag, &flag1, "one", NULL },
533     { NULL, '2', arg_flag, &flag2, "two", NULL },
534     { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
535     { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
536     { "baz", 'x', arg_string, &baz_string, "baz", "name" },
537 };
538 
539 int main(int argc, char **argv)
540 {
541     int optind = 0;
542     while(getarg(args, 5, argc, argv, &optind))
543 	printf("Bad arg: %s\n", argv[optind]);
544     printf("flag1 = %d\n", flag1);
545     printf("flag2 = %d\n", flag2);
546     printf("foo_flag = %d\n", foo_flag);
547     printf("bar_int = %d\n", bar_int);
548     printf("baz_flag = %s\n", baz_string);
549     arg_printusage (args, 5, argv[0], "nothing here");
550 }
551 #endif
552