1 /* 2 * Copyright (C) 1984-2021 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 is_tty; 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 char rscroll_char; 54 extern int rscroll_attr; 55 extern int mousecap; 56 extern int wheel_lines; 57 extern int less_is_more; 58 extern int linenum_width; 59 extern int status_col_width; 60 extern int use_color; 61 #if LOGFILE 62 extern char *namelogfile; 63 extern int force_logfile; 64 extern int logfile; 65 #endif 66 #if TAGS 67 public char *tagoption = NULL; 68 extern char *tags; 69 extern char ztags[]; 70 #endif 71 #if LESSTEST 72 extern char *ttyin_name; 73 extern int rstat_file; 74 #endif /*LESSTEST*/ 75 #if MSDOS_COMPILER 76 extern int nm_fg_color, nm_bg_color; 77 extern int bo_fg_color, bo_bg_color; 78 extern int ul_fg_color, ul_bg_color; 79 extern int so_fg_color, so_bg_color; 80 extern int bl_fg_color, bl_bg_color; 81 extern int sgr_mode; 82 #if MSDOS_COMPILER==WIN32C 83 #ifndef COMMON_LVB_UNDERSCORE 84 #define COMMON_LVB_UNDERSCORE 0x8000 85 #endif 86 #endif 87 #endif 88 89 90 #if LOGFILE 91 /* 92 * Handler for -o option. 93 */ 94 public void 95 opt_o(type, s) 96 int type; 97 char *s; 98 { 99 PARG parg; 100 char *filename; 101 102 if (secure) 103 { 104 error("log file support is not available", NULL_PARG); 105 return; 106 } 107 switch (type) 108 { 109 case INIT: 110 namelogfile = save(s); 111 break; 112 case TOGGLE: 113 if (ch_getflags() & CH_CANSEEK) 114 { 115 error("Input is not a pipe", NULL_PARG); 116 return; 117 } 118 if (logfile >= 0) 119 { 120 error("Log file is already in use", NULL_PARG); 121 return; 122 } 123 s = skipsp(s); 124 if (namelogfile != NULL) 125 free(namelogfile); 126 filename = lglob(s); 127 namelogfile = shell_unquote(filename); 128 free(filename); 129 use_logfile(namelogfile); 130 sync_logfile(); 131 break; 132 case QUERY: 133 if (logfile < 0) 134 error("No log file", NULL_PARG); 135 else 136 { 137 parg.p_string = namelogfile; 138 error("Log file \"%s\"", &parg); 139 } 140 break; 141 } 142 } 143 144 /* 145 * Handler for -O option. 146 */ 147 public void 148 opt__O(type, s) 149 int type; 150 char *s; 151 { 152 force_logfile = TRUE; 153 opt_o(type, s); 154 } 155 #endif 156 157 /* 158 * Handlers for -j option. 159 */ 160 public void 161 opt_j(type, s) 162 int type; 163 char *s; 164 { 165 PARG parg; 166 char buf[16]; 167 int len; 168 int err; 169 170 switch (type) 171 { 172 case INIT: 173 case TOGGLE: 174 if (*s == '.') 175 { 176 s++; 177 jump_sline_fraction = getfraction(&s, "j", &err); 178 if (err) 179 error("Invalid line fraction", NULL_PARG); 180 else 181 calc_jump_sline(); 182 } else 183 { 184 int sline = getnum(&s, "j", &err); 185 if (err) 186 error("Invalid line number", NULL_PARG); 187 else 188 { 189 jump_sline = sline; 190 jump_sline_fraction = -1; 191 } 192 } 193 break; 194 case QUERY: 195 if (jump_sline_fraction < 0) 196 { 197 parg.p_int = jump_sline; 198 error("Position target at screen line %d", &parg); 199 } else 200 { 201 202 sprintf(buf, ".%06ld", jump_sline_fraction); 203 len = (int) strlen(buf); 204 while (len > 2 && buf[len-1] == '0') 205 len--; 206 buf[len] = '\0'; 207 parg.p_string = buf; 208 error("Position target at screen position %s", &parg); 209 } 210 break; 211 } 212 } 213 214 public void 215 calc_jump_sline(VOID_PARAM) 216 { 217 if (jump_sline_fraction < 0) 218 return; 219 jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM; 220 } 221 222 /* 223 * Handlers for -# option. 224 */ 225 public void 226 opt_shift(type, s) 227 int type; 228 char *s; 229 { 230 PARG parg; 231 char buf[16]; 232 int len; 233 int err; 234 235 switch (type) 236 { 237 case INIT: 238 case TOGGLE: 239 if (*s == '.') 240 { 241 s++; 242 shift_count_fraction = getfraction(&s, "#", &err); 243 if (err) 244 error("Invalid column fraction", NULL_PARG); 245 else 246 calc_shift_count(); 247 } else 248 { 249 int hs = getnum(&s, "#", &err); 250 if (err) 251 error("Invalid column number", NULL_PARG); 252 else 253 { 254 shift_count = hs; 255 shift_count_fraction = -1; 256 } 257 } 258 break; 259 case QUERY: 260 if (shift_count_fraction < 0) 261 { 262 parg.p_int = shift_count; 263 error("Horizontal shift %d columns", &parg); 264 } else 265 { 266 267 sprintf(buf, ".%06ld", shift_count_fraction); 268 len = (int) strlen(buf); 269 while (len > 2 && buf[len-1] == '0') 270 len--; 271 buf[len] = '\0'; 272 parg.p_string = buf; 273 error("Horizontal shift %s of screen width", &parg); 274 } 275 break; 276 } 277 } 278 public void 279 calc_shift_count(VOID_PARAM) 280 { 281 if (shift_count_fraction < 0) 282 return; 283 shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM; 284 } 285 286 #if USERFILE 287 public void 288 opt_k(type, s) 289 int type; 290 char *s; 291 { 292 PARG parg; 293 294 switch (type) 295 { 296 case INIT: 297 if (lesskey(s, 0)) 298 { 299 parg.p_string = s; 300 error("Cannot use lesskey file \"%s\"", &parg); 301 } 302 break; 303 } 304 } 305 #endif 306 307 #if TAGS 308 /* 309 * Handler for -t option. 310 */ 311 public void 312 opt_t(type, s) 313 int type; 314 char *s; 315 { 316 IFILE save_ifile; 317 POSITION pos; 318 319 switch (type) 320 { 321 case INIT: 322 tagoption = save(s); 323 /* Do the rest in main() */ 324 break; 325 case TOGGLE: 326 if (secure) 327 { 328 error("tags support is not available", NULL_PARG); 329 break; 330 } 331 findtag(skipsp(s)); 332 save_ifile = save_curr_ifile(); 333 /* 334 * Try to open the file containing the tag 335 * and search for the tag in that file. 336 */ 337 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION) 338 { 339 /* Failed: reopen the old file. */ 340 reedit_ifile(save_ifile); 341 break; 342 } 343 unsave_ifile(save_ifile); 344 jump_loc(pos, jump_sline); 345 break; 346 } 347 } 348 349 /* 350 * Handler for -T option. 351 */ 352 public void 353 opt__T(type, s) 354 int type; 355 char *s; 356 { 357 PARG parg; 358 char *filename; 359 360 switch (type) 361 { 362 case INIT: 363 tags = save(s); 364 break; 365 case TOGGLE: 366 s = skipsp(s); 367 if (tags != NULL && tags != ztags) 368 free(tags); 369 filename = lglob(s); 370 tags = shell_unquote(filename); 371 free(filename); 372 break; 373 case QUERY: 374 parg.p_string = tags; 375 error("Tags file \"%s\"", &parg); 376 break; 377 } 378 } 379 #endif 380 381 /* 382 * Handler for -p option. 383 */ 384 public void 385 opt_p(type, s) 386 int type; 387 char *s; 388 { 389 switch (type) 390 { 391 case INIT: 392 /* 393 * Unget a command for the specified string. 394 */ 395 if (less_is_more) 396 { 397 /* 398 * In "more" mode, the -p argument is a command, 399 * not a search string, so we don't need a slash. 400 */ 401 every_first_cmd = save(s); 402 } else 403 { 404 plusoption = TRUE; 405 /* 406 * {{ This won't work if the "/" command is 407 * changed or invalidated by a .lesskey file. }} 408 */ 409 ungetsc("/"); 410 ungetsc(s); 411 ungetcc_back(CHAR_END_COMMAND); 412 } 413 break; 414 } 415 } 416 417 /* 418 * Handler for -P option. 419 */ 420 public void 421 opt__P(type, s) 422 int type; 423 char *s; 424 { 425 char **proto; 426 PARG parg; 427 428 switch (type) 429 { 430 case INIT: 431 case TOGGLE: 432 /* 433 * Figure out which prototype string should be changed. 434 */ 435 switch (*s) 436 { 437 case 's': proto = &prproto[PR_SHORT]; s++; break; 438 case 'm': proto = &prproto[PR_MEDIUM]; s++; break; 439 case 'M': proto = &prproto[PR_LONG]; s++; break; 440 case '=': proto = &eqproto; s++; break; 441 case 'h': proto = &hproto; s++; break; 442 case 'w': proto = &wproto; s++; break; 443 default: proto = &prproto[PR_SHORT]; break; 444 } 445 free(*proto); 446 *proto = save(s); 447 break; 448 case QUERY: 449 parg.p_string = prproto[pr_type]; 450 error("%s", &parg); 451 break; 452 } 453 } 454 455 /* 456 * Handler for the -b option. 457 */ 458 /*ARGSUSED*/ 459 public void 460 opt_b(type, s) 461 int type; 462 char *s; 463 { 464 switch (type) 465 { 466 case INIT: 467 case TOGGLE: 468 /* 469 * Set the new number of buffers. 470 */ 471 ch_setbufspace(bufspace); 472 break; 473 case QUERY: 474 break; 475 } 476 } 477 478 /* 479 * Handler for the -i option. 480 */ 481 /*ARGSUSED*/ 482 public void 483 opt_i(type, s) 484 int type; 485 char *s; 486 { 487 switch (type) 488 { 489 case TOGGLE: 490 chg_caseless(); 491 break; 492 case QUERY: 493 case INIT: 494 break; 495 } 496 } 497 498 /* 499 * Handler for the -V option. 500 */ 501 /*ARGSUSED*/ 502 public void 503 opt__V(type, s) 504 int type; 505 char *s; 506 { 507 switch (type) 508 { 509 case TOGGLE: 510 case QUERY: 511 dispversion(); 512 break; 513 case INIT: 514 set_output(1); /* Force output to stdout per GNU standard for --version output. */ 515 putstr("less "); 516 putstr(version); 517 putstr(" ("); 518 putstr(pattern_lib_name()); 519 putstr(" regular expressions)\n"); 520 { 521 char constant *copyright = "Copyright (C) 1984-2021 Mark Nudelman\n\n"; 522 if (copyright[0] == '@') 523 copyright = "Copyright (C) 1984 Mark Nudelman\n\n"; 524 putstr(copyright); 525 } 526 if (version[strlen(version)-1] == 'x') 527 { 528 putstr("** This is an EXPERIMENTAL build of the 'less' software,\n"); 529 putstr("** and may not function correctly.\n"); 530 putstr("** Obtain release builds from the web page below.\n\n"); 531 } 532 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n"); 533 putstr("For information about the terms of redistribution,\n"); 534 putstr("see the file named README in the less distribution.\n"); 535 putstr("Home page: https://greenwoodsoftware.com/less\n"); 536 quit(QUIT_OK); 537 break; 538 } 539 } 540 541 #if MSDOS_COMPILER 542 /* 543 * Parse an MSDOS color descriptor. 544 */ 545 static void 546 colordesc(s, fg_color, bg_color) 547 char *s; 548 int *fg_color; 549 int *bg_color; 550 { 551 int fg, bg; 552 #if MSDOS_COMPILER==WIN32C 553 int ul = 0; 554 555 if (*s == 'u') 556 { 557 ul = COMMON_LVB_UNDERSCORE; 558 s++; 559 if (*s == '\0') 560 { 561 *fg_color = nm_fg_color | ul; 562 *bg_color = nm_bg_color; 563 return; 564 } 565 } 566 #endif 567 if (parse_color(s, &fg, &bg) == CT_NULL) 568 { 569 PARG p; 570 p.p_string = s; 571 error("Invalid color string \"%s\"", &p); 572 } else 573 { 574 if (fg == CV_NOCHANGE) 575 fg = nm_fg_color; 576 if (bg == CV_NOCHANGE) 577 bg = nm_bg_color; 578 #if MSDOS_COMPILER==WIN32C 579 fg |= ul; 580 #endif 581 *fg_color = fg; 582 *bg_color = bg; 583 } 584 } 585 #endif 586 587 static int 588 color_from_namechar(namechar) 589 char namechar; 590 { 591 switch (namechar) 592 { 593 case 'A': return AT_COLOR_ATTN; 594 case 'B': return AT_COLOR_BIN; 595 case 'C': return AT_COLOR_CTRL; 596 case 'E': return AT_COLOR_ERROR; 597 case 'M': return AT_COLOR_MARK; 598 case 'N': return AT_COLOR_LINENUM; 599 case 'P': return AT_COLOR_PROMPT; 600 case 'R': return AT_COLOR_RSCROLL; 601 case 'S': return AT_COLOR_SEARCH; 602 case 'n': return AT_NORMAL; 603 case 's': return AT_STANDOUT; 604 case 'd': return AT_BOLD; 605 case 'u': return AT_UNDERLINE; 606 case 'k': return AT_BLINK; 607 default: return -1; 608 } 609 } 610 611 /* 612 * Handler for the -D option. 613 */ 614 /*ARGSUSED*/ 615 public void 616 opt_D(type, s) 617 int type; 618 char *s; 619 { 620 PARG p; 621 int attr; 622 623 switch (type) 624 { 625 case INIT: 626 case TOGGLE: 627 #if MSDOS_COMPILER 628 if (*s == 'a') 629 { 630 sgr_mode = !sgr_mode; 631 break; 632 } 633 #endif 634 attr = color_from_namechar(s[0]); 635 if (attr < 0) 636 { 637 p.p_char = s[0]; 638 error("Invalid color specifier '%c'", &p); 639 return; 640 } 641 if (!use_color && (attr & AT_COLOR)) 642 { 643 error("Set --use-color before changing colors", NULL_PARG); 644 return; 645 } 646 s++; 647 #if MSDOS_COMPILER 648 if (!(attr & AT_COLOR)) 649 { 650 switch (attr) 651 { 652 case AT_NORMAL: 653 colordesc(s, &nm_fg_color, &nm_bg_color); 654 break; 655 case AT_BOLD: 656 colordesc(s, &bo_fg_color, &bo_bg_color); 657 break; 658 case AT_UNDERLINE: 659 colordesc(s, &ul_fg_color, &ul_bg_color); 660 break; 661 case AT_BLINK: 662 colordesc(s, &bl_fg_color, &bl_bg_color); 663 break; 664 case AT_STANDOUT: 665 colordesc(s, &so_fg_color, &so_bg_color); 666 break; 667 } 668 if (type == TOGGLE) 669 { 670 at_enter(AT_STANDOUT); 671 at_exit(); 672 } 673 } else 674 #endif 675 if (set_color_map(attr, s) < 0) 676 { 677 p.p_string = s; 678 error("Invalid color string \"%s\"", &p); 679 return; 680 } 681 break; 682 #if MSDOS_COMPILER 683 case QUERY: 684 p.p_string = (sgr_mode) ? "on" : "off"; 685 error("SGR mode is %s", &p); 686 break; 687 #endif 688 } 689 } 690 691 /* 692 * Handler for the -x option. 693 */ 694 public void 695 opt_x(type, s) 696 int type; 697 char *s; 698 { 699 extern int tabstops[]; 700 extern int ntabstops; 701 extern int tabdefault; 702 char msg[60+(4*TABSTOP_MAX)]; 703 int i; 704 PARG p; 705 706 switch (type) 707 { 708 case INIT: 709 case TOGGLE: 710 /* Start at 1 because tabstops[0] is always zero. */ 711 for (i = 1; i < TABSTOP_MAX; ) 712 { 713 int n = 0; 714 s = skipsp(s); 715 while (*s >= '0' && *s <= '9') 716 n = (10 * n) + (*s++ - '0'); 717 if (n > tabstops[i-1]) 718 tabstops[i++] = n; 719 s = skipsp(s); 720 if (*s++ != ',') 721 break; 722 } 723 if (i < 2) 724 return; 725 ntabstops = i; 726 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2]; 727 break; 728 case QUERY: 729 strcpy(msg, "Tab stops "); 730 if (ntabstops > 2) 731 { 732 for (i = 1; i < ntabstops; i++) 733 { 734 if (i > 1) 735 strcat(msg, ","); 736 sprintf(msg+strlen(msg), "%d", tabstops[i]); 737 } 738 sprintf(msg+strlen(msg), " and then "); 739 } 740 sprintf(msg+strlen(msg), "every %d spaces", 741 tabdefault); 742 p.p_string = msg; 743 error("%s", &p); 744 break; 745 } 746 } 747 748 749 /* 750 * Handler for the -" option. 751 */ 752 public void 753 opt_quote(type, s) 754 int type; 755 char *s; 756 { 757 char buf[3]; 758 PARG parg; 759 760 switch (type) 761 { 762 case INIT: 763 case TOGGLE: 764 if (s[0] == '\0') 765 { 766 openquote = closequote = '\0'; 767 break; 768 } 769 if (s[1] != '\0' && s[2] != '\0') 770 { 771 error("-\" must be followed by 1 or 2 chars", NULL_PARG); 772 return; 773 } 774 openquote = s[0]; 775 if (s[1] == '\0') 776 closequote = openquote; 777 else 778 closequote = s[1]; 779 break; 780 case QUERY: 781 buf[0] = openquote; 782 buf[1] = closequote; 783 buf[2] = '\0'; 784 parg.p_string = buf; 785 error("quotes %s", &parg); 786 break; 787 } 788 } 789 790 /* 791 * Handler for the --rscroll option. 792 */ 793 /*ARGSUSED*/ 794 public void 795 opt_rscroll(type, s) 796 int type; 797 char *s; 798 { 799 PARG p; 800 801 switch (type) 802 { 803 case INIT: 804 case TOGGLE: { 805 char *fmt; 806 int attr = AT_STANDOUT; 807 setfmt(s, &fmt, &attr, "*s>"); 808 if (strcmp(fmt, "-") == 0) 809 { 810 rscroll_char = 0; 811 } else 812 { 813 rscroll_char = *fmt ? *fmt : '>'; 814 rscroll_attr = attr|AT_COLOR_RSCROLL; 815 } 816 break; } 817 case QUERY: { 818 p.p_string = rscroll_char ? prchar(rscroll_char) : "-"; 819 error("rscroll char is %s", &p); 820 break; } 821 } 822 } 823 824 /* 825 * "-?" means display a help message. 826 * If from the command line, exit immediately. 827 */ 828 /*ARGSUSED*/ 829 public void 830 opt_query(type, s) 831 int type; 832 char *s; 833 { 834 switch (type) 835 { 836 case QUERY: 837 case TOGGLE: 838 error("Use \"h\" for help", NULL_PARG); 839 break; 840 case INIT: 841 dohelp = 1; 842 } 843 } 844 845 /* 846 * Handler for the --mouse option. 847 */ 848 /*ARGSUSED*/ 849 public void 850 opt_mousecap(type, s) 851 int type; 852 char *s; 853 { 854 switch (type) 855 { 856 case TOGGLE: 857 if (mousecap == OPT_OFF) 858 deinit_mouse(); 859 else 860 init_mouse(); 861 break; 862 case INIT: 863 case QUERY: 864 break; 865 } 866 } 867 868 /* 869 * Handler for the --wheel-lines option. 870 */ 871 /*ARGSUSED*/ 872 public void 873 opt_wheel_lines(type, s) 874 int type; 875 char *s; 876 { 877 switch (type) 878 { 879 case INIT: 880 case TOGGLE: 881 if (wheel_lines <= 0) 882 wheel_lines = default_wheel_lines(); 883 break; 884 case QUERY: 885 break; 886 } 887 } 888 889 /* 890 * Handler for the --line-number-width option. 891 */ 892 /*ARGSUSED*/ 893 public void 894 opt_linenum_width(type, s) 895 int type; 896 char *s; 897 { 898 PARG parg; 899 900 switch (type) 901 { 902 case INIT: 903 case TOGGLE: 904 if (linenum_width > MAX_LINENUM_WIDTH) 905 { 906 parg.p_int = MAX_LINENUM_WIDTH; 907 error("Line number width must not be larger than %d", &parg); 908 linenum_width = MIN_LINENUM_WIDTH; 909 } 910 break; 911 case QUERY: 912 break; 913 } 914 } 915 916 /* 917 * Handler for the --status-column-width option. 918 */ 919 /*ARGSUSED*/ 920 public void 921 opt_status_col_width(type, s) 922 int type; 923 char *s; 924 { 925 PARG parg; 926 927 switch (type) 928 { 929 case INIT: 930 case TOGGLE: 931 if (status_col_width > MAX_STATUSCOL_WIDTH) 932 { 933 parg.p_int = MAX_STATUSCOL_WIDTH; 934 error("Status column width must not be larger than %d", &parg); 935 status_col_width = 2; 936 } 937 break; 938 case QUERY: 939 break; 940 } 941 } 942 943 #if LESSTEST 944 /* 945 * Handler for the --tty option. 946 */ 947 /*ARGSUSED*/ 948 public void 949 opt_ttyin_name(type, s) 950 int type; 951 char *s; 952 { 953 switch (type) 954 { 955 case INIT: 956 ttyin_name = s; 957 is_tty = 1; 958 break; 959 } 960 } 961 962 /* 963 * Handler for the --rstat option. 964 */ 965 /*ARGSUSED*/ 966 public void 967 opt_rstat(type, s) 968 int type; 969 char *s; 970 { 971 switch (type) 972 { 973 case INIT: 974 rstat_file = open(s, O_WRONLY|O_CREAT, 0664); 975 if (rstat_file < 0) 976 { 977 PARG parg; 978 parg.p_string = s; 979 error("Cannot create rstat file \"%s\"", &parg); 980 } 981 break; 982 } 983 } 984 #endif /*LESSTEST*/ 985 986 /* 987 * Get the "screen window" size. 988 */ 989 public int 990 get_swindow(VOID_PARAM) 991 { 992 if (swindow > 0) 993 return (swindow); 994 return (sc_height + swindow); 995 } 996 997