1 /* -*- c-basic-offset: 4; -*- */
2 /* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
3 
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2.0,
6   as published by the Free Software Foundation.
7 
8   This program is also distributed with certain software (including
9   but not limited to OpenSSL) that is licensed under separate terms,
10   as designated in a particular file or component or in included license
11   documentation.  The authors of MySQL hereby grant you an additional
12   permission to link the program and your derivative works with the
13   separately licensed software that they have included with MySQL.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License, version 2.0, for more details.
19 
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
23 
24 #include <ndb_global.h>
25 
26 #include "getarg.h"
27 
28 #ifndef HAVE_STRLCPY
29 static size_t
strlcpy(char * dst,const char * src,size_t dst_sz)30 strlcpy (char *dst, const char *src, size_t dst_sz)
31 {
32     size_t n;
33     char *p;
34     for (p = dst, n = 0;
35 	 n + 1 < dst_sz && *src != '\0';
36 	 ++p, ++src, ++n)
37 	*p = *src;
38     *p = '\0';
39     if (*src == '\0')
40 	return n;
41     else
42 	return n + strlen (src);
43 }
44 #endif
45 #ifndef HAVE_STRLCAT
46 static size_t
strlcat(char * dst,const char * src,size_t dst_sz)47 strlcat (char *dst, const char *src, size_t dst_sz)
48 {
49     size_t len = strlen(dst);
50     return len + strlcpy (dst + len, src, dst_sz - len);
51 }
52 #endif
53 
54 #define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
55 
56 #ifndef max
57 #define max(a, b) (a) > (b) ? (a) : (b)
58 #endif
59 
60 #ifdef HAVE___PROGNAME
61 extern char *__progname;
62 #endif
63 
64 #ifndef TRUE
65 #define TRUE 1
66 #endif
67 
68 #ifndef FALSE
69 #define FALSE 0
70 #endif
71 
72 char *
strupr(char * str)73 strupr(char *str)
74 {
75   char *s;
76 
77   for(s = str; *s; s++)
78     *s = toupper(*s);
79   return str;
80 }
81 
82 static size_t
print_arg(char * string,size_t len,int mdoc,int longp,struct getargs * arg)83 print_arg (char *string, size_t len, int mdoc, int longp, struct getargs *arg)
84 {
85     const char *s;
86 
87     *string = '\0';
88 
89     if (ISFLAG(*arg) || (!longp && arg->type == arg_counter))
90 	return 0;
91 
92     if(mdoc){
93 	if(longp)
94 	    strlcat(string, "= Ns", len);
95 	strlcat(string, " Ar ", len);
96     }else
97 	if (longp)
98 	    strlcat (string, "=", len);
99 	else
100 	    strlcat (string, " ", len);
101 
102     if (arg->arg_help)
103 	s = arg->arg_help;
104     else if (arg->type == arg_integer || arg->type == arg_counter)
105 	s = "integer";
106     else if (arg->type == arg_string)
107 	s = "string";
108     else if (arg->type == arg_double)
109 	s = "float";
110     else
111 	s = "<undefined>";
112 
113     strlcat(string, s, len);
114     return 1 + strlen(s);
115 }
116 
117 #ifdef GETARGMANDOC
118 static void
mandoc_template(struct getargs * args,size_t num_args,const char * progname,const char * extra_string)119 mandoc_template(struct getargs *args,
120 		size_t num_args,
121 		const char *progname,
122 		const char *extra_string)
123 {
124     size_t i;
125     char timestr[64], cmd[64];
126     char buf[128];
127     const char *p;
128     time_t t;
129 
130     printf(".\\\" Things to fix:\n");
131     printf(".\\\"   * correct section, and operating system\n");
132     printf(".\\\"   * remove Op from mandatory flags\n");
133     printf(".\\\"   * use better macros for arguments (like .Pa for files)\n");
134     printf(".\\\"\n");
135     t = time(NULL);
136     strftime(timestr, sizeof(timestr), "%B %e, %Y", localtime(&t));
137     printf(".Dd %s\n", timestr);
138     p = strrchr(progname, '/');
139     if(p) p++; else p = progname;
140     strlcpy(cmd, p, sizeof(cmd));
141     strupr(cmd);
142 
143     printf(".Dt %s SECTION\n", cmd);
144     printf(".Os OPERATING_SYSTEM\n");
145     printf(".Sh NAME\n");
146     printf(".Nm %s\n", p);
147     printf(".Nd\n");
148     printf("in search of a description\n");
149     printf(".Sh SYNOPSIS\n");
150     printf(".Nm\n");
151     for(i = 0; i < num_args; i++){
152 	/* we seem to hit a limit on number of arguments if doing
153            short and long flags with arguments -- split on two lines */
154 	if(ISFLAG(args[i]) ||
155 	   args[i].short_name == 0 || args[i].long_name == NULL) {
156 	    printf(".Op ");
157 
158 	    if(args[i].short_name) {
159 		print_arg(buf, sizeof(buf), 1, 0, args + i);
160 		printf("Fl %c%s", args[i].short_name, buf);
161 		if(args[i].long_name)
162 		    printf(" | ");
163 	    }
164 	    if(args[i].long_name) {
165 		print_arg(buf, sizeof(buf), 1, 1, args + i);
166 		printf("Fl -%s%s%s",
167 		       args[i].type == arg_negative_flag ? "no-" : "",
168 		       args[i].long_name, buf);
169 	    }
170 	    printf("\n");
171 	} else {
172 	    print_arg(buf, sizeof(buf), 1, 0, args + i);
173 	    printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf);
174 	    print_arg(buf, sizeof(buf), 1, 1, args + i);
175 	    printf(".Fl -%s%s Oc\n.Xc\n", args[i].long_name, buf);
176 	}
177     /*
178 	    if(args[i].type == arg_strings)
179 		fprintf (stderr, "...");
180 		*/
181     }
182     if (extra_string && *extra_string)
183 	printf (".Ar %s\n", extra_string);
184     printf(".Sh DESCRIPTION\n");
185     printf("Supported options:\n");
186     printf(".Bl -tag -width Ds\n");
187     for(i = 0; i < num_args; i++){
188 	printf(".It Xo\n");
189 	if(args[i].short_name){
190 	    printf(".Fl %c", args[i].short_name);
191 	    print_arg(buf, sizeof(buf), 1, 0, args + i);
192 	    printf("%s", buf);
193 	    if(args[i].long_name)
194 		printf(" Ns ,");
195 	    printf("\n");
196 	}
197 	if(args[i].long_name){
198 	    printf(".Fl -%s%s",
199 		   args[i].type == arg_negative_flag ? "no-" : "",
200 		   args[i].long_name);
201 	    print_arg(buf, sizeof(buf), 1, 1, args + i);
202 	    printf("%s\n", buf);
203 	}
204 	printf(".Xc\n");
205 	if(args[i].help)
206 	    printf("%s\n", args[i].help);
207     /*
208 	    if(args[i].type == arg_strings)
209 		fprintf (stderr, "...");
210 		*/
211     }
212     printf(".El\n");
213     printf(".\\\".Sh ENVIRONMENT\n");
214     printf(".\\\".Sh FILES\n");
215     printf(".\\\".Sh EXAMPLES\n");
216     printf(".\\\".Sh DIAGNOSTICS\n");
217     printf(".\\\".Sh SEE ALSO\n");
218     printf(".\\\".Sh STANDARDS\n");
219     printf(".\\\".Sh HISTORY\n");
220     printf(".\\\".Sh AUTHORS\n");
221     printf(".\\\".Sh BUGS\n");
222 }
223 #endif /* GETARGMANDOC */
224 
225 static int
check_column(FILE * f,int col,int len,int columns)226 check_column(FILE *f, int col, int len, int columns)
227 {
228     if(col + len > columns) {
229 	fprintf(f, "\n");
230 	col = fprintf(f, "  ");
231     }
232     return col;
233 }
234 
235 void
arg_printusage(struct getargs * args,size_t num_args,const char * progname,const char * extra_string)236 arg_printusage (struct getargs *args,
237 		size_t num_args,
238 		const char *progname,
239 		const char *extra_string)
240 {
241     unsigned int i;
242     size_t max_len = 0;
243     char buf[128];
244     int col = 0, columns;
245 
246 #ifdef HAVE___PROGNAME
247     if (progname == NULL)
248 	progname = __progname;
249 #endif
250     if (progname == NULL)
251 	progname = "";
252 
253 #ifdef GETARGMANDOC
254     if(getenv("GETARGMANDOC")){
255 	mandoc_template(args, num_args, progname, extra_string);
256 	return;
257     }
258 #endif
259 
260     columns = 80; /* Always assume that the window is 80 chars wide */
261     col = 0;
262     col += fprintf (stderr, "Usage: %s", progname);
263     for (i = 0; i < num_args; ++i) {
264 	size_t len = 0;
265 
266 	if (args[i].long_name) {
267 	    buf[0] = '\0';
268 	    strlcat(buf, "[--", sizeof(buf));
269 	    len += 2;
270 	    if(args[i].type == arg_negative_flag) {
271 		strlcat(buf, "no-", sizeof(buf));
272 		len += 3;
273 	    }
274 	    strlcat(buf, args[i].long_name, sizeof(buf));
275 	    len += strlen(args[i].long_name);
276 	    len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
277 			     0, 1, &args[i]);
278 	    strlcat(buf, "]", sizeof(buf));
279 	    if(args[i].type == arg_strings)
280 		strlcat(buf, "...", sizeof(buf));
281 	    col = check_column(stderr, col, (int)strlen(buf) + 1, columns);
282 	    col += fprintf(stderr, " %s", buf);
283 	}
284 	if (args[i].short_name) {
285             snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
286 	    len += 2;
287 	    len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
288 			     0, 0, &args[i]);
289 	    strlcat(buf, "]", sizeof(buf));
290 	    if(args[i].type == arg_strings)
291 		strlcat(buf, "...", sizeof(buf));
292 	    col = check_column(stderr, col, (int)strlen(buf) + 1, columns);
293 	    col += fprintf(stderr, " %s", buf);
294 	}
295 	if (args[i].long_name && args[i].short_name)
296 	    len += 2; /* ", " */
297 	max_len = max(max_len, len);
298     }
299     if (extra_string) {
300 	col = check_column(stderr, col, (int)strlen(extra_string) + 1, columns);
301 	fprintf (stderr, " %s\n", extra_string);
302     } else
303 	fprintf (stderr, "\n");
304     for (i = 0; i < num_args; ++i) {
305 	if (args[i].help) {
306 	    size_t count = 0;
307 
308 	    if (args[i].short_name) {
309 		count += fprintf (stderr, "-%c", args[i].short_name);
310 		print_arg (buf, sizeof(buf), 0, 0, &args[i]);
311 		count += fprintf(stderr, "%s", buf);
312 	    }
313 	    if (args[i].short_name && args[i].long_name)
314 		count += fprintf (stderr, ", ");
315 	    if (args[i].long_name) {
316 		count += fprintf (stderr, "--");
317 		if (args[i].type == arg_negative_flag)
318 		    count += fprintf (stderr, "no-");
319 		count += fprintf (stderr, "%s", args[i].long_name);
320 		print_arg (buf, sizeof(buf), 0, 1, &args[i]);
321 		count += fprintf(stderr, "%s", buf);
322 	    }
323 	    while(count++ <= max_len)
324 		putc (' ', stderr);
325 	    fprintf (stderr, "%s\n", args[i].help);
326 	}
327     }
328 }
329 
330 static void
add_string(getarg_strings * s,char * value)331 add_string(getarg_strings *s, char *value)
332 {
333     s->strings = (char **)realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
334     s->strings[s->num_strings] = value;
335     s->num_strings++;
336 }
337 
338 static int
arg_match_long(struct getargs * args,size_t num_args,char * argv,int argc,const char ** rargv,int * optind)339 arg_match_long(struct getargs *args, size_t num_args,
340 	       char *argv, int argc, const char **rargv, int *optind)
341 {
342     unsigned int i;
343     const char *optarg = NULL;
344     int negate = 0;
345     int partial_match = 0;
346     struct getargs *partial = NULL;
347     struct getargs *current = NULL;
348     int argv_len;
349     char *p;
350 
351     argv_len = (int)strlen(argv);
352     p = strchr (argv, '=');
353     if (p != NULL)
354 	argv_len = (int)(p - argv);
355 
356     for (i = 0; i < num_args; ++i) {
357 	if(args[i].long_name) {
358 	    int len = (int)strlen(args[i].long_name);
359 	    char *p = argv;
360 	    int p_len = argv_len;
361 	    negate = 0;
362 
363 	    for (;;) {
364 		if (strncmp (args[i].long_name, p, p_len) == 0) {
365 		    if(p_len == len)
366 			current = &args[i];
367 		    else {
368 			++partial_match;
369 			partial = &args[i];
370 		    }
371 		    optarg  = p + p_len;
372 		} else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
373 		    negate = !negate;
374 		    p += 3;
375 		    p_len -= 3;
376 		    continue;
377 		}
378 		break;
379 	    }
380 	    if (current)
381 		break;
382 	}
383     }
384     if (current == NULL) {
385 	if (partial_match == 1)
386 	    current = partial;
387 	else
388 	    return ARG_ERR_NO_MATCH;
389     }
390 
391     if(*optarg == '\0'
392        && !ISFLAG(*current)
393        && current->type != arg_collect
394        && current->type != arg_counter)
395 	return ARG_ERR_NO_MATCH;
396     switch(current->type){
397     case arg_integer:
398     {
399 	int tmp;
400 	if(sscanf(optarg + 1, "%d", &tmp) != 1)
401 	    return ARG_ERR_BAD_ARG;
402 	*(int*)current->value = tmp;
403 	return 0;
404     }
405     case arg_string:
406     {
407 	*(char**)current->value = (char*)optarg + 1;
408 	return 0;
409     }
410     case arg_strings:
411     {
412 	add_string((getarg_strings*)current->value, (char*)optarg + 1);
413 	return 0;
414     }
415     case arg_flag:
416     case arg_negative_flag:
417     {
418 	int *flag = (int *)current->value;
419 	if(*optarg == '\0' ||
420 	   strcmp(optarg + 1, "yes") == 0 ||
421 	   strcmp(optarg + 1, "true") == 0){
422 	    *flag = !negate;
423 	    return 0;
424 	} else if (*optarg && strcmp(optarg + 1, "maybe") == 0) {
425 	    *flag = rand() & 1;
426 	} else {
427 	    *flag = negate;
428 	    return 0;
429 	}
430 	return ARG_ERR_BAD_ARG;
431     }
432     case arg_counter :
433     {
434 	int val;
435 
436 	if (*optarg == '\0')
437 	    val = 1;
438 	else {
439 	    char *endstr;
440 
441 	    val = strtol (optarg, &endstr, 0);
442 	    if (endstr == optarg)
443 		return ARG_ERR_BAD_ARG;
444 	}
445 	*(int *)current->value += val;
446 	return 0;
447     }
448     case arg_double:
449     {
450 	double tmp;
451 	if(sscanf(optarg + 1, "%lf", &tmp) != 1)
452 	    return ARG_ERR_BAD_ARG;
453 	*(double*)current->value = tmp;
454 	return 0;
455     }
456     case arg_collect:{
457 	struct getarg_collect_info *c = (getarg_collect_info *)current->value;
458 	int o = (int)(argv - rargv[*optind]);
459 	return (*c->func)(FALSE, argc, rargv, optind, &o, c->data);
460     }
461 
462     default:
463 	abort ();
464 	return ARG_ERR_BAD_ARG;
465     }
466 }
467 
468 static int
arg_match_short(struct getargs * args,size_t num_args,char * argv,int argc,const char ** rargv,int * optind)469 arg_match_short (struct getargs *args, size_t num_args,
470 		 char *argv, int argc, const char **rargv, int *optind)
471 {
472     int j, k;
473 
474     for(j = 1; j > 0 && j < (int)strlen(rargv[*optind]); j++) {
475 	for(k = 0; k < (int)num_args; k++) {
476 	    char *optarg;
477 
478 	    if(args[k].short_name == 0)
479 		continue;
480 	    if(argv[j] == args[k].short_name) {
481 		if(args[k].type == arg_flag) {
482 		    *(int*)args[k].value = 1;
483 		    break;
484 		}
485 		if(args[k].type == arg_negative_flag) {
486 		    *(int*)args[k].value = 0;
487 		    break;
488 		}
489 		if(args[k].type == arg_counter) {
490 		    ++*(int *)args[k].value;
491 		    break;
492 		}
493 		if(args[k].type == arg_collect) {
494 		    struct getarg_collect_info *c = (getarg_collect_info *)args[k].value;
495 
496 		    if((*c->func)(TRUE, argc, rargv, optind, &j, c->data))
497 			return ARG_ERR_BAD_ARG;
498 		    break;
499 		}
500 
501 		if(argv[j + 1])
502 		    optarg = &argv[j + 1];
503 		else {
504 		    ++*optind;
505 		    optarg = (char *) rargv[*optind];
506 		}
507 		if(optarg == NULL) {
508 		    --*optind;
509 		    return ARG_ERR_NO_ARG;
510 		}
511 		if(args[k].type == arg_integer) {
512 		    int tmp;
513 		    if(sscanf(optarg, "%d", &tmp) != 1)
514 			return ARG_ERR_BAD_ARG;
515 		    *(int*)args[k].value = tmp;
516 		    return 0;
517 		} else if(args[k].type == arg_string) {
518 		    *(char**)args[k].value = optarg;
519 		    return 0;
520 		} else if(args[k].type == arg_strings) {
521 		    add_string((getarg_strings*)args[k].value, optarg);
522 		    return 0;
523 		} else if(args[k].type == arg_double) {
524 		    double tmp;
525 		    if(sscanf(optarg, "%lf", &tmp) != 1)
526 			return ARG_ERR_BAD_ARG;
527 		    *(double*)args[k].value = tmp;
528 		    return 0;
529 		}
530 		return ARG_ERR_BAD_ARG;
531 	    }
532 	}
533 	if (k == (int)num_args)
534 	    return ARG_ERR_NO_MATCH;
535     }
536     return 0;
537 }
538 
539 int
getarg(struct getargs * args,size_t num_args,int argc,const char ** argv,int * optind)540 getarg(struct getargs *args, size_t num_args,
541        int argc, const char **argv, int *optind)
542 {
543     int i;
544     int ret = 0;
545 
546     srand ((unsigned int)time(NULL));
547     (*optind)++;
548     for(i = *optind; i < argc; i++) {
549 	if(argv[i][0] != '-')
550 	    break;
551 	if(argv[i][1] == '-'){
552 	    if(argv[i][2] == 0){
553 		i++;
554 		break;
555 	    }
556 	    ret = arg_match_long (args, num_args, (char *) argv[i] + 2,
557 				  argc, argv, &i);
558 	} else {
559 	    ret = arg_match_short (args, num_args, (char *) argv[i],
560 				   argc, argv, &i);
561 	}
562 	if(ret)
563 	    break;
564     }
565     *optind = i;
566     return ret;
567 }
568 
569 
570 #ifdef TEST
571 int foo_flag = 2;
572 int flag1 = 0;
573 int flag2 = 0;
574 int bar_int;
575 char *baz_string;
576 
577 struct getargs args[] = {
578     { NULL, '1', arg_flag, &flag1, "one", NULL },
579     { NULL, '2', arg_flag, &flag2, "two", NULL },
580     { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
581     { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
582     { "baz", 'x', arg_string, &baz_string, "baz", "name" },
583 };
584 
main(int argc,char ** argv)585 int main(int argc, char **argv)
586 {
587     int optind = 0;
588     while(getarg(args, 5, argc, argv, &optind))
589 	printf("Bad arg: %s\n", argv[optind]);
590     printf("flag1 = %d\n", flag1);
591     printf("flag2 = %d\n", flag2);
592     printf("foo_flag = %d\n", foo_flag);
593     printf("bar_int = %d\n", bar_int);
594     printf("baz_flag = %s\n", baz_string);
595     arg_printusage (args, 5, argv[0], "nothing here");
596 }
597 #endif
598