1 /* $NetBSD: option.c,v 1.3 2011/07/03 20:14:13 tron Exp $ */ 2 3 /* 4 * Copyright (C) 1984-2011 Mark Nudelman 5 * 6 * You may distribute under the terms of either the GNU General Public 7 * License or the Less License, as specified in the README file. 8 * 9 * For more information about less, or for information on how to 10 * contact the author, see the README file. 11 */ 12 13 14 /* 15 * Process command line options. 16 * 17 * Each option is a single letter which controls a program variable. 18 * The options have defaults which may be changed via 19 * the command line option, toggled via the "-" command, 20 * or queried via the "_" command. 21 */ 22 23 #include "less.h" 24 #include "option.h" 25 26 static struct loption *pendopt; 27 public int plusoption = FALSE; 28 29 static char *optstring __P((char *, char **, char *, char *)); 30 static int flip_triple __P((int, int)); 31 static void nostring __P((char *)); 32 33 extern int screen_trashed; 34 extern int less_is_more; 35 extern int quit_at_eof; 36 extern char *every_first_cmd; 37 38 /* 39 * Return a printable description of an option. 40 */ 41 static char * 42 opt_desc(o) 43 struct loption *o; 44 { 45 static char buf[OPTNAME_MAX + 10]; 46 if (o->oletter == OLETTER_NONE) 47 SNPRINTF1(buf, sizeof(buf), "--%s", o->onames->oname); 48 else 49 SNPRINTF2(buf, sizeof(buf), "-%c (--%s)", o->oletter, o->onames->oname); 50 return (buf); 51 } 52 53 /* 54 * Return a string suitable for printing as the "name" of an option. 55 * For example, if the option letter is 'x', just return "-x". 56 */ 57 public char * 58 propt(c) 59 int c; 60 { 61 static char buf[8]; 62 63 sprintf(buf, "-%s", prchar(c)); 64 return (buf); 65 } 66 67 /* 68 * Scan an argument (either from the command line or from the 69 * LESS environment variable) and process it. 70 */ 71 public void 72 scan_option(s) 73 char *s; 74 { 75 register struct loption *o; 76 register int optc; 77 char *optname; 78 char *printopt; 79 char *str; 80 int set_default; 81 int lc; 82 int err; 83 PARG parg; 84 85 if (s == NULL) 86 return; 87 88 /* 89 * If we have a pending option which requires an argument, 90 * handle it now. 91 * This happens if the previous option was, for example, "-P" 92 * without a following string. In that case, the current 93 * option is simply the argument for the previous option. 94 */ 95 if (pendopt != NULL) 96 { 97 switch (pendopt->otype & OTYPE) 98 { 99 case STRING: 100 (*pendopt->ofunc)(INIT, s); 101 break; 102 case NUMBER: 103 printopt = opt_desc(pendopt); 104 *(pendopt->ovar) = getnum(&s, printopt, (int*)NULL); 105 break; 106 } 107 pendopt = NULL; 108 return; 109 } 110 111 set_default = FALSE; 112 optname = NULL; 113 114 while (*s != '\0') 115 { 116 /* 117 * Check some special cases first. 118 */ 119 switch (optc = *s++) 120 { 121 case ' ': 122 case '\t': 123 case END_OPTION_STRING: 124 continue; 125 case '-': 126 /* 127 * "--" indicates an option name instead of a letter. 128 */ 129 if (*s == '-') 130 { 131 optname = ++s; 132 break; 133 } 134 /* 135 * "-+" means set these options back to their defaults. 136 * (They may have been set otherwise by previous 137 * options.) 138 */ 139 set_default = (*s == '+'); 140 if (set_default) 141 s++; 142 continue; 143 case '+': 144 /* 145 * An option prefixed by a "+" is ungotten, so 146 * that it is interpreted as less commands 147 * processed at the start of the first input file. 148 * "++" means process the commands at the start of 149 * EVERY input file. 150 */ 151 plusoption = TRUE; 152 s = optstring(s, &str, propt('+'), NULL); 153 if (*str == '+') 154 every_first_cmd = save(++str); 155 else 156 ungetsc(str); 157 continue; 158 case '0': case '1': case '2': case '3': case '4': 159 case '5': case '6': case '7': case '8': case '9': 160 /* 161 * Special "more" compatibility form "-<number>" 162 * instead of -z<number> to set the scrolling 163 * window size. 164 */ 165 s--; 166 optc = 'z'; 167 break; 168 case 'n': 169 if (less_is_more) 170 optc = 'z'; 171 break; 172 } 173 174 /* 175 * Not a special case. 176 * Look up the option letter in the option table. 177 */ 178 err = 0; 179 if (optname == NULL) 180 { 181 printopt = propt(optc); 182 lc = ASCII_IS_LOWER(optc); 183 o = findopt(optc); 184 } else 185 { 186 printopt = optname; 187 lc = ASCII_IS_LOWER(optname[0]); 188 o = findopt_name(&optname, NULL, &err); 189 s = optname; 190 optname = NULL; 191 if (*s == '\0' || *s == ' ') 192 { 193 /* 194 * The option name matches exactly. 195 */ 196 ; 197 } else if (*s == '=') 198 { 199 /* 200 * The option name is followed by "=value". 201 */ 202 if (o != NULL && 203 (o->otype & OTYPE) != STRING && 204 (o->otype & OTYPE) != NUMBER) 205 { 206 parg.p_string = printopt; 207 error("The %s option should not be followed by =", 208 &parg); 209 quit(QUIT_ERROR); 210 } 211 s++; 212 } else 213 { 214 /* 215 * The specified name is longer than the 216 * real option name. 217 */ 218 o = NULL; 219 } 220 } 221 if (o == NULL) 222 { 223 parg.p_string = printopt; 224 if (err == OPT_AMBIG) 225 error("%s is an ambiguous abbreviation (\"less --help\" for help)", 226 &parg); 227 else 228 error("There is no %s option (\"less --help\" for help)", 229 &parg); 230 quit(QUIT_ERROR); 231 } 232 233 str = NULL; 234 switch (o->otype & OTYPE) 235 { 236 case BOOL: 237 if (set_default) 238 *(o->ovar) = o->odefault; 239 else 240 *(o->ovar) = ! o->odefault; 241 break; 242 case TRIPLE: 243 if (set_default) 244 *(o->ovar) = o->odefault; 245 else 246 *(o->ovar) = flip_triple(o->odefault, lc); 247 break; 248 case STRING: 249 if (*s == '\0') 250 { 251 /* 252 * Set pendopt and return. 253 * We will get the string next time 254 * scan_option is called. 255 */ 256 pendopt = o; 257 return; 258 } 259 /* 260 * Don't do anything here. 261 * All processing of STRING options is done by 262 * the handling function. 263 */ 264 while (*s == ' ') 265 s++; 266 s = optstring(s, &str, printopt, o->odesc[1]); 267 break; 268 case NUMBER: 269 if (*s == '\0') 270 { 271 pendopt = o; 272 return; 273 } 274 *(o->ovar) = getnum(&s, printopt, (int*)NULL); 275 break; 276 } 277 /* 278 * If the option has a handling function, call it. 279 */ 280 if (o->ofunc != NULL) 281 (*o->ofunc)(INIT, str); 282 } 283 } 284 285 /* 286 * Toggle command line flags from within the program. 287 * Used by the "-" and "_" commands. 288 * how_toggle may be: 289 * OPT_NO_TOGGLE just report the current setting, without changing it. 290 * OPT_TOGGLE invert the current setting 291 * OPT_UNSET set to the default value 292 * OPT_SET set to the inverse of the default value 293 */ 294 public void 295 toggle_option(o, lower, s, how_toggle) 296 struct loption *o; 297 int lower; 298 char *s; 299 int how_toggle; 300 { 301 register int num; 302 int no_prompt; 303 int err; 304 PARG parg; 305 306 no_prompt = (how_toggle & OPT_NO_PROMPT); 307 how_toggle &= ~OPT_NO_PROMPT; 308 309 if (o == NULL) 310 { 311 error("No such option", NULL_PARG); 312 return; 313 } 314 315 if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) 316 { 317 parg.p_string = opt_desc(o); 318 error("Cannot change the %s option", &parg); 319 return; 320 } 321 322 if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) 323 { 324 parg.p_string = opt_desc(o); 325 error("Cannot query the %s option", &parg); 326 return; 327 } 328 329 /* 330 * Check for something which appears to be a do_toggle 331 * (because the "-" command was used), but really is not. 332 * This could be a string option with no string, or 333 * a number option with no number. 334 */ 335 switch (o->otype & OTYPE) 336 { 337 case STRING: 338 case NUMBER: 339 if (how_toggle == OPT_TOGGLE && *s == '\0') 340 how_toggle = OPT_NO_TOGGLE; 341 break; 342 } 343 344 #if HILITE_SEARCH 345 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) 346 repaint_hilite(0); 347 #endif 348 349 /* 350 * Now actually toggle (change) the variable. 351 */ 352 if (how_toggle != OPT_NO_TOGGLE) 353 { 354 switch (o->otype & OTYPE) 355 { 356 case BOOL: 357 /* 358 * Boolean. 359 */ 360 switch (how_toggle) 361 { 362 case OPT_TOGGLE: 363 *(o->ovar) = ! *(o->ovar); 364 break; 365 case OPT_UNSET: 366 *(o->ovar) = o->odefault; 367 break; 368 case OPT_SET: 369 *(o->ovar) = ! o->odefault; 370 break; 371 } 372 break; 373 case TRIPLE: 374 /* 375 * Triple: 376 * If user gave the lower case letter, then switch 377 * to 1 unless already 1, in which case make it 0. 378 * If user gave the upper case letter, then switch 379 * to 2 unless already 2, in which case make it 0. 380 */ 381 switch (how_toggle) 382 { 383 case OPT_TOGGLE: 384 *(o->ovar) = flip_triple(*(o->ovar), lower); 385 break; 386 case OPT_UNSET: 387 *(o->ovar) = o->odefault; 388 break; 389 case OPT_SET: 390 *(o->ovar) = flip_triple(o->odefault, lower); 391 break; 392 } 393 break; 394 case STRING: 395 /* 396 * String: don't do anything here. 397 * The handling function will do everything. 398 */ 399 switch (how_toggle) 400 { 401 case OPT_SET: 402 case OPT_UNSET: 403 error("Cannot use \"-+\" or \"--\" for a string option", 404 NULL_PARG); 405 return; 406 } 407 break; 408 case NUMBER: 409 /* 410 * Number: set the variable to the given number. 411 */ 412 switch (how_toggle) 413 { 414 case OPT_TOGGLE: 415 num = getnum(&s, NULL, &err); 416 if (!err) 417 *(o->ovar) = num; 418 break; 419 case OPT_UNSET: 420 *(o->ovar) = o->odefault; 421 break; 422 case OPT_SET: 423 error("Can't use \"-!\" for a numeric option", 424 NULL_PARG); 425 return; 426 } 427 break; 428 } 429 } 430 431 /* 432 * Call the handling function for any special action 433 * specific to this option. 434 */ 435 if (o->ofunc != NULL) 436 (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); 437 438 #if HILITE_SEARCH 439 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) 440 chg_hilite(); 441 #endif 442 443 if (!no_prompt) 444 { 445 /* 446 * Print a message describing the new setting. 447 */ 448 switch (o->otype & OTYPE) 449 { 450 case BOOL: 451 case TRIPLE: 452 if (*(o->ovar) < 0) 453 error("Negative option is invalid", NULL_PARG); 454 /* 455 * Print the odesc message. 456 */ 457 error(o->odesc[*(o->ovar)], NULL_PARG); 458 break; 459 case NUMBER: 460 /* 461 * The message is in odesc[1] and has a %d for 462 * the value of the variable. 463 */ 464 parg.p_int = *(o->ovar); 465 error(o->odesc[1], &parg); 466 break; 467 case STRING: 468 /* 469 * Message was already printed by the handling function. 470 */ 471 break; 472 } 473 } 474 475 if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) 476 screen_trashed = TRUE; 477 } 478 479 /* 480 * "Toggle" a triple-valued option. 481 */ 482 static int 483 flip_triple(val, lc) 484 int val; 485 int lc; 486 { 487 if (lc) 488 return ((val == OPT_ON) ? OPT_OFF : OPT_ON); 489 else 490 return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS); 491 } 492 493 /* 494 * Determine if an option takes a parameter. 495 */ 496 public int 497 opt_has_param(o) 498 struct loption *o; 499 { 500 if (o == NULL) 501 return (0); 502 if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) 503 return (0); 504 return (1); 505 } 506 507 /* 508 * Return the prompt to be used for a given option letter. 509 * Only string and number valued options have prompts. 510 */ 511 public char * 512 opt_prompt(o) 513 struct loption *o; 514 { 515 if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) 516 return ("?"); 517 return (o->odesc[0]); 518 } 519 520 /* 521 * Return whether or not there is a string option pending; 522 * that is, if the previous option was a string-valued option letter 523 * (like -P) without a following string. 524 * In that case, the current option is taken to be the string for 525 * the previous option. 526 */ 527 public int 528 isoptpending() 529 { 530 return (pendopt != NULL); 531 } 532 533 /* 534 * Print error message about missing string. 535 */ 536 static void 537 nostring(printopt) 538 char *printopt; 539 { 540 PARG parg; 541 parg.p_string = printopt; 542 error("Value is required after %s", &parg); 543 } 544 545 /* 546 * Print error message if a STRING type option is not followed by a string. 547 */ 548 public void 549 nopendopt() 550 { 551 nostring(opt_desc(pendopt)); 552 } 553 554 /* 555 * Scan to end of string or to an END_OPTION_STRING character. 556 * In the latter case, replace the char with a null char. 557 * Return a pointer to the remainder of the string, if any. 558 */ 559 static char * 560 optstring(s, p_str, printopt, validchars) 561 char *s; 562 char **p_str; 563 char *printopt; 564 char *validchars; 565 { 566 register char *p; 567 568 if (*s == '\0') 569 { 570 nostring(printopt); 571 quit(QUIT_ERROR); 572 } 573 *p_str = s; 574 for (p = s; *p != '\0'; p++) 575 { 576 if (*p == END_OPTION_STRING || 577 (validchars != NULL && strchr(validchars, *p) == NULL)) 578 { 579 switch (*p) 580 { 581 case END_OPTION_STRING: 582 case ' ': case '\t': case '-': 583 /* Replace the char with a null to terminate string. */ 584 *p++ = '\0'; 585 break; 586 default: 587 /* Cannot replace char; make a copy of the string. */ 588 *p_str = (char *) ecalloc(p-s+1, sizeof(char)); 589 strncpy(*p_str, s, p-s); 590 (*p_str)[p-s] = '\0'; 591 break; 592 } 593 break; 594 } 595 } 596 return (p); 597 } 598 599 /* 600 */ 601 static int 602 num_error(printopt, errp) 603 char *printopt; 604 int *errp; 605 { 606 PARG parg; 607 608 if (errp != NULL) 609 { 610 *errp = TRUE; 611 return (-1); 612 } 613 if (printopt != NULL) 614 { 615 parg.p_string = printopt; 616 error("Number is required after %s", &parg); 617 } 618 quit(QUIT_ERROR); 619 /* NOTREACHED */ 620 return (-1); 621 } 622 623 /* 624 * Translate a string into a number. 625 * Like atoi(), but takes a pointer to a char *, and updates 626 * the char * to point after the translated number. 627 */ 628 public int 629 getnum(sp, printopt, errp) 630 char **sp; 631 char *printopt; 632 int *errp; 633 { 634 register char *s; 635 register int n; 636 register int neg; 637 638 s = skipsp(*sp); 639 neg = FALSE; 640 if (*s == '-') 641 { 642 neg = TRUE; 643 s++; 644 } 645 if (*s < '0' || *s > '9') 646 return (num_error(printopt, errp)); 647 648 n = 0; 649 while (*s >= '0' && *s <= '9') 650 n = 10 * n + *s++ - '0'; 651 *sp = s; 652 if (errp != NULL) 653 *errp = FALSE; 654 if (neg) 655 n = -n; 656 return (n); 657 } 658 659 /* 660 * Translate a string into a fraction, represented by the part of a 661 * number which would follow a decimal point. 662 * The value of the fraction is returned as parts per NUM_FRAC_DENOM. 663 * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM. 664 */ 665 public long 666 getfraction(sp, printopt, errp) 667 char **sp; 668 char *printopt; 669 int *errp; 670 { 671 register char *s; 672 long frac = 0; 673 int fraclen = 0; 674 675 s = skipsp(*sp); 676 if (*s < '0' || *s > '9') 677 return (num_error(printopt, errp)); 678 679 for ( ; *s >= '0' && *s <= '9'; s++) 680 { 681 frac = (frac * 10) + (*s - '0'); 682 fraclen++; 683 } 684 if (fraclen > NUM_LOG_FRAC_DENOM) 685 while (fraclen-- > NUM_LOG_FRAC_DENOM) 686 frac /= 10; 687 else 688 while (fraclen++ < NUM_LOG_FRAC_DENOM) 689 frac *= 10; 690 *sp = s; 691 if (errp != NULL) 692 *errp = FALSE; 693 return (frac); 694 } 695 696 697 /* 698 * Get the value of the -e flag. 699 */ 700 public int 701 get_quit_at_eof() 702 { 703 if (!less_is_more) 704 return quit_at_eof; 705 /* When less_is_more is set, the -e flag semantics are different. */ 706 return quit_at_eof ? OPT_ON : OPT_ONPLUS; 707 } 708