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.40 2001/04/25 12:06:10 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 Oc\n.Xc\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 *flag = rand() & 1; 406 } else { 407 *flag = negate; 408 return 0; 409 } 410 return ARG_ERR_BAD_ARG; 411 } 412 case arg_counter : 413 { 414 int val; 415 416 if (*optarg == '\0') 417 val = 1; 418 else { 419 char *endstr; 420 421 val = strtol (optarg, &endstr, 0); 422 if (endstr == optarg) 423 return ARG_ERR_BAD_ARG; 424 } 425 *(int *)current->value += val; 426 return 0; 427 } 428 case arg_double: 429 { 430 double tmp; 431 if(sscanf(optarg + 1, "%lf", &tmp) != 1) 432 return ARG_ERR_BAD_ARG; 433 *(double*)current->value = tmp; 434 return 0; 435 } 436 case arg_collect:{ 437 struct getarg_collect_info *c = current->value; 438 int o = argv - rargv[*optind]; 439 return (*c->func)(FALSE, argc, rargv, optind, &o, c->data); 440 } 441 442 default: 443 abort (); 444 } 445 } 446 447 static int 448 arg_match_short (struct getargs *args, size_t num_args, 449 char *argv, int argc, char **rargv, int *optind) 450 { 451 int j, k; 452 453 for(j = 1; j > 0 && j < strlen(rargv[*optind]); j++) { 454 for(k = 0; k < num_args; k++) { 455 char *optarg; 456 457 if(args[k].short_name == 0) 458 continue; 459 if(argv[j] == args[k].short_name) { 460 if(args[k].type == arg_flag) { 461 *(int*)args[k].value = 1; 462 break; 463 } 464 if(args[k].type == arg_negative_flag) { 465 *(int*)args[k].value = 0; 466 break; 467 } 468 if(args[k].type == arg_counter) { 469 ++*(int *)args[k].value; 470 break; 471 } 472 if(args[k].type == arg_collect) { 473 struct getarg_collect_info *c = args[k].value; 474 475 if((*c->func)(TRUE, argc, rargv, optind, &j, c->data)) 476 return ARG_ERR_BAD_ARG; 477 break; 478 } 479 480 if(argv[j + 1]) 481 optarg = &argv[j + 1]; 482 else { 483 ++*optind; 484 optarg = rargv[*optind]; 485 } 486 if(optarg == NULL) { 487 --*optind; 488 return ARG_ERR_NO_ARG; 489 } 490 if(args[k].type == arg_integer) { 491 int tmp; 492 if(sscanf(optarg, "%d", &tmp) != 1) 493 return ARG_ERR_BAD_ARG; 494 *(int*)args[k].value = tmp; 495 return 0; 496 } else if(args[k].type == arg_string) { 497 *(char**)args[k].value = optarg; 498 return 0; 499 } else if(args[k].type == arg_strings) { 500 add_string((getarg_strings*)args[k].value, optarg); 501 return 0; 502 } else if(args[k].type == arg_double) { 503 double tmp; 504 if(sscanf(optarg, "%lf", &tmp) != 1) 505 return ARG_ERR_BAD_ARG; 506 *(double*)args[k].value = tmp; 507 return 0; 508 } 509 return ARG_ERR_BAD_ARG; 510 } 511 } 512 if (k == num_args) 513 return ARG_ERR_NO_MATCH; 514 } 515 return 0; 516 } 517 518 int 519 getarg(struct getargs *args, size_t num_args, 520 int argc, char **argv, int *optind) 521 { 522 int i; 523 int ret = 0; 524 525 srand (time(NULL)); 526 (*optind)++; 527 for(i = *optind; i < argc; i++) { 528 if(argv[i][0] != '-') 529 break; 530 if(argv[i][1] == '-'){ 531 if(argv[i][2] == 0){ 532 i++; 533 break; 534 } 535 ret = arg_match_long (args, num_args, argv[i] + 2, 536 argc, argv, &i); 537 } else { 538 ret = arg_match_short (args, num_args, argv[i], 539 argc, argv, &i); 540 } 541 if(ret) 542 break; 543 } 544 *optind = i; 545 return ret; 546 } 547 548 void 549 free_getarg_strings (getarg_strings *s) 550 { 551 free (s->strings); 552 } 553 554 #if TEST 555 int foo_flag = 2; 556 int flag1 = 0; 557 int flag2 = 0; 558 int bar_int; 559 char *baz_string; 560 561 struct getargs args[] = { 562 { NULL, '1', arg_flag, &flag1, "one", NULL }, 563 { NULL, '2', arg_flag, &flag2, "two", NULL }, 564 { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL }, 565 { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"}, 566 { "baz", 'x', arg_string, &baz_string, "baz", "name" }, 567 }; 568 569 int main(int argc, char **argv) 570 { 571 int optind = 0; 572 while(getarg(args, 5, argc, argv, &optind)) 573 printf("Bad arg: %s\n", argv[optind]); 574 printf("flag1 = %d\n", flag1); 575 printf("flag2 = %d\n", flag2); 576 printf("foo_flag = %d\n", foo_flag); 577 printf("bar_int = %d\n", bar_int); 578 printf("baz_flag = %s\n", baz_string); 579 arg_printusage (args, 5, argv[0], "nothing here"); 580 } 581 #endif 582