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