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