1 /* 2 * Copyright (C) 1984-2026 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 lbool dohelp; 38 extern char openquote; 39 extern char closequote; 40 extern char *prproto[]; 41 extern char *eprproto[]; 42 extern char *eqproto; 43 extern char *hproto; 44 extern char *wproto; 45 extern char *every_first_cmd; 46 extern IFILE curr_ifile; 47 extern char version[]; 48 extern int jump_sline_arg; 49 extern int jump_sline; 50 extern long jump_sline_fraction; 51 extern int shift_count; 52 extern long shift_count_fraction; 53 extern int match_shift; 54 extern long match_shift_fraction; 55 extern LWCHAR rscroll_char; 56 extern int rscroll_attr; 57 extern int emouse; 58 extern int mouse_reverse; 59 extern int xmouse; 60 extern int wheel_lines; 61 extern lbool less_is_more; 62 extern int linenum_width; 63 extern int status_col_width; 64 extern int use_color; 65 extern int want_filesize; 66 extern int header_lines; 67 extern int header_cols; 68 extern int def_search_type; 69 extern int chopline; 70 extern int tabstops[]; 71 extern int ntabstops; 72 extern int tabdefault; 73 extern int no_paste; 74 extern int hilite_target; 75 extern char intr_char; 76 extern int utf_mode; 77 extern int nosearch_header_lines; 78 extern int nosearch_header_cols; 79 extern POSITION header_start_pos; 80 extern char *init_header; 81 extern char *first_cmd_at_prompt; 82 extern char *autosave; 83 #if LOGFILE 84 extern char *namelogfile; 85 extern lbool force_logfile; 86 extern int logfile; 87 #endif 88 #if TAGS 89 public char *tagoption = NULL; 90 extern char *tags; 91 extern char ztags[]; 92 #endif 93 #if LESSTEST 94 extern constant char *ttyin_name; 95 extern lbool is_tty; 96 #endif /*LESSTEST*/ 97 #if MSDOS_COMPILER 98 extern int nm_fg_color, nm_bg_color, nm_attr; 99 extern int bo_fg_color, bo_bg_color, bo_attr; 100 extern int ul_fg_color, ul_bg_color, ul_attr; 101 extern int so_fg_color, so_bg_color, so_attr; 102 extern int bl_fg_color, bl_bg_color, bl_attr; 103 extern int sgr_mode; 104 #if MSDOS_COMPILER==WIN32C 105 #ifndef COMMON_LVB_UNDERSCORE 106 #define COMMON_LVB_UNDERSCORE 0x8000 107 #endif 108 #ifndef COMMON_LVB_REVERSE_VIDEO 109 #define COMMON_LVB_REVERSE_VIDEO 0x4000 110 #endif 111 #endif 112 #endif 113 114 115 #if LOGFILE 116 /* 117 * Handler for -o option. 118 */ 119 public void opt_o(int type, constant char *s) 120 { 121 PARG parg; 122 char *filename; 123 124 if (!secure_allow(SF_LOGFILE)) 125 { 126 error(LM(log_file_support_is_not_available), NULL_PARG); 127 return; 128 } 129 switch (type) 130 { 131 case INIT: 132 namelogfile = save(s); 133 break; 134 case TOGGLE: 135 if (ch_getflags() & CH_CANSEEK) 136 { 137 error(LM(Input_is_not_a_pipe), NULL_PARG); 138 return; 139 } 140 if (logfile >= 0) 141 { 142 error(LM(Log_file_is_already_in_use), NULL_PARG); 143 return; 144 } 145 s = skipspc(s); 146 if (namelogfile != NULL) 147 free(namelogfile); 148 filename = lglob(s); 149 namelogfile = shell_unquote(filename); 150 free(filename); 151 use_logfile(namelogfile); 152 sync_logfile(); 153 break; 154 case QUERY: 155 if (logfile < 0) 156 error(LM(No_log_file), NULL_PARG); 157 else 158 { 159 parg.p_string = namelogfile; 160 error(LM(Log_file_X), &parg); 161 } 162 break; 163 } 164 } 165 166 /* 167 * Handler for -O option. 168 */ 169 public void opt__O(int type, constant char *s) 170 { 171 force_logfile = TRUE; 172 opt_o(type, s); 173 } 174 #endif 175 176 static void toggle_fraction(int *num, long *frac, constant char *s, constant char *printopt, lbool neg_ok, void (*calc)(void)) 177 { 178 if (s == NULL) 179 { 180 } else if (*s == '.') 181 { 182 s++; 183 if (!getfraction(&s, frac)) 184 { 185 PARG parg; 186 parg.p_string = printopt; 187 error(LM(Invalid_fraction_in_X), &parg); 188 return; 189 } 190 } else 191 { 192 if (!getnumc(&s, printopt, neg_ok, num)) 193 return; 194 *frac = -1; 195 } 196 (*calc)(); 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_arg, &jump_sline_fraction, 231 s, "-j", TRUE, calc_jump_sline); 232 break; 233 case QUERY: 234 query_fraction(jump_sline_arg, jump_sline_fraction, 235 LM(Position_target_at_screen_line_X), LM(Position_target_at_screen_position_X)); 236 break; 237 } 238 } 239 240 public void calc_jump_sline(void) 241 { 242 jump_sline = jump_sline_arg; 243 /* If jump_sline_fraction is set, calculate jump_sline from it. */ 244 if (jump_sline_fraction >= 0) 245 jump_sline = (int) muldiv(sc_height, jump_sline_fraction, NUM_FRAC_DENOM); 246 /* Negative jump_sline means relative to bottom of screen. */ 247 if (jump_sline < 0) 248 jump_sline += sc_height; 249 /* If jump_sline is obscured by header, move it after the header. */ 250 if (jump_sline <= header_lines) 251 jump_sline = header_lines + 1; 252 /* If jump_sline is past bottom of screen, move it to the bottom. */ 253 if (jump_sline >= sc_height) 254 jump_sline = sc_height - 1; 255 } 256 257 /* 258 * Handlers for -# option. 259 */ 260 public void opt_shift(int type, constant char *s) 261 { 262 switch (type) 263 { 264 case INIT: 265 case TOGGLE: 266 toggle_fraction(&shift_count, &shift_count_fraction, 267 s, "-#", FALSE, calc_shift_count); 268 break; 269 case QUERY: 270 query_fraction(shift_count, shift_count_fraction, 271 LM(Horizontal_shift_X_columns), LM(Horizontal_shift_X_of_screen_width)); 272 break; 273 } 274 } 275 276 public void calc_shift_count(void) 277 { 278 if (shift_count_fraction < 0) 279 return; 280 shift_count = (int) muldiv(sc_width, shift_count_fraction, NUM_FRAC_DENOM); 281 } 282 283 #if USERFILE 284 public void opt_k(int type, constant char *s) 285 { 286 PARG parg; 287 288 switch (type) 289 { 290 case INIT: 291 if (lesskey(s, FALSE)) 292 { 293 parg.p_string = s; 294 error(LM(Cannot_use_lesskey_file_X), &parg); 295 } 296 break; 297 } 298 } 299 300 #if HAVE_LESSKEYSRC 301 public void opt_ks(int type, constant char *s) 302 { 303 PARG parg; 304 305 switch (type) 306 { 307 case INIT: 308 if (lesskey_src(s, FALSE)) 309 { 310 parg.p_string = s; 311 error(LM(Cannot_use_lesskey_source_file_X), &parg); 312 } 313 break; 314 } 315 } 316 317 public void opt_kc(int type, constant char *s) 318 { 319 switch (type) 320 { 321 case INIT: 322 if (lesskey_content(s, FALSE)) 323 { 324 error(LM(Error_in_lesskey_content), NULL_PARG); 325 } 326 break; 327 } 328 } 329 330 #endif /* HAVE_LESSKEYSRC */ 331 #endif /* USERFILE */ 332 333 /* 334 * Handler for -S option. 335 */ 336 public void opt__S(int type, constant char *s) 337 { 338 (void)s; 339 switch (type) 340 { 341 case TOGGLE: 342 pos_rehead(TRUE); 343 break; 344 } 345 } 346 347 #if TAGS 348 /* 349 * Handler for -t option. 350 */ 351 public void opt_t(int type, constant char *s) 352 { 353 IFILE save_ifile; 354 POSITION pos; 355 356 switch (type) 357 { 358 case INIT: 359 tagoption = save(s); 360 /* Do the rest in main() */ 361 break; 362 case TOGGLE: 363 if (!secure_allow(SF_TAGS)) 364 { 365 error(LM(tags_support_is_not_available), NULL_PARG); 366 break; 367 } 368 findtag(skipspc(s)); 369 save_ifile = save_curr_ifile(); 370 /* 371 * Try to open the file containing the tag 372 * and search for the tag in that file. 373 */ 374 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION) 375 { 376 /* Failed: reopen the old file. */ 377 reedit_ifile(save_ifile); 378 break; 379 } 380 unsave_ifile(save_ifile); 381 jump_loc(pos, jump_sline); 382 break; 383 } 384 } 385 386 /* 387 * Handler for -T option. 388 */ 389 public void opt__T(int type, constant char *s) 390 { 391 PARG parg; 392 char *filename; 393 394 switch (type) 395 { 396 case INIT: 397 tags = save(s); 398 break; 399 case TOGGLE: 400 s = skipspc(s); 401 if (tags != NULL && tags != ztags) 402 free(tags); 403 filename = lglob(s); 404 tags = shell_unquote(filename); 405 free(filename); 406 break; 407 case QUERY: 408 parg.p_string = tags; 409 error(LM(Tags_file_X), &parg); 410 break; 411 } 412 } 413 #endif 414 415 /* 416 * Handler for -p option. 417 */ 418 public void opt_p(int type, constant char *s) 419 { 420 switch (type) 421 { 422 case INIT: 423 /* 424 * Unget a command for the specified string. 425 */ 426 if (less_is_more) 427 { 428 /* 429 * In "more" mode, the -p argument is a command, 430 * not a search string, so we don't need a slash. 431 */ 432 every_first_cmd = save(s); 433 } else 434 { 435 plusoption = TRUE; 436 /* 437 * {{ This won't work if the "/" command is 438 * changed or invalidated by a .lesskey file. }} 439 */ 440 ungetsc("/"); 441 ungetsc(s); 442 ungetcc_end_command(); 443 } 444 break; 445 } 446 } 447 448 /* 449 * Handler for -P option. 450 */ 451 public void opt__P(int type, constant char *s) 452 { 453 char **proto; 454 PARG parg; 455 456 switch (type) 457 { 458 case INIT: 459 case TOGGLE: 460 /* 461 * Figure out which prototype string should be changed. 462 */ 463 switch (*s) 464 { 465 case 's': proto = &prproto[PR_SHORT]; s++; break; 466 case 'm': proto = &prproto[PR_MEDIUM]; s++; break; 467 case 'M': proto = &prproto[PR_LONG]; s++; break; 468 case '=': proto = &eqproto; s++; break; 469 case 'h': proto = &hproto; s++; break; 470 case 'w': proto = &wproto; s++; break; 471 default: proto = &prproto[PR_SHORT]; break; 472 } 473 free(*proto); 474 *proto = save(s); 475 break; 476 case QUERY: 477 parg.p_string = prproto[pr_type]; 478 switch (pr_type) 479 { 480 case PR_MEDIUM: error(LM(Prompt_medium_X), &parg); break; 481 case PR_LONG: error(LM(Prompt_long_X), &parg); break; 482 default: error(LM(Prompt_short_X), &parg); break; 483 } 484 break; 485 } 486 } 487 488 /* 489 * Handler for --end-prompt option. 490 */ 491 public void opt_end_prompt(int type, constant char *s) 492 { 493 char **pend; 494 PARG parg; 495 496 switch (type) 497 { 498 case INIT: 499 case TOGGLE: 500 switch (*s) 501 { 502 case 's': pend = &eprproto[PR_SHORT]; s++; break; 503 case 'm': pend = &eprproto[PR_MEDIUM]; s++; break; 504 case 'M': pend = &eprproto[PR_LONG]; s++; break; 505 default: pend = &eprproto[PR_SHORT]; break; 506 } 507 if (*pend != NULL) 508 free(*pend); 509 *pend = (strcmp(s, "-") == 0) ? NULL : save(s); 510 break; 511 case QUERY: 512 parg.p_string = eprproto[pr_type]; 513 if (parg.p_string == NULL) 514 parg.p_string = LM(nothing); 515 switch (pr_type) 516 { 517 case PR_MEDIUM: error(LM(Print_after_medium_prompt_X), &parg); break; 518 case PR_LONG: error(LM(Print_after_long_prompt_X), &parg); break; 519 default: error(LM(Print_after_short_prompt_X), &parg); break; 520 } 521 break; 522 } 523 } 524 525 /* 526 * Handler for --autosave option. 527 */ 528 public void opt_autosave(int type, constant char *s) 529 { 530 PARG parg; 531 532 switch (type) 533 { 534 case INIT: 535 case TOGGLE: 536 if (s == NULL) 537 s = "-"; 538 if (autosave != NULL) 539 free(autosave); 540 autosave = save(s); 541 break; 542 case QUERY: 543 parg.p_string = (autosave != NULL) ? autosave : "-"; 544 error(LM(Autosave_actions_X), &parg); 545 break; 546 } 547 } 548 549 /* 550 * Handler for the -b option. 551 */ 552 /*ARGSUSED*/ 553 public void opt_b(int type, constant char *s) 554 { 555 (void)s; 556 switch (type) 557 { 558 case INIT: 559 case TOGGLE: 560 /* 561 * Set the new number of buffers. 562 */ 563 ch_setbufspace((ssize_t) bufspace); 564 break; 565 case QUERY: 566 break; 567 } 568 } 569 570 /* 571 * Handler for the -i option. 572 */ 573 /*ARGSUSED*/ 574 public void opt_i(int type, constant char *s) 575 { 576 (void)s; 577 switch (type) 578 { 579 case TOGGLE: 580 chg_caseless(); 581 break; 582 case QUERY: 583 case INIT: 584 break; 585 } 586 } 587 588 /* 589 * Handler for the -V option. 590 */ 591 /*ARGSUSED*/ 592 public void opt__V(int type, constant char *s) 593 { 594 (void)s; 595 switch (type) 596 { 597 case TOGGLE: 598 case QUERY: 599 dispversion(); 600 break; 601 case INIT: 602 set_output(1, TRUE); /* Force output to stdout per GNU standard for --version output. */ 603 putstr("less "); 604 putstr(version); 605 putstr(" ("); 606 putstr(pattern_lib_name()); 607 putstr(" regular expressions)\n"); 608 { 609 char constant *copyright = 610 "Copyright (C) 1984-2026 Mark Nudelman\n\n"; 611 putstr(copyright); 612 } 613 if (version[strlen(version)-1] == 'x') 614 { 615 putstr("** This is an EXPERIMENTAL build of the 'less' software,\n"); 616 putstr("** and may not function correctly.\n"); 617 putstr("** Obtain release builds from the web page below.\n\n"); 618 } 619 #if LESSTEST 620 putstr("This build supports LESSTEST.\n"); 621 #endif /*LESSTEST*/ 622 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n"); 623 putstr("For information about the terms of redistribution,\n"); 624 putstr("see the file named README in the less distribution.\n"); 625 putstr("Home page: https://greenwoodsoftware.com/less\n"); 626 quit(QUIT_OK); 627 break; 628 } 629 } 630 631 #if MSDOS_COMPILER 632 /* 633 * Parse an MSDOS color descriptor. 634 */ 635 static void colordesc(constant char *s, int *fg_color, int *bg_color, int *dattr) 636 { 637 int fg, bg; 638 CHAR_ATTR attr; 639 if (parse_color(s, &fg, &bg, &attr) == CT_NULL) 640 { 641 PARG p; 642 p.p_string = s; 643 error(LM(Invalid_color_string_X), &p); 644 } else 645 { 646 *fg_color = fg; 647 *bg_color = bg; 648 *dattr = 0; 649 #if MSDOS_COMPILER==WIN32C 650 if (attr & CATTR_UNDERLINE) 651 *dattr |= COMMON_LVB_UNDERSCORE; 652 if (attr & CATTR_STANDOUT) 653 *dattr |= COMMON_LVB_REVERSE_VIDEO; 654 #endif 655 } 656 } 657 #endif 658 659 static int color_from_namechar(char namechar) 660 { 661 switch (namechar) 662 { 663 case 'B': return AT_COLOR_BIN; 664 case 'C': return AT_COLOR_CTRL; 665 case 'E': return AT_COLOR_ERROR; 666 case 'H': return AT_COLOR_HEADER; 667 case 'J': return AT_COLOR_TARGET; 668 case 'M': return AT_COLOR_MARK; 669 case 'N': return AT_COLOR_LINENUM; 670 case 'O': return AT_COLOR_OSC8; 671 case 'P': return AT_COLOR_PROMPT; 672 case 'R': return AT_COLOR_RSCROLL; 673 case 'S': return AT_COLOR_SEARCH; 674 case 'T': return AT_COLOR_TILDE; 675 case 'W': case 'A': return AT_COLOR_ATTN; 676 case 'n': return AT_NORMAL; 677 case 's': return AT_STANDOUT; 678 case 'd': return AT_BOLD; 679 case 'u': return AT_UNDERLINE; 680 case 'k': return AT_BLINK; 681 default: 682 if (namechar >= '1' && namechar <= '0'+NUM_SEARCH_COLORS) 683 return AT_COLOR_SUBSEARCH(namechar-'0'); 684 return -1; 685 } 686 } 687 688 /* 689 * Handler for the -D option. 690 */ 691 /*ARGSUSED*/ 692 public void opt_D(int type, constant char *s) 693 { 694 PARG p; 695 int attr; 696 697 switch (type) 698 { 699 case INIT: 700 case TOGGLE: 701 #if MSDOS_COMPILER 702 if (*s == 'a') 703 { 704 sgr_mode = !sgr_mode; 705 if (type == TOGGLE) 706 { 707 p.p_string = (sgr_mode) ? LM(on) : LM(off); 708 error(LM(SGR_mode_is_X), &p); 709 } 710 break; 711 } 712 #endif 713 attr = color_from_namechar(s[0]); 714 if (attr < 0) 715 { 716 p.p_char = s[0]; 717 error(LM(Invalid_color_specifier_X), &p); 718 return; 719 } 720 if (!use_color && (attr & AT_COLOR)) 721 { 722 error(LM(Set__use_color_before_changing_colors), NULL_PARG); 723 return; 724 } 725 s++; 726 #if MSDOS_COMPILER 727 if (!(attr & AT_COLOR)) 728 { 729 switch (attr) 730 { 731 case AT_NORMAL: 732 colordesc(s, &nm_fg_color, &nm_bg_color, &nm_attr); 733 break; 734 case AT_BOLD: 735 colordesc(s, &bo_fg_color, &bo_bg_color, &bo_attr); 736 break; 737 case AT_UNDERLINE: 738 colordesc(s, &ul_fg_color, &ul_bg_color, &ul_attr); 739 break; 740 case AT_BLINK: 741 colordesc(s, &bl_fg_color, &bl_bg_color, &bl_attr); 742 break; 743 case AT_STANDOUT: 744 colordesc(s, &so_fg_color, &so_bg_color, &so_attr); 745 break; 746 } 747 if (type == TOGGLE) 748 { 749 init_win_colors(); 750 at_enter(AT_STANDOUT); 751 at_exit(); 752 } 753 } else 754 #endif 755 if (set_color_map(attr, s) < 0) 756 { 757 p.p_string = s; 758 error(LM(Invalid_color_string_X), &p); 759 return; 760 } 761 break; 762 } 763 } 764 765 /* 766 */ 767 public void set_tabs(constant char *s, size_t len) 768 { 769 int i; 770 constant char *es = s + len; 771 /* Start at 1 because tabstops[0] is always zero. */ 772 for (i = 1; i < TABSTOP_MAX; ) 773 { 774 int n = 0; 775 lbool v = FALSE; 776 while (s < es && *s == ' ') 777 s++; 778 for (; s < es && *s >= '0' && *s <= '9'; s++) 779 { 780 v = v || ckd_mul(&n, n, 10); 781 v = v || ckd_add(&n, n, *s - '0'); 782 } 783 if (!v && n > tabstops[i-1]) 784 tabstops[i++] = n; 785 while (s < es && *s == ' ') 786 s++; 787 if (s == es || *s++ != ',') 788 break; 789 } 790 if (i < 2) 791 return; 792 ntabstops = i; 793 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2]; 794 } 795 796 /* 797 * Handler for the -x option. 798 */ 799 public void opt_x(int type, constant char *s) 800 { 801 size_t msglen; 802 char *msg; 803 int i; 804 PARG p; 805 806 switch (type) 807 { 808 case INIT: 809 case TOGGLE: 810 set_tabs(s, strlen(s)); 811 break; 812 case QUERY: 813 msglen = strlen(LM(Tab_stops)) + strlen(LM(and_then)) + strlen(LM(every_X_spaces)) + ((INT_STRLEN_BOUND(int)+1)*ntabstops) + 2; 814 msg = ecalloc(msglen, sizeof(char)); 815 strcpy(msg, LM(Tab_stops)); 816 if (ntabstops > 2) 817 { 818 for (i = 1; i < ntabstops; i++) 819 { 820 if (i > 1) 821 strcat(msg, ","); 822 sprintf(msg+strlen(msg), "%d", tabstops[i]); 823 } 824 strcat(msg, " "); 825 strcat(msg, LM(and_then)); 826 } 827 sprintf(msg+strlen(msg), LM(every_X_spaces), tabdefault); 828 p.p_string = msg; 829 error("%s", &p); 830 free(msg); 831 break; 832 } 833 } 834 835 836 /* 837 * Handler for the -" option. 838 */ 839 public void opt_quote(int type, constant char *s) 840 { 841 char buf[3]; 842 PARG parg; 843 844 switch (type) 845 { 846 case INIT: 847 case TOGGLE: 848 if (s[0] == '\0') 849 { 850 openquote = closequote = '\0'; 851 break; 852 } 853 if (s[1] != '\0' && s[2] != '\0') 854 { 855 error(LM(dq_must_be_followed_by_1_or_2_chars), NULL_PARG); 856 return; 857 } 858 openquote = s[0]; 859 if (s[1] == '\0') 860 closequote = openquote; 861 else 862 closequote = s[1]; 863 break; 864 case QUERY: 865 buf[0] = openquote; 866 buf[1] = closequote; 867 buf[2] = '\0'; 868 parg.p_string = buf; 869 error(LM(quotes_X), &parg); 870 break; 871 } 872 } 873 874 /* 875 * Handler for the --rscroll option. 876 */ 877 /*ARGSUSED*/ 878 public void opt_rscroll(int type, constant char *s) 879 { 880 PARG p; 881 882 switch (type) 883 { 884 case INIT: 885 case TOGGLE: { 886 constant char *fmt; 887 int attr = AT_STANDOUT; 888 setfmt(s, &fmt, &attr, "*s>", FALSE); 889 if (strcmp(fmt, "-") == 0) 890 { 891 rscroll_char = 0; 892 } else 893 { 894 rscroll_attr = attr|AT_COLOR_RSCROLL; 895 if (*fmt == '\0') 896 rscroll_char = '>'; 897 else 898 { 899 LWCHAR ch = step_charc(&fmt, +1, fmt+strlen(fmt)); 900 if (pwidth(ch, rscroll_attr, 0, 0) > 1) 901 error(LM(cannot_set_rscroll_to_a_wide_character), NULL_PARG); 902 else 903 rscroll_char = ch; 904 } 905 } 906 break; } 907 case QUERY: { 908 p.p_string = rscroll_char == 0 ? "-" : utf_mode ? prutfchar(rscroll_char) : prchar(rscroll_char); 909 error(LM(rscroll_character_is_X), &p); 910 break; } 911 } 912 } 913 914 /* 915 * "-?" means display a help message. 916 * If from the command line, exit immediately. 917 */ 918 /*ARGSUSED*/ 919 public void opt_query(int type, constant char *s) 920 { 921 (void)s; 922 switch (type) 923 { 924 case QUERY: 925 case TOGGLE: 926 error(LM(Use_h_for_help), NULL_PARG); 927 break; 928 case INIT: 929 dohelp = TRUE; 930 } 931 } 932 933 /*ARGSUSED*/ 934 public void opt_match_shift(int type, constant char *s) 935 { 936 switch (type) 937 { 938 case INIT: 939 case TOGGLE: 940 toggle_fraction(&match_shift, &match_shift_fraction, 941 s, "--match-shift", FALSE, calc_match_shift); 942 break; 943 case QUERY: 944 query_fraction(match_shift, match_shift_fraction, 945 LM(Search_match_shift_is_X), LM(Search_match_shift_is_X_of_screen_width)); 946 break; 947 } 948 } 949 950 public void calc_match_shift(void) 951 { 952 if (match_shift_fraction < 0) 953 return; 954 match_shift = (int) muldiv(sc_width, match_shift_fraction, NUM_FRAC_DENOM); 955 } 956 957 /* 958 * Handler for the --emouse option. 959 */ 960 /*ARGSUSED*/ 961 public void opt_emouse(int type, constant char *s) 962 { 963 /* Order of entries matters for QUERY. 964 * Combinations must come after their components. */ 965 static struct csl_bitmap_def emouse_defs[] = { 966 { "hscroll", EMOUSE_HSCROLL }, 967 { "vscroll", EMOUSE_VSCROLL }, 968 { "hdrag", EMOUSE_HDRAG }, 969 { "vdrag", EMOUSE_VDRAG }, 970 { "lclick", EMOUSE_LCLICK }, 971 { "rclick", EMOUSE_RCLICK }, 972 { "scroll", EMOUSE_HSCROLL|EMOUSE_VSCROLL }, 973 { "drag", EMOUSE_HDRAG|EMOUSE_VDRAG }, 974 { "hmove", EMOUSE_HSCROLL|EMOUSE_HDRAG }, 975 { "vmove", EMOUSE_VSCROLL|EMOUSE_VDRAG }, 976 { "move", EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_HSCROLL|EMOUSE_HDRAG }, 977 { "click", EMOUSE_LCLICK|EMOUSE_RCLICK }, 978 { "all", EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_HSCROLL|EMOUSE_HDRAG|EMOUSE_LCLICK|EMOUSE_RCLICK }, 979 }; 980 981 switch (type) 982 { 983 case INIT: 984 case TOGGLE: { 985 lbool was_mouse_enabled = (emouse != 0); 986 if (s == NULL || strcmp(s, "-") == 0) 987 emouse = 0; 988 else 989 emouse = parse_csl_bitmap(s, 990 emouse_defs, countof(emouse_defs), "--emouse"); 991 if (type == TOGGLE && was_mouse_enabled != (emouse != 0)) 992 { 993 if (emouse == 0) 994 deinit_mouse(); 995 else 996 init_mouse(); 997 } 998 break; } 999 case QUERY: { 1000 char buf[128]; 1001 char *bp = buf; 1002 char *ebuf = buf + sizeof(buf); 1003 int qmouse = emouse; 1004 PARG parg; 1005 int i; 1006 1007 for (i = countof(emouse_defs)-1; i >= 0; i--) 1008 { 1009 int bit = emouse_defs[i].bit_value; 1010 if ((qmouse & bit) == bit) 1011 { 1012 if (bp > buf && bp+1 < ebuf) 1013 *bp++ = ','; 1014 strncpy(bp, emouse_defs[i].bit_name, ptr_diff(ebuf, bp)); 1015 bp += strlen(bp); 1016 qmouse &= ~bit; 1017 } 1018 } 1019 *bp = '\0'; 1020 parg.p_string = buf; 1021 if (buf[0] == '\0') 1022 error(LM(Ignore_mouse_input), NULL_PARG); 1023 else 1024 error(LM(Mouse_features_enabled_X), &parg); 1025 break; } 1026 } 1027 } 1028 1029 /* 1030 * Handler for the --mouse option. 1031 */ 1032 /*ARGSUSED*/ 1033 public void opt_mouse(int type, constant char *s) 1034 { 1035 (void)s; 1036 switch (type) 1037 { 1038 case INIT: 1039 case TOGGLE: 1040 if (emouse == 0) 1041 { 1042 opt_emouse(type, "vmove,click"); 1043 mouse_reverse = (xmouse == OPT_ONPLUS); 1044 } else 1045 { 1046 opt_emouse(type, "-"); 1047 xmouse = 0; 1048 } 1049 if (type == INIT) 1050 break; 1051 /*FALLTHRU*/ 1052 case QUERY: /* odesc[] entries are NULL so we can do the QUERY here */ 1053 if (emouse == (EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_LCLICK|EMOUSE_RCLICK)) 1054 { 1055 if (mouse_reverse) 1056 error(LM(Use_the_mouse_for_scrolling_vertically_reverse), NULL_PARG); 1057 else 1058 error(LM(Use_the_mouse_for_scrolling_vertically), NULL_PARG); 1059 } else 1060 opt_emouse(QUERY, NULL); 1061 break; 1062 } 1063 } 1064 1065 /* 1066 * Handler for the --wheel-lines option. 1067 */ 1068 /*ARGSUSED*/ 1069 public void opt_wheel_lines(int type, constant char *s) 1070 { 1071 (void)s; 1072 switch (type) 1073 { 1074 case INIT: 1075 case TOGGLE: 1076 if (wheel_lines <= 0) 1077 wheel_lines = default_wheel_lines(); 1078 break; 1079 case QUERY: 1080 break; 1081 } 1082 } 1083 1084 /* 1085 * Handler for the --line-number-width option. 1086 */ 1087 /*ARGSUSED*/ 1088 public void opt_linenum_width(int type, constant char *s) 1089 { 1090 PARG parg; 1091 1092 (void)s; 1093 switch (type) 1094 { 1095 case INIT: 1096 case TOGGLE: 1097 if (linenum_width > MAX_LINENUM_WIDTH) 1098 { 1099 parg.p_int = MAX_LINENUM_WIDTH; 1100 error(LM(Line_number_width_must_not_be_larger_than_X), &parg); 1101 linenum_width = MIN_LINENUM_WIDTH; 1102 } 1103 break; 1104 case QUERY: 1105 break; 1106 } 1107 } 1108 1109 /* 1110 * Handler for the --status-column-width option. 1111 */ 1112 /*ARGSUSED*/ 1113 public void opt_status_col_width(int type, constant char *s) 1114 { 1115 PARG parg; 1116 1117 (void)s; 1118 switch (type) 1119 { 1120 case INIT: 1121 case TOGGLE: 1122 if (status_col_width > MAX_STATUSCOL_WIDTH) 1123 { 1124 parg.p_int = MAX_STATUSCOL_WIDTH; 1125 error(LM(Status_column_width_must_not_be_larger_than_X), &parg); 1126 status_col_width = 2; 1127 } 1128 break; 1129 case QUERY: 1130 break; 1131 } 1132 } 1133 1134 /* 1135 * Handler for the --file-size option. 1136 */ 1137 /*ARGSUSED*/ 1138 public void opt_filesize(int type, constant char *s) 1139 { 1140 (void)s; 1141 switch (type) 1142 { 1143 case INIT: 1144 case TOGGLE: 1145 if (want_filesize && curr_ifile != NULL && ch_length() == NULL_POSITION) 1146 scan_eof(); 1147 break; 1148 case QUERY: 1149 break; 1150 } 1151 } 1152 1153 /* 1154 * Handler for the --cmd option. 1155 */ 1156 /*ARGSUSED*/ 1157 public void opt_first_cmd_at_prompt(int type, constant char *s) 1158 { 1159 switch (type) 1160 { 1161 case INIT: 1162 case TOGGLE: 1163 first_cmd_at_prompt = save(s); 1164 break; 1165 case QUERY: 1166 break; 1167 } 1168 } 1169 1170 /* 1171 * Handler for the --intr option. 1172 */ 1173 /*ARGSUSED*/ 1174 public void opt_intr(int type, constant char *s) 1175 { 1176 PARG p; 1177 1178 switch (type) 1179 { 1180 case INIT: 1181 case TOGGLE: 1182 intr_char = *s; 1183 if (intr_char == '^' && s[1] != '\0') 1184 intr_char = CONTROL(s[1]); 1185 break; 1186 case QUERY: { 1187 p.p_string = prchar((LWCHAR) intr_char); 1188 error(LM(interrupt_character_is_X), &p); 1189 break; } 1190 } 1191 } 1192 1193 /* 1194 * Return the next number from a comma-separated list. 1195 * Return -1 if the list entry is missing or empty. 1196 * Updates *sp to point to the first char of the next number in the list. 1197 */ 1198 static lbool next_cnum(constant char **sp, constant char *printopt, mutable int *p_num) 1199 { 1200 if (**sp == '\0') /* at end of line */ 1201 { 1202 *p_num = -1; 1203 return TRUE; 1204 } 1205 if (**sp == ',') /* that's the next comma; we have an empty string */ 1206 { 1207 ++(*sp); 1208 *p_num = -1; 1209 return TRUE; 1210 } 1211 if (!getnumc(sp, printopt, FALSE, p_num)) 1212 return FALSE; 1213 if (**sp == ',') 1214 ++(*sp); 1215 return TRUE; 1216 } 1217 1218 /* 1219 * Parse a parameter to the --header option. 1220 * Value is "L,C,N", where each field is a decimal number or empty. 1221 */ 1222 static lbool parse_header(constant char *s, int *lines, int *cols, POSITION *start_pos) 1223 { 1224 int n; 1225 1226 if (*s == '-') 1227 s = "0,0"; 1228 if (!next_cnum(&s, "--header", &n)) 1229 return FALSE; 1230 if (n >= 0) *lines = n; 1231 if (!next_cnum(&s, "--header", &n)) 1232 return FALSE; 1233 if (n >= 0) *cols = n; 1234 if (!next_cnum(&s, "--header", &n)) 1235 return FALSE; 1236 if (n > 0) 1237 { 1238 if (n < 1) n = 1; 1239 *start_pos = find_pos((LINENUM)n); 1240 } 1241 return TRUE; 1242 } 1243 1244 /* 1245 * Handler for the --header option. 1246 */ 1247 /*ARGSUSED*/ 1248 public void opt_header(int type, constant char *s) 1249 { 1250 switch (type) 1251 { 1252 case INIT: 1253 /* Can't call parse_header now because input file is not yet opened, 1254 * so find_pos won't work. */ 1255 init_header = save(s); 1256 break; 1257 case TOGGLE: { 1258 int lines = header_lines; 1259 int cols = header_cols; 1260 POSITION start_pos = (type == INIT) ? ch_zero() : position(TOP); 1261 if (start_pos == NULL_POSITION) start_pos = ch_zero(); 1262 if (!parse_header(s, &lines, &cols, &start_pos)) 1263 break; 1264 header_lines = lines; 1265 header_cols = cols; 1266 set_header(start_pos); 1267 calc_jump_sline(); 1268 break; } 1269 case QUERY: { 1270 char buf[3*INT_STRLEN_BOUND(long)+3]; 1271 PARG parg; 1272 SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos)); 1273 parg.p_string = buf; 1274 error(LM(Header_is_X), &parg); 1275 break; } 1276 } 1277 } 1278 1279 /* 1280 * Handler for the --hilite-target option. 1281 */ 1282 /*ARGSUSED*/ 1283 public void opt_hilite_target(int type, constant char *s) 1284 { 1285 (void)s; 1286 switch (type) 1287 { 1288 case INIT: 1289 break; 1290 case TOGGLE: 1291 draw_target_attn(hilite_target != OPT_OFF); 1292 break; 1293 case QUERY: 1294 break; 1295 } 1296 } 1297 1298 /* 1299 * Handler for the --search-options option. 1300 */ 1301 /*ARGSUSED*/ 1302 public void opt_search_type(int type, constant char *s) 1303 { 1304 int st; 1305 PARG parg; 1306 char buf[16]; 1307 char *bp; 1308 int i; 1309 1310 switch (type) 1311 { 1312 case INIT: 1313 case TOGGLE: 1314 st = 0; 1315 for (; *s != '\0'; s++) 1316 { 1317 switch (*s) 1318 { 1319 case 'E': case 'e': case CONTROL('E'): st |= SRCH_PAST_EOF; break; 1320 case 'F': case 'f': case CONTROL('F'): st |= SRCH_FIRST_FILE; break; 1321 case 'K': case 'k': case CONTROL('K'): st |= SRCH_NO_MOVE; break; 1322 case 'N': case 'n': case CONTROL('N'): st |= SRCH_NO_MATCH; break; 1323 case 'R': case 'r': case CONTROL('R'): st |= SRCH_NO_REGEX; break; 1324 case 'W': case 'w': case CONTROL('W'): st |= SRCH_WRAP; break; 1325 case '-': st = 0; break; 1326 case '^': break; 1327 default: 1328 if (*s >= '1' && *s <= '0'+NUM_SEARCH_COLORS) 1329 { 1330 st |= SRCH_SUBSEARCH(*s-'0'); 1331 break; 1332 } 1333 parg.p_char = *s; 1334 error(LM(invalid_search_option_X), &parg); 1335 return; 1336 } 1337 } 1338 def_search_type = norm_search_type(st); 1339 break; 1340 case QUERY: 1341 bp = buf; 1342 if (def_search_type & SRCH_PAST_EOF) *bp++ = 'E'; 1343 if (def_search_type & SRCH_FIRST_FILE) *bp++ = 'F'; 1344 if (def_search_type & SRCH_NO_MOVE) *bp++ = 'K'; 1345 if (def_search_type & SRCH_NO_MATCH) *bp++ = 'N'; 1346 if (def_search_type & SRCH_NO_REGEX) *bp++ = 'R'; 1347 if (def_search_type & SRCH_WRAP) *bp++ = 'W'; 1348 for (i = 1; i <= NUM_SEARCH_COLORS; i++) 1349 if (def_search_type & SRCH_SUBSEARCH(i)) 1350 *bp++ = (char) ('0'+i); 1351 if (bp == buf) 1352 *bp++ = '-'; 1353 *bp = '\0'; 1354 parg.p_string = buf; 1355 error(LM(search_options_X), &parg); 1356 break; 1357 } 1358 } 1359 1360 /* 1361 * Handler for the --no-search-headers, --no-search-header-lines 1362 * and --no-search-header-cols options. 1363 */ 1364 static void do_nosearch_headers(int type, int no_header_lines, int no_header_cols) 1365 { 1366 switch (type) 1367 { 1368 case INIT: 1369 case TOGGLE: 1370 nosearch_header_lines = no_header_lines; 1371 nosearch_header_cols = no_header_cols; 1372 if (type != TOGGLE) break; 1373 /*FALLTHRU*/ 1374 case QUERY: 1375 if (nosearch_header_lines && nosearch_header_cols) 1376 error(LM(Search_does_not_include_header_lines_or_columns), NULL_PARG); 1377 else if (nosearch_header_lines) 1378 error(LM(Search_includes_header_columns_but_not_header_lines), NULL_PARG); 1379 else if (nosearch_header_cols) 1380 error(LM(Search_includes_header_lines_but_not_header_columns), NULL_PARG); 1381 else 1382 error(LM(Search_includes_header_lines_and_columns), NULL_PARG); 1383 } 1384 } 1385 1386 /*ARGSUSED*/ 1387 public void opt_nosearch_headers(int type, constant char *s) 1388 { 1389 (void)s; 1390 do_nosearch_headers(type, 1, 1); 1391 } 1392 1393 /*ARGSUSED*/ 1394 public void opt_nosearch_header_lines(int type, constant char *s) 1395 { 1396 (void)s; 1397 do_nosearch_headers(type, 1, 0); 1398 } 1399 1400 /*ARGSUSED*/ 1401 public void opt_nosearch_header_cols(int type, constant char *s) 1402 { 1403 (void)s; 1404 do_nosearch_headers(type, 0, 1); 1405 } 1406 1407 /*ARGSUSED*/ 1408 public void opt_no_paste(int type, constant char *s) 1409 { 1410 (void)s; 1411 switch (type) 1412 { 1413 case TOGGLE: 1414 if (no_paste) 1415 init_bracketed_paste(); 1416 else 1417 deinit_bracketed_paste(); 1418 break; 1419 case INIT: 1420 case QUERY: 1421 break; 1422 } 1423 } 1424 1425 #if LESSTEST 1426 /* 1427 * Handler for the --tty option. 1428 */ 1429 /*ARGSUSED*/ 1430 public void opt_ttyin_name(int type, constant char *s) 1431 { 1432 switch (type) 1433 { 1434 case INIT: 1435 ttyin_name = s; 1436 is_tty = TRUE; 1437 break; 1438 } 1439 } 1440 #endif /*LESSTEST*/ 1441 1442 public int chop_line(void) 1443 { 1444 return (chopline || header_cols > 0 || header_lines > 0); 1445 } 1446 1447 /* 1448 * Get the "screen window" size. 1449 */ 1450 public int get_swindow(void) 1451 { 1452 if (swindow > 0) 1453 return (swindow); 1454 return (sc_height - header_lines + swindow); 1455 } 1456 1457 /* 1458 * Should an action initiate an autosave? 1459 */ 1460 public lbool autosave_action(char act) 1461 { 1462 return strchr(autosave, act) != NULL || strchr(autosave, '*') != NULL; 1463 } 1464