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.34 2000/02/13 21:06:43 assar 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%s", 129 args[i].type == arg_negative_flag ? "no-" : "", 130 args[i].long_name, buf); 131 } 132 printf("\n"); 133 } else { 134 print_arg(buf, sizeof(buf), 1, 0, args + i); 135 printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf); 136 print_arg(buf, sizeof(buf), 1, 1, args + i); 137 printf(".Fl -%s%s Oc\n.Xc\n", args[i].long_name, buf); 138 } 139 /* 140 if(args[i].type == arg_strings) 141 fprintf (stderr, "..."); 142 */ 143 } 144 if (extra_string && *extra_string) 145 printf (".Ar %s\n", extra_string); 146 printf(".Sh DESCRIPTION\n"); 147 printf("Supported options:\n"); 148 printf(".Bl -tag -width Ds\n"); 149 for(i = 0; i < num_args; i++){ 150 printf(".It Xo\n"); 151 if(args[i].short_name){ 152 printf(".Fl %c", args[i].short_name); 153 print_arg(buf, sizeof(buf), 1, 0, args + i); 154 printf("%s", buf); 155 if(args[i].long_name) 156 printf(" Ns ,"); 157 printf("\n"); 158 } 159 if(args[i].long_name){ 160 printf(".Fl -%s%s", 161 args[i].type == arg_negative_flag ? "no-" : "", 162 args[i].long_name); 163 print_arg(buf, sizeof(buf), 1, 1, args + i); 164 printf("%s\n", buf); 165 } 166 printf(".Xc\n"); 167 if(args[i].help) 168 printf("%s\n", args[i].help); 169 /* 170 if(args[i].type == arg_strings) 171 fprintf (stderr, "..."); 172 */ 173 } 174 printf(".El\n"); 175 printf(".\\\".Sh ENVIRONMENT\n"); 176 printf(".\\\".Sh FILES\n"); 177 printf(".\\\".Sh EXAMPLES\n"); 178 printf(".\\\".Sh DIAGNOSTICS\n"); 179 printf(".\\\".Sh SEE ALSO\n"); 180 printf(".\\\".Sh STANDARDS\n"); 181 printf(".\\\".Sh HISTORY\n"); 182 printf(".\\\".Sh AUTHORS\n"); 183 printf(".\\\".Sh BUGS\n"); 184 } 185 186 static int 187 check_column(FILE *f, int col, int len, int columns) 188 { 189 if(col + len > columns) { 190 fprintf(f, "\n"); 191 col = fprintf(f, " "); 192 } 193 return col; 194 } 195 196 void 197 arg_printusage (struct getargs *args, 198 size_t num_args, 199 const char *progname, 200 const char *extra_string) 201 { 202 int i; 203 size_t max_len = 0; 204 char buf[128]; 205 int col = 0, columns; 206 struct winsize ws; 207 208 if (progname == NULL) 209 progname = __progname; 210 211 if(getenv("GETARGMANDOC")){ 212 mandoc_template(args, num_args, progname, extra_string); 213 return; 214 } 215 if(get_window_size(2, &ws) == 0) 216 columns = ws.ws_col; 217 else 218 columns = 80; 219 col = 0; 220 col += fprintf (stderr, "Usage: %s", progname); 221 for (i = 0; i < num_args; ++i) { 222 size_t len = 0; 223 224 if (args[i].long_name) { 225 buf[0] = '\0'; 226 strlcat(buf, "[--", sizeof(buf)); 227 len += 2; 228 if(args[i].type == arg_negative_flag) { 229 strlcat(buf, "no-", sizeof(buf)); 230 len += 3; 231 } 232 strlcat(buf, args[i].long_name, sizeof(buf)); 233 len += strlen(args[i].long_name); 234 len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf), 235 0, 1, &args[i]); 236 strlcat(buf, "]", sizeof(buf)); 237 if(args[i].type == arg_strings) 238 strlcat(buf, "...", sizeof(buf)); 239 col = check_column(stderr, col, strlen(buf) + 1, columns); 240 col += fprintf(stderr, " %s", buf); 241 } 242 if (args[i].short_name) { 243 snprintf(buf, sizeof(buf), "[-%c", args[i].short_name); 244 len += 2; 245 len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf), 246 0, 0, &args[i]); 247 strlcat(buf, "]", sizeof(buf)); 248 if(args[i].type == arg_strings) 249 strlcat(buf, "...", sizeof(buf)); 250 col = check_column(stderr, col, strlen(buf) + 1, columns); 251 col += fprintf(stderr, " %s", buf); 252 } 253 if (args[i].long_name && args[i].short_name) 254 len += 2; /* ", " */ 255 max_len = max(max_len, len); 256 } 257 if (extra_string) { 258 col = check_column(stderr, col, strlen(extra_string) + 1, columns); 259 fprintf (stderr, " %s\n", extra_string); 260 } else 261 fprintf (stderr, "\n"); 262 for (i = 0; i < num_args; ++i) { 263 if (args[i].help) { 264 size_t count = 0; 265 266 if (args[i].short_name) { 267 count += fprintf (stderr, "-%c", args[i].short_name); 268 print_arg (buf, sizeof(buf), 0, 0, &args[i]); 269 count += fprintf(stderr, "%s", buf); 270 } 271 if (args[i].short_name && args[i].long_name) 272 count += fprintf (stderr, ", "); 273 if (args[i].long_name) { 274 count += fprintf (stderr, "--"); 275 if (args[i].type == arg_negative_flag) 276 count += fprintf (stderr, "no-"); 277 count += fprintf (stderr, "%s", args[i].long_name); 278 print_arg (buf, sizeof(buf), 0, 1, &args[i]); 279 count += fprintf(stderr, "%s", buf); 280 } 281 while(count++ <= max_len) 282 putc (' ', stderr); 283 fprintf (stderr, "%s\n", args[i].help); 284 } 285 } 286 } 287 288 static void 289 add_string(getarg_strings *s, char *value) 290 { 291 s->strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings)); 292 s->strings[s->num_strings] = value; 293 s->num_strings++; 294 } 295 296 static int 297 arg_match_long(struct getargs *args, size_t num_args, 298 char *argv, int argc, char **rargv, int *optind) 299 { 300 int i; 301 char *optarg = NULL; 302 int negate = 0; 303 int partial_match = 0; 304 struct getargs *partial = NULL; 305 struct getargs *current = NULL; 306 int argv_len; 307 char *p; 308 309 argv_len = strlen(argv); 310 p = strchr (argv, '='); 311 if (p != NULL) 312 argv_len = p - argv; 313 314 for (i = 0; i < num_args; ++i) { 315 if(args[i].long_name) { 316 int len = strlen(args[i].long_name); 317 char *p = argv; 318 int p_len = argv_len; 319 negate = 0; 320 321 for (;;) { 322 if (strncmp (args[i].long_name, p, p_len) == 0) { 323 if(p_len == len) 324 current = &args[i]; 325 else { 326 ++partial_match; 327 partial = &args[i]; 328 } 329 optarg = p + p_len; 330 } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) { 331 negate = !negate; 332 p += 3; 333 p_len -= 3; 334 continue; 335 } 336 break; 337 } 338 if (current) 339 break; 340 } 341 } 342 if (current == NULL) { 343 if (partial_match == 1) 344 current = partial; 345 else 346 return ARG_ERR_NO_MATCH; 347 } 348 349 if(*optarg == '\0' 350 && !ISFLAG(*current) 351 && current->type != arg_collect 352 && current->type != arg_counter) 353 return ARG_ERR_NO_MATCH; 354 switch(current->type){ 355 case arg_integer: 356 { 357 int tmp; 358 if(sscanf(optarg + 1, "%d", &tmp) != 1) 359 return ARG_ERR_BAD_ARG; 360 *(int*)current->value = tmp; 361 return 0; 362 } 363 case arg_string: 364 { 365 *(char**)current->value = optarg + 1; 366 return 0; 367 } 368 case arg_strings: 369 { 370 add_string((getarg_strings*)current->value, optarg + 1); 371 return 0; 372 } 373 case arg_flag: 374 case arg_negative_flag: 375 { 376 int *flag = current->value; 377 if(*optarg == '\0' || 378 strcmp(optarg + 1, "yes") == 0 || 379 strcmp(optarg + 1, "true") == 0){ 380 *flag = !negate; 381 return 0; 382 } else if (*optarg && strcmp(optarg + 1, "maybe") == 0) { 383 *flag = rand() & 1; 384 } else { 385 *flag = negate; 386 return 0; 387 } 388 return ARG_ERR_BAD_ARG; 389 } 390 case arg_counter : 391 { 392 int val; 393 394 if (*optarg == '\0') 395 val = 1; 396 else { 397 char *endstr; 398 399 val = strtol (optarg, &endstr, 0); 400 if (endstr == optarg) 401 return ARG_ERR_BAD_ARG; 402 } 403 *(int *)current->value += val; 404 return 0; 405 } 406 case arg_double: 407 { 408 double tmp; 409 if(sscanf(optarg + 1, "%lf", &tmp) != 1) 410 return ARG_ERR_BAD_ARG; 411 *(double*)current->value = tmp; 412 return 0; 413 } 414 case arg_collect:{ 415 struct getarg_collect_info *c = current->value; 416 int o = argv - rargv[*optind]; 417 return (*c->func)(FALSE, argc, rargv, optind, &o, c->data); 418 } 419 420 default: 421 abort (); 422 } 423 } 424 425 static int 426 arg_match_short (struct getargs *args, size_t num_args, 427 char *argv, int argc, char **rargv, int *optind) 428 { 429 int j, k; 430 431 for(j = 1; j > 0 && j < strlen(rargv[*optind]); j++) { 432 for(k = 0; k < num_args; k++) { 433 char *optarg; 434 435 if(args[k].short_name == 0) 436 continue; 437 if(argv[j] == args[k].short_name) { 438 if(args[k].type == arg_flag) { 439 *(int*)args[k].value = 1; 440 break; 441 } 442 if(args[k].type == arg_negative_flag) { 443 *(int*)args[k].value = 0; 444 break; 445 } 446 if(args[k].type == arg_counter) { 447 ++*(int *)args[k].value; 448 break; 449 } 450 if(args[k].type == arg_collect) { 451 struct getarg_collect_info *c = args[k].value; 452 453 if((*c->func)(TRUE, argc, rargv, optind, &j, c->data)) 454 return ARG_ERR_BAD_ARG; 455 break; 456 } 457 458 if(argv[j + 1]) 459 optarg = &argv[j + 1]; 460 else { 461 ++*optind; 462 optarg = rargv[*optind]; 463 } 464 if(optarg == NULL) 465 return ARG_ERR_NO_ARG; 466 if(args[k].type == arg_integer) { 467 int tmp; 468 if(sscanf(optarg, "%d", &tmp) != 1) 469 return ARG_ERR_BAD_ARG; 470 *(int*)args[k].value = tmp; 471 return 0; 472 } else if(args[k].type == arg_string) { 473 *(char**)args[k].value = optarg; 474 return 0; 475 } else if(args[k].type == arg_strings) { 476 add_string((getarg_strings*)args[k].value, optarg); 477 return 0; 478 } else if(args[k].type == arg_double) { 479 double tmp; 480 if(sscanf(optarg, "%lf", &tmp) != 1) 481 return ARG_ERR_BAD_ARG; 482 *(double*)args[k].value = tmp; 483 return 0; 484 } 485 return ARG_ERR_BAD_ARG; 486 } 487 } 488 if (k == num_args) 489 return ARG_ERR_NO_MATCH; 490 } 491 return 0; 492 } 493 494 int 495 getarg(struct getargs *args, size_t num_args, 496 int argc, char **argv, int *optind) 497 { 498 int i; 499 int ret = 0; 500 501 srand (time(NULL)); 502 (*optind)++; 503 for(i = *optind; i < argc; i++) { 504 if(argv[i][0] != '-') 505 break; 506 if(argv[i][1] == '-'){ 507 if(argv[i][2] == 0){ 508 i++; 509 break; 510 } 511 ret = arg_match_long (args, num_args, argv[i] + 2, 512 argc, argv, &i); 513 } else { 514 ret = arg_match_short (args, num_args, argv[i], 515 argc, argv, &i); 516 } 517 if(ret) 518 break; 519 } 520 *optind = i; 521 return ret; 522 } 523 524 #if TEST 525 int foo_flag = 2; 526 int flag1 = 0; 527 int flag2 = 0; 528 int bar_int; 529 char *baz_string; 530 531 struct getargs args[] = { 532 { NULL, '1', arg_flag, &flag1, "one", NULL }, 533 { NULL, '2', arg_flag, &flag2, "two", NULL }, 534 { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL }, 535 { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"}, 536 { "baz", 'x', arg_string, &baz_string, "baz", "name" }, 537 }; 538 539 int main(int argc, char **argv) 540 { 541 int optind = 0; 542 while(getarg(args, 5, argc, argv, &optind)) 543 printf("Bad arg: %s\n", argv[optind]); 544 printf("flag1 = %d\n", flag1); 545 printf("flag2 = %d\n", flag2); 546 printf("foo_flag = %d\n", foo_flag); 547 printf("bar_int = %d\n", bar_int); 548 printf("baz_flag = %s\n", baz_string); 549 arg_printusage (args, 5, argv[0], "nothing here"); 550 } 551 #endif 552