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