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