1 /* 2 * Copyright (C) 1984-2017 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 11 /* 12 * Handling functions for command line options. 13 * 14 * Most options are handled by the generic code in option.c. 15 * But all string options, and a few non-string options, require 16 * special handling specific to the particular option. 17 * This special processing is done by the "handling functions" in this file. 18 * 19 * Each handling function is passed a "type" and, if it is a string 20 * option, the string which should be "assigned" to the option. 21 * The type may be one of: 22 * INIT The option is being initialized from the command line. 23 * TOGGLE The option is being changed from within the program. 24 * QUERY The setting of the option is merely being queried. 25 */ 26 27 #include "less.h" 28 #include "option.h" 29 30 extern int nbufs; 31 extern int bufspace; 32 extern int pr_type; 33 extern int plusoption; 34 extern int swindow; 35 extern int sc_width; 36 extern int sc_height; 37 extern int secure; 38 extern int dohelp; 39 extern int any_display; 40 extern char openquote; 41 extern char closequote; 42 extern char *prproto[]; 43 extern char *eqproto; 44 extern char *hproto; 45 extern char *wproto; 46 extern char *every_first_cmd; 47 extern IFILE curr_ifile; 48 extern char version[]; 49 extern int jump_sline; 50 extern long jump_sline_fraction; 51 extern int shift_count; 52 extern long shift_count_fraction; 53 extern LWCHAR rscroll_char; 54 extern int rscroll_attr; 55 extern int less_is_more; 56 #if LOGFILE 57 extern char *namelogfile; 58 extern int force_logfile; 59 extern int logfile; 60 #endif 61 #if TAGS 62 public char *tagoption = NULL; 63 extern char *tags; 64 extern char ztags[]; 65 #endif 66 #if MSDOS_COMPILER 67 extern int nm_fg_color, nm_bg_color; 68 extern int bo_fg_color, bo_bg_color; 69 extern int ul_fg_color, ul_bg_color; 70 extern int so_fg_color, so_bg_color; 71 extern int bl_fg_color, bl_bg_color; 72 extern int sgr_mode; 73 #if MSDOS_COMPILER==WIN32C 74 #ifndef COMMON_LVB_UNDERSCORE 75 #define COMMON_LVB_UNDERSCORE 0x8000 76 #endif 77 #endif 78 #endif 79 80 81 #if LOGFILE 82 /* 83 * Handler for -o option. 84 */ 85 public void 86 opt_o(type, s) 87 int type; 88 char *s; 89 { 90 PARG parg; 91 char *filename; 92 93 if (secure) 94 { 95 error("log file support is not available", NULL_PARG); 96 return; 97 } 98 switch (type) 99 { 100 case INIT: 101 namelogfile = save(s); 102 break; 103 case TOGGLE: 104 if (ch_getflags() & CH_CANSEEK) 105 { 106 error("Input is not a pipe", NULL_PARG); 107 return; 108 } 109 if (logfile >= 0) 110 { 111 error("Log file is already in use", NULL_PARG); 112 return; 113 } 114 s = skipsp(s); 115 if (namelogfile != NULL) 116 free(namelogfile); 117 filename = lglob(s); 118 namelogfile = shell_unquote(filename); 119 free(filename); 120 use_logfile(namelogfile); 121 sync_logfile(); 122 break; 123 case QUERY: 124 if (logfile < 0) 125 error("No log file", NULL_PARG); 126 else 127 { 128 parg.p_string = namelogfile; 129 error("Log file \"%s\"", &parg); 130 } 131 break; 132 } 133 } 134 135 /* 136 * Handler for -O option. 137 */ 138 public void 139 opt__O(type, s) 140 int type; 141 char *s; 142 { 143 force_logfile = TRUE; 144 opt_o(type, s); 145 } 146 #endif 147 148 /* 149 * Handlers for -j option. 150 */ 151 public void 152 opt_j(type, s) 153 int type; 154 char *s; 155 { 156 PARG parg; 157 char buf[16]; 158 int len; 159 int err; 160 161 switch (type) 162 { 163 case INIT: 164 case TOGGLE: 165 if (*s == '.') 166 { 167 s++; 168 jump_sline_fraction = getfraction(&s, "j", &err); 169 if (err) 170 error("Invalid line fraction", NULL_PARG); 171 else 172 calc_jump_sline(); 173 } else 174 { 175 int sline = getnum(&s, "j", &err); 176 if (err) 177 error("Invalid line number", NULL_PARG); 178 else 179 { 180 jump_sline = sline; 181 jump_sline_fraction = -1; 182 } 183 } 184 break; 185 case QUERY: 186 if (jump_sline_fraction < 0) 187 { 188 parg.p_int = jump_sline; 189 error("Position target at screen line %d", &parg); 190 } else 191 { 192 193 sprintf(buf, ".%06ld", jump_sline_fraction); 194 len = (int) strlen(buf); 195 while (len > 2 && buf[len-1] == '0') 196 len--; 197 buf[len] = '\0'; 198 parg.p_string = buf; 199 error("Position target at screen position %s", &parg); 200 } 201 break; 202 } 203 } 204 205 public void 206 calc_jump_sline() 207 { 208 if (jump_sline_fraction < 0) 209 return; 210 jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM; 211 } 212 213 /* 214 * Handlers for -# option. 215 */ 216 public void 217 opt_shift(type, s) 218 int type; 219 char *s; 220 { 221 PARG parg; 222 char buf[16]; 223 int len; 224 int err; 225 226 switch (type) 227 { 228 case INIT: 229 case TOGGLE: 230 if (*s == '.') 231 { 232 s++; 233 shift_count_fraction = getfraction(&s, "#", &err); 234 if (err) 235 error("Invalid column fraction", NULL_PARG); 236 else 237 calc_shift_count(); 238 } else 239 { 240 int hs = getnum(&s, "#", &err); 241 if (err) 242 error("Invalid column number", NULL_PARG); 243 else 244 { 245 shift_count = hs; 246 shift_count_fraction = -1; 247 } 248 } 249 break; 250 case QUERY: 251 if (shift_count_fraction < 0) 252 { 253 parg.p_int = shift_count; 254 error("Horizontal shift %d columns", &parg); 255 } else 256 { 257 258 sprintf(buf, ".%06ld", shift_count_fraction); 259 len = (int) strlen(buf); 260 while (len > 2 && buf[len-1] == '0') 261 len--; 262 buf[len] = '\0'; 263 parg.p_string = buf; 264 error("Horizontal shift %s of screen width", &parg); 265 } 266 break; 267 } 268 } 269 public void 270 calc_shift_count() 271 { 272 if (shift_count_fraction < 0) 273 return; 274 shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM; 275 } 276 277 #if USERFILE 278 public void 279 opt_k(type, s) 280 int type; 281 char *s; 282 { 283 PARG parg; 284 285 switch (type) 286 { 287 case INIT: 288 if (lesskey(s, 0)) 289 { 290 parg.p_string = s; 291 error("Cannot use lesskey file \"%s\"", &parg); 292 } 293 break; 294 } 295 } 296 #endif 297 298 #if TAGS 299 /* 300 * Handler for -t option. 301 */ 302 public void 303 opt_t(type, s) 304 int type; 305 char *s; 306 { 307 IFILE save_ifile; 308 POSITION pos; 309 310 switch (type) 311 { 312 case INIT: 313 tagoption = save(s); 314 /* Do the rest in main() */ 315 break; 316 case TOGGLE: 317 if (secure) 318 { 319 error("tags support is not available", NULL_PARG); 320 break; 321 } 322 findtag(skipsp(s)); 323 save_ifile = save_curr_ifile(); 324 /* 325 * Try to open the file containing the tag 326 * and search for the tag in that file. 327 */ 328 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION) 329 { 330 /* Failed: reopen the old file. */ 331 reedit_ifile(save_ifile); 332 break; 333 } 334 unsave_ifile(save_ifile); 335 jump_loc(pos, jump_sline); 336 break; 337 } 338 } 339 340 /* 341 * Handler for -T option. 342 */ 343 public void 344 opt__T(type, s) 345 int type; 346 char *s; 347 { 348 PARG parg; 349 char *filename; 350 351 switch (type) 352 { 353 case INIT: 354 tags = save(s); 355 break; 356 case TOGGLE: 357 s = skipsp(s); 358 if (tags != NULL && tags != ztags) 359 free(tags); 360 filename = lglob(s); 361 tags = shell_unquote(filename); 362 free(filename); 363 break; 364 case QUERY: 365 parg.p_string = tags; 366 error("Tags file \"%s\"", &parg); 367 break; 368 } 369 } 370 #endif 371 372 /* 373 * Handler for -p option. 374 */ 375 public void 376 opt_p(type, s) 377 int type; 378 char *s; 379 { 380 switch (type) 381 { 382 case INIT: 383 /* 384 * Unget a command for the specified string. 385 */ 386 if (less_is_more) 387 { 388 /* 389 * In "more" mode, the -p argument is a command, 390 * not a search string, so we don't need a slash. 391 */ 392 every_first_cmd = save(s); 393 } else 394 { 395 plusoption = TRUE; 396 ungetcc(CHAR_END_COMMAND); 397 ungetsc(s); 398 /* 399 * {{ This won't work if the "/" command is 400 * changed or invalidated by a .lesskey file. }} 401 */ 402 ungetsc("/"); 403 } 404 break; 405 } 406 } 407 408 /* 409 * Handler for -P option. 410 */ 411 public void 412 opt__P(type, s) 413 int type; 414 char *s; 415 { 416 char **proto; 417 PARG parg; 418 419 switch (type) 420 { 421 case INIT: 422 case TOGGLE: 423 /* 424 * Figure out which prototype string should be changed. 425 */ 426 switch (*s) 427 { 428 case 's': proto = &prproto[PR_SHORT]; s++; break; 429 case 'm': proto = &prproto[PR_MEDIUM]; s++; break; 430 case 'M': proto = &prproto[PR_LONG]; s++; break; 431 case '=': proto = &eqproto; s++; break; 432 case 'h': proto = &hproto; s++; break; 433 case 'w': proto = &wproto; s++; break; 434 default: proto = &prproto[PR_SHORT]; break; 435 } 436 free(*proto); 437 *proto = save(s); 438 break; 439 case QUERY: 440 parg.p_string = prproto[pr_type]; 441 error("%s", &parg); 442 break; 443 } 444 } 445 446 /* 447 * Handler for the -b option. 448 */ 449 /*ARGSUSED*/ 450 public void 451 opt_b(type, s) 452 int type; 453 char *s; 454 { 455 switch (type) 456 { 457 case INIT: 458 case TOGGLE: 459 /* 460 * Set the new number of buffers. 461 */ 462 ch_setbufspace(bufspace); 463 break; 464 case QUERY: 465 break; 466 } 467 } 468 469 /* 470 * Handler for the -i option. 471 */ 472 /*ARGSUSED*/ 473 public void 474 opt_i(type, s) 475 int type; 476 char *s; 477 { 478 switch (type) 479 { 480 case TOGGLE: 481 chg_caseless(); 482 break; 483 case QUERY: 484 case INIT: 485 break; 486 } 487 } 488 489 /* 490 * Handler for the -V option. 491 */ 492 /*ARGSUSED*/ 493 public void 494 opt__V(type, s) 495 int type; 496 char *s; 497 { 498 switch (type) 499 { 500 case TOGGLE: 501 case QUERY: 502 dispversion(); 503 break; 504 case INIT: 505 /* 506 * Force output to stdout per GNU standard for --version output. 507 */ 508 any_display = 1; 509 putstr("less "); 510 putstr(version); 511 putstr(" ("); 512 #if HAVE_GNU_REGEX 513 putstr("GNU "); 514 #endif 515 #if HAVE_POSIX_REGCOMP 516 putstr("POSIX "); 517 #endif 518 #if HAVE_PCRE 519 putstr("PCRE "); 520 #endif 521 #if HAVE_RE_COMP 522 putstr("BSD "); 523 #endif 524 #if HAVE_REGCMP 525 putstr("V8 "); 526 #endif 527 #if HAVE_V8_REGCOMP 528 putstr("Spencer V8 "); 529 #endif 530 #if !HAVE_GNU_REGEX && !HAVE_POSIX_REGCOMP && !HAVE_PCRE && !HAVE_RE_COMP && !HAVE_REGCMP && !HAVE_V8_REGCOMP 531 putstr("no "); 532 #endif 533 putstr("regular expressions)\n"); 534 putstr("Copyright (C) 1984-2017 Mark Nudelman\n\n"); 535 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n"); 536 putstr("For information about the terms of redistribution,\n"); 537 putstr("see the file named README in the less distribution.\n"); 538 putstr("Homepage: http://www.greenwoodsoftware.com/less\n"); 539 quit(QUIT_OK); 540 break; 541 } 542 } 543 544 #if MSDOS_COMPILER 545 /* 546 * Parse an MSDOS color descriptor. 547 */ 548 static void 549 colordesc(s, fg_color, bg_color) 550 char *s; 551 int *fg_color; 552 int *bg_color; 553 { 554 int fg, bg; 555 int err; 556 #if MSDOS_COMPILER==WIN32C 557 int ul = 0; 558 559 if (*s == 'u') 560 { 561 ul = COMMON_LVB_UNDERSCORE; 562 ++s; 563 } 564 #endif 565 fg = getnum(&s, "D", &err); 566 if (err) 567 { 568 #if MSDOS_COMPILER==WIN32C 569 if (ul) 570 fg = nm_fg_color; 571 else 572 #endif 573 { 574 error("Missing fg color in -D", NULL_PARG); 575 return; 576 } 577 } 578 if (*s != '.') 579 bg = nm_bg_color; 580 else 581 { 582 s++; 583 bg = getnum(&s, "D", &err); 584 if (err) 585 { 586 error("Missing bg color in -D", NULL_PARG); 587 return; 588 } 589 } 590 #if MSDOS_COMPILER==WIN32C 591 if (*s == 'u') 592 { 593 ul = COMMON_LVB_UNDERSCORE; 594 ++s; 595 } 596 fg |= ul; 597 #endif 598 if (*s != '\0') 599 error("Extra characters at end of -D option", NULL_PARG); 600 *fg_color = fg; 601 *bg_color = bg; 602 } 603 604 /* 605 * Handler for the -D option. 606 */ 607 /*ARGSUSED*/ 608 public void 609 opt_D(type, s) 610 int type; 611 char *s; 612 { 613 PARG p; 614 615 switch (type) 616 { 617 case INIT: 618 case TOGGLE: 619 switch (*s++) 620 { 621 case 'n': 622 colordesc(s, &nm_fg_color, &nm_bg_color); 623 break; 624 case 'd': 625 colordesc(s, &bo_fg_color, &bo_bg_color); 626 break; 627 case 'u': 628 colordesc(s, &ul_fg_color, &ul_bg_color); 629 break; 630 case 'k': 631 colordesc(s, &bl_fg_color, &bl_bg_color); 632 break; 633 case 's': 634 colordesc(s, &so_fg_color, &so_bg_color); 635 break; 636 case 'a': 637 sgr_mode = !sgr_mode; 638 break; 639 default: 640 error("-D must be followed by n, d, u, k, s or a", NULL_PARG); 641 break; 642 } 643 if (type == TOGGLE) 644 { 645 at_enter(AT_STANDOUT); 646 at_exit(); 647 } 648 break; 649 case QUERY: 650 p.p_string = (sgr_mode) ? "on" : "off"; 651 error("SGR mode is %s", &p); 652 break; 653 } 654 } 655 #endif 656 657 /* 658 * Handler for the -x option. 659 */ 660 public void 661 opt_x(type, s) 662 int type; 663 char *s; 664 { 665 extern int tabstops[]; 666 extern int ntabstops; 667 extern int tabdefault; 668 char msg[60+(4*TABSTOP_MAX)]; 669 int i; 670 PARG p; 671 672 switch (type) 673 { 674 case INIT: 675 case TOGGLE: 676 /* Start at 1 because tabstops[0] is always zero. */ 677 for (i = 1; i < TABSTOP_MAX; ) 678 { 679 int n = 0; 680 s = skipsp(s); 681 while (*s >= '0' && *s <= '9') 682 n = (10 * n) + (*s++ - '0'); 683 if (n > tabstops[i-1]) 684 tabstops[i++] = n; 685 s = skipsp(s); 686 if (*s++ != ',') 687 break; 688 } 689 if (i < 2) 690 return; 691 ntabstops = i; 692 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2]; 693 break; 694 case QUERY: 695 strcpy(msg, "Tab stops "); 696 if (ntabstops > 2) 697 { 698 for (i = 1; i < ntabstops; i++) 699 { 700 if (i > 1) 701 strcat(msg, ","); 702 sprintf(msg+strlen(msg), "%d", tabstops[i]); 703 } 704 sprintf(msg+strlen(msg), " and then "); 705 } 706 sprintf(msg+strlen(msg), "every %d spaces", 707 tabdefault); 708 p.p_string = msg; 709 error("%s", &p); 710 break; 711 } 712 } 713 714 715 /* 716 * Handler for the -" option. 717 */ 718 public void 719 opt_quote(type, s) 720 int type; 721 char *s; 722 { 723 char buf[3]; 724 PARG parg; 725 726 switch (type) 727 { 728 case INIT: 729 case TOGGLE: 730 if (s[0] == '\0') 731 { 732 openquote = closequote = '\0'; 733 break; 734 } 735 if (s[1] != '\0' && s[2] != '\0') 736 { 737 error("-\" must be followed by 1 or 2 chars", NULL_PARG); 738 return; 739 } 740 openquote = s[0]; 741 if (s[1] == '\0') 742 closequote = openquote; 743 else 744 closequote = s[1]; 745 break; 746 case QUERY: 747 buf[0] = openquote; 748 buf[1] = closequote; 749 buf[2] = '\0'; 750 parg.p_string = buf; 751 error("quotes %s", &parg); 752 break; 753 } 754 } 755 756 /* 757 * Handler for the --rscroll option. 758 */ 759 /*ARGSUSED*/ 760 public void 761 opt_rscroll(type, s) 762 int type; 763 char *s; 764 { 765 PARG p; 766 767 switch (type) 768 { 769 case INIT: 770 case TOGGLE: { 771 char *fmt; 772 int attr = AT_STANDOUT; 773 setfmt(s, &fmt, &attr, "*s>"); 774 if (strcmp(fmt, "-") == 0) 775 { 776 rscroll_char = 0; 777 } else 778 { 779 rscroll_char = *fmt ? *fmt : '>'; 780 rscroll_attr = attr; 781 } 782 break; } 783 case QUERY: { 784 p.p_string = rscroll_char ? prchar(rscroll_char) : "-"; 785 error("rscroll char is %s", &p); 786 break; } 787 } 788 } 789 790 /* 791 * "-?" means display a help message. 792 * If from the command line, exit immediately. 793 */ 794 /*ARGSUSED*/ 795 public void 796 opt_query(type, s) 797 int type; 798 char *s; 799 { 800 switch (type) 801 { 802 case QUERY: 803 case TOGGLE: 804 error("Use \"h\" for help", NULL_PARG); 805 break; 806 case INIT: 807 dohelp = 1; 808 } 809 } 810 811 /* 812 * Get the "screen window" size. 813 */ 814 public int 815 get_swindow() 816 { 817 if (swindow > 0) 818 return (swindow); 819 return (sc_height + swindow); 820 } 821 822