1 /* 2 * Copyright (c) 1997 - 2002 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 21005 2007-06-08 01:54:35Z lha $"); 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(" ,"); 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 ROKEN_LIB_FUNCTION 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 int 311 add_string(getarg_strings *s, char *value) 312 { 313 char **strings; 314 315 strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings)); 316 if (strings == NULL) { 317 free(s->strings); 318 s->strings = NULL; 319 s->num_strings = 0; 320 return ENOMEM; 321 } 322 s->strings = strings; 323 s->strings[s->num_strings] = value; 324 s->num_strings++; 325 return 0; 326 } 327 328 static int 329 arg_match_long(struct getargs *args, size_t num_args, 330 char *argv, int argc, char **rargv, int *goptind) 331 { 332 int i; 333 char *goptarg = NULL; 334 int negate = 0; 335 int partial_match = 0; 336 struct getargs *partial = NULL; 337 struct getargs *current = NULL; 338 int argv_len; 339 char *p; 340 int p_len; 341 342 argv_len = strlen(argv); 343 p = strchr (argv, '='); 344 if (p != NULL) 345 argv_len = p - argv; 346 347 for (i = 0; i < num_args; ++i) { 348 if(args[i].long_name) { 349 int len = strlen(args[i].long_name); 350 p = argv; 351 p_len = argv_len; 352 negate = 0; 353 354 for (;;) { 355 if (strncmp (args[i].long_name, p, p_len) == 0) { 356 if(p_len == len) 357 current = &args[i]; 358 else { 359 ++partial_match; 360 partial = &args[i]; 361 } 362 goptarg = p + p_len; 363 } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) { 364 negate = !negate; 365 p += 3; 366 p_len -= 3; 367 continue; 368 } 369 break; 370 } 371 if (current) 372 break; 373 } 374 } 375 if (current == NULL) { 376 if (partial_match == 1) 377 current = partial; 378 else 379 return ARG_ERR_NO_MATCH; 380 } 381 382 if(*goptarg == '\0' 383 && !ISFLAG(*current) 384 && current->type != arg_collect 385 && current->type != arg_counter) 386 return ARG_ERR_NO_MATCH; 387 switch(current->type){ 388 case arg_integer: 389 { 390 int tmp; 391 if(sscanf(goptarg + 1, "%d", &tmp) != 1) 392 return ARG_ERR_BAD_ARG; 393 *(int*)current->value = tmp; 394 return 0; 395 } 396 case arg_string: 397 { 398 *(char**)current->value = goptarg + 1; 399 return 0; 400 } 401 case arg_strings: 402 { 403 return add_string((getarg_strings*)current->value, goptarg + 1); 404 } 405 case arg_flag: 406 case arg_negative_flag: 407 { 408 int *flag = current->value; 409 if(*goptarg == '\0' || 410 strcmp(goptarg + 1, "yes") == 0 || 411 strcmp(goptarg + 1, "true") == 0){ 412 *flag = !negate; 413 return 0; 414 } else if (*goptarg && strcmp(goptarg + 1, "maybe") == 0) { 415 #ifdef HAVE_RANDOM 416 *flag = random() & 1; 417 #else 418 *flag = rand() & 1; 419 #endif 420 } else { 421 *flag = negate; 422 return 0; 423 } 424 return ARG_ERR_BAD_ARG; 425 } 426 case arg_counter : 427 { 428 int val; 429 430 if (*goptarg == '\0') 431 val = 1; 432 else if(sscanf(goptarg + 1, "%d", &val) != 1) 433 return ARG_ERR_BAD_ARG; 434 *(int *)current->value += val; 435 return 0; 436 } 437 case arg_double: 438 { 439 double tmp; 440 if(sscanf(goptarg + 1, "%lf", &tmp) != 1) 441 return ARG_ERR_BAD_ARG; 442 *(double*)current->value = tmp; 443 return 0; 444 } 445 case arg_collect:{ 446 struct getarg_collect_info *c = current->value; 447 int o = argv - rargv[*goptind]; 448 return (*c->func)(FALSE, argc, rargv, goptind, &o, c->data); 449 } 450 451 default: 452 abort (); 453 } 454 } 455 456 static int 457 arg_match_short (struct getargs *args, size_t num_args, 458 char *argv, int argc, char **rargv, int *goptind) 459 { 460 int j, k; 461 462 for(j = 1; j > 0 && j < strlen(rargv[*goptind]); j++) { 463 for(k = 0; k < num_args; k++) { 464 char *goptarg; 465 466 if(args[k].short_name == 0) 467 continue; 468 if(argv[j] == args[k].short_name) { 469 if(args[k].type == arg_flag) { 470 *(int*)args[k].value = 1; 471 break; 472 } 473 if(args[k].type == arg_negative_flag) { 474 *(int*)args[k].value = 0; 475 break; 476 } 477 if(args[k].type == arg_counter) { 478 ++*(int *)args[k].value; 479 break; 480 } 481 if(args[k].type == arg_collect) { 482 struct getarg_collect_info *c = args[k].value; 483 484 if((*c->func)(TRUE, argc, rargv, goptind, &j, c->data)) 485 return ARG_ERR_BAD_ARG; 486 break; 487 } 488 489 if(argv[j + 1]) 490 goptarg = &argv[j + 1]; 491 else { 492 ++*goptind; 493 goptarg = rargv[*goptind]; 494 } 495 if(goptarg == NULL) { 496 --*goptind; 497 return ARG_ERR_NO_ARG; 498 } 499 if(args[k].type == arg_integer) { 500 int tmp; 501 if(sscanf(goptarg, "%d", &tmp) != 1) 502 return ARG_ERR_BAD_ARG; 503 *(int*)args[k].value = tmp; 504 return 0; 505 } else if(args[k].type == arg_string) { 506 *(char**)args[k].value = goptarg; 507 return 0; 508 } else if(args[k].type == arg_strings) { 509 return add_string((getarg_strings*)args[k].value, goptarg); 510 } else if(args[k].type == arg_double) { 511 double tmp; 512 if(sscanf(goptarg, "%lf", &tmp) != 1) 513 return ARG_ERR_BAD_ARG; 514 *(double*)args[k].value = tmp; 515 return 0; 516 } 517 return ARG_ERR_BAD_ARG; 518 } 519 } 520 if (k == num_args) 521 return ARG_ERR_NO_MATCH; 522 } 523 return 0; 524 } 525 526 int ROKEN_LIB_FUNCTION 527 getarg(struct getargs *args, size_t num_args, 528 int argc, char **argv, int *goptind) 529 { 530 int i; 531 int ret = 0; 532 533 #if defined(HAVE_SRANDOMDEV) 534 srandomdev(); 535 #elif defined(HAVE_RANDOM) 536 srandom(time(NULL)); 537 #else 538 srand (time(NULL)); 539 #endif 540 (*goptind)++; 541 for(i = *goptind; i < argc; i++) { 542 if(argv[i][0] != '-') 543 break; 544 if(argv[i][1] == '-'){ 545 if(argv[i][2] == 0){ 546 i++; 547 break; 548 } 549 ret = arg_match_long (args, num_args, argv[i] + 2, 550 argc, argv, &i); 551 } else { 552 ret = arg_match_short (args, num_args, argv[i], 553 argc, argv, &i); 554 } 555 if(ret) 556 break; 557 } 558 *goptind = i; 559 return ret; 560 } 561 562 void ROKEN_LIB_FUNCTION 563 free_getarg_strings (getarg_strings *s) 564 { 565 free (s->strings); 566 } 567 568 #if TEST 569 int foo_flag = 2; 570 int flag1 = 0; 571 int flag2 = 0; 572 int bar_int; 573 char *baz_string; 574 575 struct getargs args[] = { 576 { NULL, '1', arg_flag, &flag1, "one", NULL }, 577 { NULL, '2', arg_flag, &flag2, "two", NULL }, 578 { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL }, 579 { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"}, 580 { "baz", 'x', arg_string, &baz_string, "baz", "name" }, 581 }; 582 583 int main(int argc, char **argv) 584 { 585 int goptind = 0; 586 while(getarg(args, 5, argc, argv, &goptind)) 587 printf("Bad arg: %s\n", argv[goptind]); 588 printf("flag1 = %d\n", flag1); 589 printf("flag2 = %d\n", flag2); 590 printf("foo_flag = %d\n", foo_flag); 591 printf("bar_int = %d\n", bar_int); 592 printf("baz_flag = %s\n", baz_string); 593 arg_printusage (args, 5, argv[0], "nothing here"); 594 } 595 #endif 596