1 /* 2 * Copyright (c) 1997 - 2000 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.37 2000/12/25 17:03:15 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 = __progname; 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 for (i = 0; i < num_args; ++i) { 227 size_t len = 0; 228 229 if (args[i].long_name) { 230 buf[0] = '\0'; 231 strlcat(buf, "[--", sizeof(buf)); 232 len += 2; 233 if(args[i].type == arg_negative_flag) { 234 strlcat(buf, "no-", sizeof(buf)); 235 len += 3; 236 } 237 strlcat(buf, args[i].long_name, sizeof(buf)); 238 len += strlen(args[i].long_name); 239 len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf), 240 0, 1, &args[i]); 241 strlcat(buf, "]", sizeof(buf)); 242 if(args[i].type == arg_strings) 243 strlcat(buf, "...", sizeof(buf)); 244 col = check_column(stderr, col, strlen(buf) + 1, columns); 245 col += fprintf(stderr, " %s", buf); 246 } 247 if (args[i].short_name) { 248 snprintf(buf, sizeof(buf), "[-%c", args[i].short_name); 249 len += 2; 250 len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf), 251 0, 0, &args[i]); 252 strlcat(buf, "]", sizeof(buf)); 253 if(args[i].type == arg_strings) 254 strlcat(buf, "...", sizeof(buf)); 255 col = check_column(stderr, col, strlen(buf) + 1, columns); 256 col += fprintf(stderr, " %s", buf); 257 } 258 if (args[i].long_name && args[i].short_name) 259 len += 2; /* ", " */ 260 max_len = max(max_len, len); 261 } 262 if (extra_string) { 263 col = check_column(stderr, col, strlen(extra_string) + 1, columns); 264 fprintf (stderr, " %s\n", extra_string); 265 } else 266 fprintf (stderr, "\n"); 267 for (i = 0; i < num_args; ++i) { 268 if (args[i].help) { 269 size_t count = 0; 270 271 if (args[i].short_name) { 272 count += fprintf (stderr, "-%c", args[i].short_name); 273 print_arg (buf, sizeof(buf), 0, 0, &args[i]); 274 count += fprintf(stderr, "%s", buf); 275 } 276 if (args[i].short_name && args[i].long_name) 277 count += fprintf (stderr, ", "); 278 if (args[i].long_name) { 279 count += fprintf (stderr, "--"); 280 if (args[i].type == arg_negative_flag) 281 count += fprintf (stderr, "no-"); 282 count += fprintf (stderr, "%s", args[i].long_name); 283 print_arg (buf, sizeof(buf), 0, 1, &args[i]); 284 count += fprintf(stderr, "%s", buf); 285 } 286 while(count++ <= max_len) 287 putc (' ', stderr); 288 fprintf (stderr, "%s\n", args[i].help); 289 } 290 } 291 } 292 293 static void 294 add_string(getarg_strings *s, char *value) 295 { 296 s->strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings)); 297 s->strings[s->num_strings] = value; 298 s->num_strings++; 299 } 300 301 static int 302 arg_match_long(struct getargs *args, size_t num_args, 303 char *argv, int argc, char **rargv, int *optind) 304 { 305 int i; 306 char *optarg = NULL; 307 int negate = 0; 308 int partial_match = 0; 309 struct getargs *partial = NULL; 310 struct getargs *current = NULL; 311 int argv_len; 312 char *p; 313 314 argv_len = strlen(argv); 315 p = strchr (argv, '='); 316 if (p != NULL) 317 argv_len = p - argv; 318 319 for (i = 0; i < num_args; ++i) { 320 if(args[i].long_name) { 321 int len = strlen(args[i].long_name); 322 char *p = argv; 323 int p_len = argv_len; 324 negate = 0; 325 326 for (;;) { 327 if (strncmp (args[i].long_name, p, p_len) == 0) { 328 if(p_len == len) 329 current = &args[i]; 330 else { 331 ++partial_match; 332 partial = &args[i]; 333 } 334 optarg = p + p_len; 335 } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) { 336 negate = !negate; 337 p += 3; 338 p_len -= 3; 339 continue; 340 } 341 break; 342 } 343 if (current) 344 break; 345 } 346 } 347 if (current == NULL) { 348 if (partial_match == 1) 349 current = partial; 350 else 351 return ARG_ERR_NO_MATCH; 352 } 353 354 if(*optarg == '\0' 355 && !ISFLAG(*current) 356 && current->type != arg_collect 357 && current->type != arg_counter) 358 return ARG_ERR_NO_MATCH; 359 switch(current->type){ 360 case arg_integer: 361 { 362 int tmp; 363 if(sscanf(optarg + 1, "%d", &tmp) != 1) 364 return ARG_ERR_BAD_ARG; 365 *(int*)current->value = tmp; 366 return 0; 367 } 368 case arg_string: 369 { 370 *(char**)current->value = optarg + 1; 371 return 0; 372 } 373 case arg_strings: 374 { 375 add_string((getarg_strings*)current->value, optarg + 1); 376 return 0; 377 } 378 case arg_flag: 379 case arg_negative_flag: 380 { 381 int *flag = current->value; 382 if(*optarg == '\0' || 383 strcmp(optarg + 1, "yes") == 0 || 384 strcmp(optarg + 1, "true") == 0){ 385 *flag = !negate; 386 return 0; 387 } else if (*optarg && strcmp(optarg + 1, "maybe") == 0) { 388 *flag = rand() & 1; 389 } else { 390 *flag = negate; 391 return 0; 392 } 393 return ARG_ERR_BAD_ARG; 394 } 395 case arg_counter : 396 { 397 int val; 398 399 if (*optarg == '\0') 400 val = 1; 401 else { 402 char *endstr; 403 404 val = strtol (optarg, &endstr, 0); 405 if (endstr == optarg) 406 return ARG_ERR_BAD_ARG; 407 } 408 *(int *)current->value += val; 409 return 0; 410 } 411 case arg_double: 412 { 413 double tmp; 414 if(sscanf(optarg + 1, "%lf", &tmp) != 1) 415 return ARG_ERR_BAD_ARG; 416 *(double*)current->value = tmp; 417 return 0; 418 } 419 case arg_collect:{ 420 struct getarg_collect_info *c = current->value; 421 int o = argv - rargv[*optind]; 422 return (*c->func)(FALSE, argc, rargv, optind, &o, c->data); 423 } 424 425 default: 426 abort (); 427 } 428 } 429 430 static int 431 arg_match_short (struct getargs *args, size_t num_args, 432 char *argv, int argc, char **rargv, int *optind) 433 { 434 int j, k; 435 436 for(j = 1; j > 0 && j < strlen(rargv[*optind]); j++) { 437 for(k = 0; k < num_args; k++) { 438 char *optarg; 439 440 if(args[k].short_name == 0) 441 continue; 442 if(argv[j] == args[k].short_name) { 443 if(args[k].type == arg_flag) { 444 *(int*)args[k].value = 1; 445 break; 446 } 447 if(args[k].type == arg_negative_flag) { 448 *(int*)args[k].value = 0; 449 break; 450 } 451 if(args[k].type == arg_counter) { 452 ++*(int *)args[k].value; 453 break; 454 } 455 if(args[k].type == arg_collect) { 456 struct getarg_collect_info *c = args[k].value; 457 458 if((*c->func)(TRUE, argc, rargv, optind, &j, c->data)) 459 return ARG_ERR_BAD_ARG; 460 break; 461 } 462 463 if(argv[j + 1]) 464 optarg = &argv[j + 1]; 465 else { 466 ++*optind; 467 optarg = rargv[*optind]; 468 } 469 if(optarg == NULL) { 470 --*optind; 471 return ARG_ERR_NO_ARG; 472 } 473 if(args[k].type == arg_integer) { 474 int tmp; 475 if(sscanf(optarg, "%d", &tmp) != 1) 476 return ARG_ERR_BAD_ARG; 477 *(int*)args[k].value = tmp; 478 return 0; 479 } else if(args[k].type == arg_string) { 480 *(char**)args[k].value = optarg; 481 return 0; 482 } else if(args[k].type == arg_strings) { 483 add_string((getarg_strings*)args[k].value, optarg); 484 return 0; 485 } else if(args[k].type == arg_double) { 486 double tmp; 487 if(sscanf(optarg, "%lf", &tmp) != 1) 488 return ARG_ERR_BAD_ARG; 489 *(double*)args[k].value = tmp; 490 return 0; 491 } 492 return ARG_ERR_BAD_ARG; 493 } 494 } 495 if (k == num_args) 496 return ARG_ERR_NO_MATCH; 497 } 498 return 0; 499 } 500 501 int 502 getarg(struct getargs *args, size_t num_args, 503 int argc, char **argv, int *optind) 504 { 505 int i; 506 int ret = 0; 507 508 srand (time(NULL)); 509 (*optind)++; 510 for(i = *optind; i < argc; i++) { 511 if(argv[i][0] != '-') 512 break; 513 if(argv[i][1] == '-'){ 514 if(argv[i][2] == 0){ 515 i++; 516 break; 517 } 518 ret = arg_match_long (args, num_args, argv[i] + 2, 519 argc, argv, &i); 520 } else { 521 ret = arg_match_short (args, num_args, argv[i], 522 argc, argv, &i); 523 } 524 if(ret) 525 break; 526 } 527 *optind = i; 528 return ret; 529 } 530 531 #if TEST 532 int foo_flag = 2; 533 int flag1 = 0; 534 int flag2 = 0; 535 int bar_int; 536 char *baz_string; 537 538 struct getargs args[] = { 539 { NULL, '1', arg_flag, &flag1, "one", NULL }, 540 { NULL, '2', arg_flag, &flag2, "two", NULL }, 541 { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL }, 542 { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"}, 543 { "baz", 'x', arg_string, &baz_string, "baz", "name" }, 544 }; 545 546 int main(int argc, char **argv) 547 { 548 int optind = 0; 549 while(getarg(args, 5, argc, argv, &optind)) 550 printf("Bad arg: %s\n", argv[optind]); 551 printf("flag1 = %d\n", flag1); 552 printf("flag2 = %d\n", flag2); 553 printf("foo_flag = %d\n", foo_flag); 554 printf("bar_int = %d\n", bar_int); 555 printf("baz_flag = %s\n", baz_string); 556 arg_printusage (args, 5, argv[0], "nothing here"); 557 } 558 #endif 559