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