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