1 /* 2 * Copyright (C) 1984-2025 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 #include "position.h" 30 31 extern int bufspace; 32 extern int pr_type; 33 extern lbool plusoption; 34 extern int swindow; 35 extern int sc_width; 36 extern int sc_height; 37 extern int dohelp; 38 extern char openquote; 39 extern char closequote; 40 extern char *prproto[]; 41 extern char *eqproto; 42 extern char *hproto; 43 extern char *wproto; 44 extern char *every_first_cmd; 45 extern IFILE curr_ifile; 46 extern char version[]; 47 extern int jump_sline; 48 extern long jump_sline_fraction; 49 extern int shift_count; 50 extern long shift_count_fraction; 51 extern int match_shift; 52 extern long match_shift_fraction; 53 extern LWCHAR 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 extern int want_filesize; 62 extern int header_lines; 63 extern int header_cols; 64 extern int def_search_type; 65 extern int chopline; 66 extern int tabstops[]; 67 extern int ntabstops; 68 extern int tabdefault; 69 extern int no_paste; 70 extern char intr_char; 71 extern int nosearch_header_lines; 72 extern int nosearch_header_cols; 73 extern POSITION header_start_pos; 74 extern char *init_header; 75 #if LOGFILE 76 extern char *namelogfile; 77 extern lbool force_logfile; 78 extern int logfile; 79 #endif 80 #if TAGS 81 public char *tagoption = NULL; 82 extern char *tags; 83 extern char ztags[]; 84 #endif 85 #if LESSTEST 86 extern constant char *ttyin_name; 87 extern int is_tty; 88 #endif /*LESSTEST*/ 89 #if MSDOS_COMPILER 90 extern int nm_fg_color, nm_bg_color, nm_attr; 91 extern int bo_fg_color, bo_bg_color, bo_attr; 92 extern int ul_fg_color, ul_bg_color, ul_attr; 93 extern int so_fg_color, so_bg_color, so_attr; 94 extern int bl_fg_color, bl_bg_color, bl_attr; 95 extern int sgr_mode; 96 #if MSDOS_COMPILER==WIN32C 97 #ifndef COMMON_LVB_UNDERSCORE 98 #define COMMON_LVB_UNDERSCORE 0x8000 99 #endif 100 #ifndef COMMON_LVB_REVERSE_VIDEO 101 #define COMMON_LVB_REVERSE_VIDEO 0x4000 102 #endif 103 #endif 104 #endif 105 106 107 #if LOGFILE 108 /* 109 * Handler for -o option. 110 */ 111 public void opt_o(int type, constant char *s) 112 { 113 PARG parg; 114 char *filename; 115 116 if (!secure_allow(SF_LOGFILE)) 117 { 118 error("log file support is not available", NULL_PARG); 119 return; 120 } 121 switch (type) 122 { 123 case INIT: 124 namelogfile = save(s); 125 break; 126 case TOGGLE: 127 if (ch_getflags() & CH_CANSEEK) 128 { 129 error("Input is not a pipe", NULL_PARG); 130 return; 131 } 132 if (logfile >= 0) 133 { 134 error("Log file is already in use", NULL_PARG); 135 return; 136 } 137 s = skipspc(s); 138 if (namelogfile != NULL) 139 free(namelogfile); 140 filename = lglob(s); 141 namelogfile = shell_unquote(filename); 142 free(filename); 143 use_logfile(namelogfile); 144 sync_logfile(); 145 break; 146 case QUERY: 147 if (logfile < 0) 148 error("No log file", NULL_PARG); 149 else 150 { 151 parg.p_string = namelogfile; 152 error("Log file \"%s\"", &parg); 153 } 154 break; 155 } 156 } 157 158 /* 159 * Handler for -O option. 160 */ 161 public void opt__O(int type, constant char *s) 162 { 163 force_logfile = TRUE; 164 opt_o(type, s); 165 } 166 #endif 167 168 static int toggle_fraction(int *num, long *frac, constant char *s, constant char *printopt, void (*calc)(void)) 169 { 170 lbool err; 171 if (s == NULL) 172 { 173 (*calc)(); 174 } else if (*s == '.') 175 { 176 long tfrac; 177 s++; 178 tfrac = getfraction(&s, printopt, &err); 179 if (err) 180 { 181 error("Invalid fraction", NULL_PARG); 182 return -1; 183 } 184 *frac = tfrac; 185 (*calc)(); 186 } else 187 { 188 int tnum = getnumc(&s, printopt, &err); 189 if (err) 190 { 191 error("Invalid number", NULL_PARG); 192 return -1; 193 } 194 *frac = -1; 195 *num = tnum; 196 } 197 return 0; 198 } 199 200 static void query_fraction(int value, long fraction, constant char *int_msg, constant char *frac_msg) 201 { 202 PARG parg; 203 204 if (fraction < 0) 205 { 206 parg.p_int = value; 207 error(int_msg, &parg); 208 } else 209 { 210 char buf[INT_STRLEN_BOUND(long)+2]; 211 size_t len; 212 SNPRINTF1(buf, sizeof(buf), ".%06ld", fraction); 213 len = strlen(buf); 214 while (len > 2 && buf[len-1] == '0') 215 len--; 216 buf[len] = '\0'; 217 parg.p_string = buf; 218 error(frac_msg, &parg); 219 } 220 } 221 222 /* 223 * Handlers for -j option. 224 */ 225 public void opt_j(int type, constant char *s) 226 { 227 switch (type) 228 { 229 case INIT: 230 case TOGGLE: 231 toggle_fraction(&jump_sline, &jump_sline_fraction, 232 s, "j", calc_jump_sline); 233 break; 234 case QUERY: 235 query_fraction(jump_sline, jump_sline_fraction, 236 "Position target at screen line %d", "Position target at screen position %s"); 237 break; 238 } 239 } 240 241 public void calc_jump_sline(void) 242 { 243 if (jump_sline_fraction >= 0) 244 jump_sline = (int) muldiv(sc_height, jump_sline_fraction, NUM_FRAC_DENOM); 245 if (jump_sline <= header_lines) 246 jump_sline = header_lines + 1; 247 } 248 249 /* 250 * Handlers for -# option. 251 */ 252 public void opt_shift(int type, constant char *s) 253 { 254 switch (type) 255 { 256 case INIT: 257 case TOGGLE: 258 toggle_fraction(&shift_count, &shift_count_fraction, 259 s, "#", calc_shift_count); 260 break; 261 case QUERY: 262 query_fraction(shift_count, shift_count_fraction, 263 "Horizontal shift %d columns", "Horizontal shift %s of screen width"); 264 break; 265 } 266 } 267 268 public void calc_shift_count(void) 269 { 270 if (shift_count_fraction < 0) 271 return; 272 shift_count = (int) muldiv(sc_width, shift_count_fraction, NUM_FRAC_DENOM); 273 } 274 275 #if USERFILE 276 public void opt_k(int type, constant char *s) 277 { 278 PARG parg; 279 280 switch (type) 281 { 282 case INIT: 283 if (lesskey(s, 0)) 284 { 285 parg.p_string = s; 286 error("Cannot use lesskey file \"%s\"", &parg); 287 } 288 break; 289 } 290 } 291 292 #if HAVE_LESSKEYSRC 293 public void opt_ks(int type, constant char *s) 294 { 295 PARG parg; 296 297 switch (type) 298 { 299 case INIT: 300 if (lesskey_src(s, 0)) 301 { 302 parg.p_string = s; 303 error("Cannot use lesskey source file \"%s\"", &parg); 304 } 305 break; 306 } 307 } 308 309 public void opt_kc(int type, constant char *s) 310 { 311 switch (type) 312 { 313 case INIT: 314 if (lesskey_content(s, 0)) 315 { 316 error("Error in lesskey content", NULL_PARG); 317 } 318 break; 319 } 320 } 321 322 #endif /* HAVE_LESSKEYSRC */ 323 #endif /* USERFILE */ 324 325 /* 326 * Handler for -S option. 327 */ 328 public void opt__S(int type, constant char *s) 329 { 330 switch (type) 331 { 332 case TOGGLE: 333 pos_rehead(); 334 break; 335 } 336 } 337 338 #if TAGS 339 /* 340 * Handler for -t option. 341 */ 342 public void opt_t(int type, constant char *s) 343 { 344 IFILE save_ifile; 345 POSITION pos; 346 347 switch (type) 348 { 349 case INIT: 350 tagoption = save(s); 351 /* Do the rest in main() */ 352 break; 353 case TOGGLE: 354 if (!secure_allow(SF_TAGS)) 355 { 356 error("tags support is not available", NULL_PARG); 357 break; 358 } 359 findtag(skipspc(s)); 360 save_ifile = save_curr_ifile(); 361 /* 362 * Try to open the file containing the tag 363 * and search for the tag in that file. 364 */ 365 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION) 366 { 367 /* Failed: reopen the old file. */ 368 reedit_ifile(save_ifile); 369 break; 370 } 371 unsave_ifile(save_ifile); 372 jump_loc(pos, jump_sline); 373 break; 374 } 375 } 376 377 /* 378 * Handler for -T option. 379 */ 380 public void opt__T(int type, constant char *s) 381 { 382 PARG parg; 383 char *filename; 384 385 switch (type) 386 { 387 case INIT: 388 tags = save(s); 389 break; 390 case TOGGLE: 391 s = skipspc(s); 392 if (tags != NULL && tags != ztags) 393 free(tags); 394 filename = lglob(s); 395 tags = shell_unquote(filename); 396 free(filename); 397 break; 398 case QUERY: 399 parg.p_string = tags; 400 error("Tags file \"%s\"", &parg); 401 break; 402 } 403 } 404 #endif 405 406 /* 407 * Handler for -p option. 408 */ 409 public void opt_p(int type, constant char *s) 410 { 411 switch (type) 412 { 413 case INIT: 414 /* 415 * Unget a command for the specified string. 416 */ 417 if (less_is_more) 418 { 419 /* 420 * In "more" mode, the -p argument is a command, 421 * not a search string, so we don't need a slash. 422 */ 423 every_first_cmd = save(s); 424 } else 425 { 426 plusoption = TRUE; 427 /* 428 * {{ This won't work if the "/" command is 429 * changed or invalidated by a .lesskey file. }} 430 */ 431 ungetsc("/"); 432 ungetsc(s); 433 ungetcc_end_command(); 434 } 435 break; 436 } 437 } 438 439 /* 440 * Handler for -P option. 441 */ 442 public void opt__P(int type, constant char *s) 443 { 444 char **proto; 445 PARG parg; 446 447 switch (type) 448 { 449 case INIT: 450 case TOGGLE: 451 /* 452 * Figure out which prototype string should be changed. 453 */ 454 switch (*s) 455 { 456 case 's': proto = &prproto[PR_SHORT]; s++; break; 457 case 'm': proto = &prproto[PR_MEDIUM]; s++; break; 458 case 'M': proto = &prproto[PR_LONG]; s++; break; 459 case '=': proto = &eqproto; s++; break; 460 case 'h': proto = &hproto; s++; break; 461 case 'w': proto = &wproto; s++; break; 462 default: proto = &prproto[PR_SHORT]; break; 463 } 464 free(*proto); 465 *proto = save(s); 466 break; 467 case QUERY: 468 parg.p_string = prproto[pr_type]; 469 error("%s", &parg); 470 break; 471 } 472 } 473 474 /* 475 * Handler for the -b option. 476 */ 477 /*ARGSUSED*/ 478 public void opt_b(int type, constant char *s) 479 { 480 switch (type) 481 { 482 case INIT: 483 case TOGGLE: 484 /* 485 * Set the new number of buffers. 486 */ 487 ch_setbufspace((ssize_t) bufspace); 488 break; 489 case QUERY: 490 break; 491 } 492 } 493 494 /* 495 * Handler for the -i option. 496 */ 497 /*ARGSUSED*/ 498 public void opt_i(int type, constant char *s) 499 { 500 switch (type) 501 { 502 case TOGGLE: 503 chg_caseless(); 504 break; 505 case QUERY: 506 case INIT: 507 break; 508 } 509 } 510 511 /* 512 * Handler for the -V option. 513 */ 514 /*ARGSUSED*/ 515 public void opt__V(int type, constant char *s) 516 { 517 switch (type) 518 { 519 case TOGGLE: 520 case QUERY: 521 dispversion(); 522 break; 523 case INIT: 524 set_output(1); /* Force output to stdout per GNU standard for --version output. */ 525 putstr("less "); 526 putstr(version); 527 putstr(" ("); 528 putstr(pattern_lib_name()); 529 putstr(" regular expressions)\n"); 530 { 531 char constant *copyright = 532 "Copyright (C) 1984-2025 Mark Nudelman\n\n"; 533 putstr(copyright); 534 } 535 if (version[strlen(version)-1] == 'x') 536 { 537 putstr("** This is an EXPERIMENTAL build of the 'less' software,\n"); 538 putstr("** and may not function correctly.\n"); 539 putstr("** Obtain release builds from the web page below.\n\n"); 540 } 541 #if LESSTEST 542 putstr("This build supports LESSTEST.\n"); 543 #endif /*LESSTEST*/ 544 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n"); 545 putstr("For information about the terms of redistribution,\n"); 546 putstr("see the file named README in the less distribution.\n"); 547 putstr("Home page: https://greenwoodsoftware.com/less\n"); 548 quit(QUIT_OK); 549 break; 550 } 551 } 552 553 #if MSDOS_COMPILER 554 /* 555 * Parse an MSDOS color descriptor. 556 */ 557 static void colordesc(constant char *s, int *fg_color, int *bg_color, int *dattr) 558 { 559 int fg, bg; 560 CHAR_ATTR attr; 561 if (parse_color(s, &fg, &bg, &attr) == CT_NULL) 562 { 563 PARG p; 564 p.p_string = s; 565 error("Invalid color string \"%s\"", &p); 566 } else 567 { 568 *fg_color = fg; 569 *bg_color = bg; 570 *dattr = 0; 571 #if MSDOS_COMPILER==WIN32C 572 if (attr & CATTR_UNDERLINE) 573 *dattr |= COMMON_LVB_UNDERSCORE; 574 if (attr & CATTR_STANDOUT) 575 *dattr |= COMMON_LVB_REVERSE_VIDEO; 576 #endif 577 } 578 } 579 #endif 580 581 static int color_from_namechar(char namechar) 582 { 583 switch (namechar) 584 { 585 case 'B': return AT_COLOR_BIN; 586 case 'C': return AT_COLOR_CTRL; 587 case 'E': return AT_COLOR_ERROR; 588 case 'H': return AT_COLOR_HEADER; 589 case 'M': return AT_COLOR_MARK; 590 case 'N': return AT_COLOR_LINENUM; 591 case 'P': return AT_COLOR_PROMPT; 592 case 'R': return AT_COLOR_RSCROLL; 593 case 'S': return AT_COLOR_SEARCH; 594 case 'W': case 'A': return AT_COLOR_ATTN; 595 case 'n': return AT_NORMAL; 596 case 's': return AT_STANDOUT; 597 case 'd': return AT_BOLD; 598 case 'u': return AT_UNDERLINE; 599 case 'k': return AT_BLINK; 600 default: 601 if (namechar >= '1' && namechar <= '0'+NUM_SEARCH_COLORS) 602 return AT_COLOR_SUBSEARCH(namechar-'0'); 603 return -1; 604 } 605 } 606 607 /* 608 * Handler for the -D option. 609 */ 610 /*ARGSUSED*/ 611 public void opt_D(int type, constant char *s) 612 { 613 PARG p; 614 int attr; 615 616 switch (type) 617 { 618 case INIT: 619 case TOGGLE: 620 #if MSDOS_COMPILER 621 if (*s == 'a') 622 { 623 sgr_mode = !sgr_mode; 624 break; 625 } 626 #endif 627 attr = color_from_namechar(s[0]); 628 if (attr < 0) 629 { 630 p.p_char = s[0]; 631 error("Invalid color specifier '%c'", &p); 632 return; 633 } 634 if (!use_color && (attr & AT_COLOR)) 635 { 636 error("Set --use-color before changing colors", NULL_PARG); 637 return; 638 } 639 s++; 640 #if MSDOS_COMPILER 641 if (!(attr & AT_COLOR)) 642 { 643 switch (attr) 644 { 645 case AT_NORMAL: 646 colordesc(s, &nm_fg_color, &nm_bg_color, &nm_attr); 647 break; 648 case AT_BOLD: 649 colordesc(s, &bo_fg_color, &bo_bg_color, &bo_attr); 650 break; 651 case AT_UNDERLINE: 652 colordesc(s, &ul_fg_color, &ul_bg_color, &ul_attr); 653 break; 654 case AT_BLINK: 655 colordesc(s, &bl_fg_color, &bl_bg_color, &bl_attr); 656 break; 657 case AT_STANDOUT: 658 colordesc(s, &so_fg_color, &so_bg_color, &so_attr); 659 break; 660 } 661 if (type == TOGGLE) 662 { 663 init_win_colors(); 664 at_enter(AT_STANDOUT); 665 at_exit(); 666 } 667 } else 668 #endif 669 if (set_color_map(attr, s) < 0) 670 { 671 p.p_string = s; 672 error("Invalid color string \"%s\"", &p); 673 return; 674 } 675 break; 676 #if MSDOS_COMPILER 677 case QUERY: 678 p.p_string = (sgr_mode) ? "on" : "off"; 679 error("SGR mode is %s", &p); 680 break; 681 #endif 682 } 683 } 684 685 /* 686 */ 687 public void set_tabs(constant char *s, size_t len) 688 { 689 int i; 690 constant char *es = s + len; 691 /* Start at 1 because tabstops[0] is always zero. */ 692 for (i = 1; i < TABSTOP_MAX; ) 693 { 694 int n = 0; 695 lbool v = FALSE; 696 while (s < es && *s == ' ') 697 s++; 698 for (; s < es && *s >= '0' && *s <= '9'; s++) 699 { 700 v = v || ckd_mul(&n, n, 10); 701 v = v || ckd_add(&n, n, *s - '0'); 702 } 703 if (!v && n > tabstops[i-1]) 704 tabstops[i++] = n; 705 while (s < es && *s == ' ') 706 s++; 707 if (s == es || *s++ != ',') 708 break; 709 } 710 if (i < 2) 711 return; 712 ntabstops = i; 713 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2]; 714 } 715 716 /* 717 * Handler for the -x option. 718 */ 719 public void opt_x(int type, constant char *s) 720 { 721 char msg[60+((INT_STRLEN_BOUND(int)+1)*TABSTOP_MAX)]; 722 int i; 723 PARG p; 724 725 switch (type) 726 { 727 case INIT: 728 case TOGGLE: 729 set_tabs(s, strlen(s)); 730 break; 731 case QUERY: 732 strcpy(msg, "Tab stops "); 733 if (ntabstops > 2) 734 { 735 for (i = 1; i < ntabstops; i++) 736 { 737 if (i > 1) 738 strcat(msg, ","); 739 sprintf(msg+strlen(msg), "%d", tabstops[i]); 740 } 741 sprintf(msg+strlen(msg), " and then "); 742 } 743 sprintf(msg+strlen(msg), "every %d spaces", 744 tabdefault); 745 p.p_string = msg; 746 error("%s", &p); 747 break; 748 } 749 } 750 751 752 /* 753 * Handler for the -" option. 754 */ 755 public void opt_quote(int type, constant 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 opt_rscroll(int type, constant char *s) 795 { 796 PARG p; 797 798 switch (type) 799 { 800 case INIT: 801 case TOGGLE: { 802 constant char *fmt; 803 int attr = AT_STANDOUT; 804 setfmt(s, &fmt, &attr, "*s>", FALSE); 805 if (strcmp(fmt, "-") == 0) 806 { 807 rscroll_char = 0; 808 } else 809 { 810 rscroll_attr = attr|AT_COLOR_RSCROLL; 811 if (*fmt == '\0') 812 rscroll_char = '>'; 813 else 814 { 815 LWCHAR ch = step_charc(&fmt, +1, fmt+strlen(fmt)); 816 if (pwidth(ch, rscroll_attr, 0, 0) > 1) 817 error("cannot set rscroll to a wide character", NULL_PARG); 818 else 819 rscroll_char = ch; 820 } 821 } 822 break; } 823 case QUERY: { 824 p.p_string = rscroll_char ? prchar((LWCHAR) rscroll_char) : "-"; 825 error("rscroll character is %s", &p); 826 break; } 827 } 828 } 829 830 /* 831 * "-?" means display a help message. 832 * If from the command line, exit immediately. 833 */ 834 /*ARGSUSED*/ 835 public void opt_query(int type, constant char *s) 836 { 837 switch (type) 838 { 839 case QUERY: 840 case TOGGLE: 841 error("Use \"h\" for help", NULL_PARG); 842 break; 843 case INIT: 844 dohelp = 1; 845 } 846 } 847 848 /*ARGSUSED*/ 849 public void opt_match_shift(int type, constant char *s) 850 { 851 switch (type) 852 { 853 case INIT: 854 case TOGGLE: 855 toggle_fraction(&match_shift, &match_shift_fraction, 856 s, "--match-shift", calc_match_shift); 857 break; 858 case QUERY: 859 query_fraction(match_shift, match_shift_fraction, 860 "Search match shift is %d", "Search match shift is %s of screen width"); 861 break; 862 } 863 } 864 865 public void calc_match_shift(void) 866 { 867 if (match_shift_fraction < 0) 868 return; 869 match_shift = (int) muldiv(sc_width, match_shift_fraction, NUM_FRAC_DENOM); 870 } 871 872 /* 873 * Handler for the --mouse option. 874 */ 875 /*ARGSUSED*/ 876 public void opt_mousecap(int type, constant char *s) 877 { 878 switch (type) 879 { 880 case TOGGLE: 881 if (mousecap == OPT_OFF) 882 deinit_mouse(); 883 else 884 init_mouse(); 885 break; 886 case INIT: 887 case QUERY: 888 break; 889 } 890 } 891 892 /* 893 * Handler for the --wheel-lines option. 894 */ 895 /*ARGSUSED*/ 896 public void opt_wheel_lines(int type, constant char *s) 897 { 898 switch (type) 899 { 900 case INIT: 901 case TOGGLE: 902 if (wheel_lines <= 0) 903 wheel_lines = default_wheel_lines(); 904 break; 905 case QUERY: 906 break; 907 } 908 } 909 910 /* 911 * Handler for the --line-number-width option. 912 */ 913 /*ARGSUSED*/ 914 public void opt_linenum_width(int type, constant char *s) 915 { 916 PARG parg; 917 918 switch (type) 919 { 920 case INIT: 921 case TOGGLE: 922 if (linenum_width > MAX_LINENUM_WIDTH) 923 { 924 parg.p_int = MAX_LINENUM_WIDTH; 925 error("Line number width must not be larger than %d", &parg); 926 linenum_width = MIN_LINENUM_WIDTH; 927 } 928 break; 929 case QUERY: 930 break; 931 } 932 } 933 934 /* 935 * Handler for the --status-column-width option. 936 */ 937 /*ARGSUSED*/ 938 public void opt_status_col_width(int type, constant char *s) 939 { 940 PARG parg; 941 942 switch (type) 943 { 944 case INIT: 945 case TOGGLE: 946 if (status_col_width > MAX_STATUSCOL_WIDTH) 947 { 948 parg.p_int = MAX_STATUSCOL_WIDTH; 949 error("Status column width must not be larger than %d", &parg); 950 status_col_width = 2; 951 } 952 break; 953 case QUERY: 954 break; 955 } 956 } 957 958 /* 959 * Handler for the --file-size option. 960 */ 961 /*ARGSUSED*/ 962 public void opt_filesize(int type, constant char *s) 963 { 964 switch (type) 965 { 966 case INIT: 967 case TOGGLE: 968 if (want_filesize && curr_ifile != NULL && ch_length() == NULL_POSITION) 969 scan_eof(); 970 break; 971 case QUERY: 972 break; 973 } 974 } 975 976 /* 977 * Handler for the --intr option. 978 */ 979 /*ARGSUSED*/ 980 public void opt_intr(int type, constant char *s) 981 { 982 PARG p; 983 984 switch (type) 985 { 986 case INIT: 987 case TOGGLE: 988 intr_char = *s; 989 if (intr_char == '^' && s[1] != '\0') 990 intr_char = CONTROL(s[1]); 991 break; 992 case QUERY: { 993 p.p_string = prchar((LWCHAR) intr_char); 994 error("interrupt character is %s", &p); 995 break; } 996 } 997 } 998 999 /* 1000 * Return the next number from a comma-separated list. 1001 * Return -1 if the list entry is missing or empty. 1002 * Updates *sp to point to the first char of the next number in the list. 1003 */ 1004 public int next_cnum(constant char **sp, constant char *printopt, constant char *errmsg, lbool *errp) 1005 { 1006 int n; 1007 *errp = FALSE; 1008 if (**sp == '\0') /* at end of line */ 1009 return -1; 1010 if (**sp == ',') /* that's the next comma; we have an empty string */ 1011 { 1012 ++(*sp); 1013 return -1; 1014 } 1015 n = getnumc(sp, printopt, errp); 1016 if (*errp) 1017 { 1018 PARG parg; 1019 parg.p_string = errmsg; 1020 error("invalid %s", &parg); 1021 return -1; 1022 } 1023 if (**sp == ',') 1024 ++(*sp); 1025 return n; 1026 } 1027 1028 /* 1029 * Parse a parameter to the --header option. 1030 * Value is "L,C,N", where each field is a decimal number or empty. 1031 */ 1032 static lbool parse_header(constant char *s, int *lines, int *cols, POSITION *start_pos) 1033 { 1034 int n; 1035 lbool err; 1036 1037 if (*s == '-') 1038 s = "0,0"; 1039 1040 n = next_cnum(&s, "header", "number of lines", &err); 1041 if (err) return FALSE; 1042 if (n >= 0) *lines = n; 1043 1044 n = next_cnum(&s, "header", "number of columns", &err); 1045 if (err) return FALSE; 1046 if (n >= 0) *cols = n; 1047 1048 n = next_cnum(&s, "header", "line number", &err); 1049 if (err) return FALSE; 1050 if (n > 0) 1051 { 1052 LINENUM lnum = (LINENUM) n; 1053 if (lnum < 1) lnum = 1; 1054 *start_pos = find_pos(lnum); 1055 } 1056 return TRUE; 1057 } 1058 1059 /* 1060 * Handler for the --header option. 1061 */ 1062 /*ARGSUSED*/ 1063 public void opt_header(int type, constant char *s) 1064 { 1065 switch (type) 1066 { 1067 case INIT: 1068 /* Can't call parse_header now because input file is not yet opened, 1069 * so find_pos won't work. */ 1070 init_header = save(s); 1071 break; 1072 case TOGGLE: { 1073 int lines = header_lines; 1074 int cols = header_cols; 1075 POSITION start_pos = (type == INIT) ? ch_zero() : position(TOP); 1076 if (start_pos == NULL_POSITION) start_pos = ch_zero(); 1077 if (!parse_header(s, &lines, &cols, &start_pos)) 1078 break; 1079 header_lines = lines; 1080 header_cols = cols; 1081 set_header(start_pos); 1082 calc_jump_sline(); 1083 break; } 1084 case QUERY: { 1085 char buf[3*INT_STRLEN_BOUND(long)+3]; 1086 PARG parg; 1087 SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos)); 1088 parg.p_string = buf; 1089 error("Header (lines,columns,line-number) is %s", &parg); 1090 break; } 1091 } 1092 } 1093 1094 /* 1095 * Handler for the --search-options option. 1096 */ 1097 /*ARGSUSED*/ 1098 public void opt_search_type(int type, constant char *s) 1099 { 1100 int st; 1101 PARG parg; 1102 char buf[16]; 1103 char *bp; 1104 int i; 1105 1106 switch (type) 1107 { 1108 case INIT: 1109 case TOGGLE: 1110 st = 0; 1111 for (; *s != '\0'; s++) 1112 { 1113 switch (*s) 1114 { 1115 case 'E': case 'e': case CONTROL('E'): st |= SRCH_PAST_EOF; break; 1116 case 'F': case 'f': case CONTROL('F'): st |= SRCH_FIRST_FILE; break; 1117 case 'K': case 'k': case CONTROL('K'): st |= SRCH_NO_MOVE; break; 1118 case 'N': case 'n': case CONTROL('N'): st |= SRCH_NO_MATCH; break; 1119 case 'R': case 'r': case CONTROL('R'): st |= SRCH_NO_REGEX; break; 1120 case 'W': case 'w': case CONTROL('W'): st |= SRCH_WRAP; break; 1121 case '-': st = 0; break; 1122 case '^': break; 1123 default: 1124 if (*s >= '1' && *s <= '0'+NUM_SEARCH_COLORS) 1125 { 1126 st |= SRCH_SUBSEARCH(*s-'0'); 1127 break; 1128 } 1129 parg.p_char = *s; 1130 error("invalid search option '%c'", &parg); 1131 return; 1132 } 1133 } 1134 def_search_type = norm_search_type(st); 1135 break; 1136 case QUERY: 1137 bp = buf; 1138 if (def_search_type & SRCH_PAST_EOF) *bp++ = 'E'; 1139 if (def_search_type & SRCH_FIRST_FILE) *bp++ = 'F'; 1140 if (def_search_type & SRCH_NO_MOVE) *bp++ = 'K'; 1141 if (def_search_type & SRCH_NO_MATCH) *bp++ = 'N'; 1142 if (def_search_type & SRCH_NO_REGEX) *bp++ = 'R'; 1143 if (def_search_type & SRCH_WRAP) *bp++ = 'W'; 1144 for (i = 1; i <= NUM_SEARCH_COLORS; i++) 1145 if (def_search_type & SRCH_SUBSEARCH(i)) 1146 *bp++ = (char) ('0'+i); 1147 if (bp == buf) 1148 *bp++ = '-'; 1149 *bp = '\0'; 1150 parg.p_string = buf; 1151 error("search options: %s", &parg); 1152 break; 1153 } 1154 } 1155 1156 /* 1157 * Handler for the --no-search-headers, --no-search-header-lines 1158 * and --no-search-header-cols options. 1159 */ 1160 static void do_nosearch_headers(int type, int no_header_lines, int no_header_cols) 1161 { 1162 switch (type) 1163 { 1164 case INIT: 1165 case TOGGLE: 1166 nosearch_header_lines = no_header_lines; 1167 nosearch_header_cols = no_header_cols; 1168 if (type != TOGGLE) break; 1169 /*FALLTHRU*/ 1170 case QUERY: 1171 if (nosearch_header_lines && nosearch_header_cols) 1172 error("Search does not include header lines or columns", NULL_PARG); 1173 else if (nosearch_header_lines) 1174 error("Search includes header columns but not header lines", NULL_PARG); 1175 else if (nosearch_header_cols) 1176 error("Search includes header lines but not header columns", NULL_PARG); 1177 else 1178 error("Search includes header lines and columns", NULL_PARG); 1179 } 1180 } 1181 1182 /*ARGSUSED*/ 1183 public void opt_nosearch_headers(int type, constant char *s) 1184 { 1185 do_nosearch_headers(type, 1, 1); 1186 } 1187 1188 /*ARGSUSED*/ 1189 public void opt_nosearch_header_lines(int type, constant char *s) 1190 { 1191 do_nosearch_headers(type, 1, 0); 1192 } 1193 1194 /*ARGSUSED*/ 1195 public void opt_nosearch_header_cols(int type, constant char *s) 1196 { 1197 do_nosearch_headers(type, 0, 1); 1198 } 1199 1200 /*ARGSUSED*/ 1201 public void opt_no_paste(int type, constant char *s) 1202 { 1203 switch (type) 1204 { 1205 case TOGGLE: 1206 if (no_paste) 1207 init_bracketed_paste(); 1208 else 1209 deinit_bracketed_paste(); 1210 break; 1211 case INIT: 1212 case QUERY: 1213 break; 1214 } 1215 } 1216 1217 #if LESSTEST 1218 /* 1219 * Handler for the --tty option. 1220 */ 1221 /*ARGSUSED*/ 1222 public void opt_ttyin_name(int type, constant char *s) 1223 { 1224 switch (type) 1225 { 1226 case INIT: 1227 ttyin_name = s; 1228 is_tty = 1; 1229 break; 1230 } 1231 } 1232 #endif /*LESSTEST*/ 1233 1234 public int chop_line(void) 1235 { 1236 return (chopline || header_cols > 0 || header_lines > 0); 1237 } 1238 1239 /* 1240 * Get the "screen window" size. 1241 */ 1242 public int get_swindow(void) 1243 { 1244 if (swindow > 0) 1245 return (swindow); 1246 return (sc_height - header_lines + swindow); 1247 } 1248 1249