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