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 * User-level command processor. 13 */ 14 15 #include "less.h" 16 #if MSDOS_COMPILER==WIN32C 17 #include <windows.h> 18 #endif 19 #include "position.h" 20 #include "option.h" 21 #include "cmd.h" 22 23 extern int erase_char, erase2_char, kill_char; 24 extern int sigs; 25 extern int quit_if_one_screen; 26 extern int one_screen; 27 extern int sc_width; 28 extern int sc_height; 29 extern char *kent; 30 extern int swindow; 31 extern int jump_sline; 32 extern lbool quitting; 33 extern int wscroll; 34 extern int top_scroll; 35 extern int ignore_eoi; 36 extern int hshift; 37 extern int bs_mode; 38 extern int proc_backspace; 39 extern int show_attn; 40 extern int less_is_more; 41 extern int chopline; 42 extern POSITION highest_hilite; 43 extern char *every_first_cmd; 44 extern char version[]; 45 extern struct scrpos initial_scrpos; 46 extern IFILE curr_ifile; 47 extern void *ml_search; 48 extern void *ml_examine; 49 extern int wheel_lines; 50 extern int def_search_type; 51 extern lbool search_wrapped; 52 extern lbool no_poll; 53 extern int no_paste; 54 extern lbool pasting; 55 extern int no_edit_warn; 56 extern POSITION soft_eof; 57 extern POSITION search_incr_start; 58 extern char *first_cmd_at_prompt; 59 #if SHELL_ESCAPE || PIPEC 60 extern void *ml_shell; 61 #endif 62 #if EDITOR 63 extern constant char *editproto; 64 #endif 65 #if OSC8_LINK 66 extern char *osc8_uri; 67 #endif 68 extern int shift_count; 69 extern int forw_prompt; 70 extern int incr_search; 71 extern int full_screen; 72 #if MSDOS_COMPILER==WIN32C 73 extern int utf_mode; 74 extern unsigned less_acp; 75 #endif 76 77 #if SHELL_ESCAPE 78 static char *shellcmd = NULL; /* For holding last shell command for "!!" */ 79 #endif 80 static int mca; /* The multicharacter command (action) */ 81 static int search_type; /* The previous type of search */ 82 static int last_search_type; /* Type of last executed search */ 83 static LINENUM number; /* The number typed by the user */ 84 static long fraction; /* The fractional part of the number */ 85 static struct loption *curropt; 86 static lbool opt_lower; 87 static int optflag; 88 static lbool optgetname; 89 static POSITION bottompos; 90 static int save_hshift; 91 static int save_bs_mode; 92 static int save_proc_backspace; 93 static int screen_trashed_value = 0; 94 static lbool literal_char = FALSE; 95 static lbool ignoring_input = FALSE; 96 static struct scrpos search_incr_pos = { NULL_POSITION, 0 }; 97 static int search_incr_hshift; 98 #if HAVE_TIME 99 static time_type ignoring_input_time; 100 #endif 101 #if PIPEC 102 static char pipec; 103 #endif 104 105 /* Stack of ungotten chars (via ungetcc) */ 106 struct ungot { 107 struct ungot *ug_next; 108 char ug_char; 109 lbool ug_end_command; 110 }; 111 static struct ungot* ungot = NULL; 112 113 static void multi_search(constant char *pattern, int n, int silent); 114 115 /* 116 * Move the cursor to start of prompt line before executing a command. 117 * This looks nicer if the command takes a long time before 118 * updating the screen. 119 */ 120 public void cmd_exec(void) 121 { 122 clear_attn(); 123 clear_bot(); 124 flush(); 125 } 126 127 /* 128 * Indicate we are reading a multi-character command. 129 */ 130 static void set_mca(int action) 131 { 132 mca = action; 133 clear_bot(); 134 clear_cmd(); 135 } 136 137 /* 138 * Indicate we are not reading a multi-character command. 139 */ 140 static void clear_mca(void) 141 { 142 if (mca == 0) 143 return; 144 mca = 0; 145 } 146 147 /* 148 * Set up the display to start a new multi-character command. 149 */ 150 static void start_mca(int action, constant char *prompt, void *mlist, int cmdflags) 151 { 152 set_mca(action); 153 cmd_putstr(prompt); 154 set_mlist(mlist, cmdflags); 155 } 156 157 public int in_mca(void) 158 { 159 return (mca != 0 && mca != A_PREFIX); 160 } 161 162 /* 163 * Set up the display to start a new search command. 164 */ 165 static void mca_search1(void) 166 { 167 int i; 168 169 #if HILITE_SEARCH 170 if (search_type & SRCH_FILTER) 171 set_mca(A_FILTER); 172 else 173 #endif 174 if (search_type & SRCH_FORW) 175 set_mca(A_F_SEARCH); 176 else 177 set_mca(A_B_SEARCH); 178 179 if (search_type & SRCH_NO_MATCH) 180 cmd_putstr("Non-match "); 181 if (search_type & SRCH_FIRST_FILE) 182 cmd_putstr("First-file "); 183 if (search_type & SRCH_PAST_EOF) 184 cmd_putstr("EOF-ignore "); 185 if (search_type & SRCH_NO_MOVE) 186 cmd_putstr("Keep-pos "); 187 if (search_type & SRCH_NO_REGEX) 188 cmd_putstr("Regex-off "); 189 if (search_type & SRCH_WRAP) 190 cmd_putstr("Wrap "); 191 for (i = 1; i <= NUM_SEARCH_COLORS; i++) 192 { 193 if (search_type & SRCH_SUBSEARCH(i)) 194 { 195 char buf[INT_STRLEN_BOUND(int)+8]; 196 SNPRINTF1(buf, sizeof(buf), "Sub-%d ", i); 197 cmd_putstr(buf); 198 } 199 } 200 if (literal_char) 201 cmd_putstr("Lit "); 202 203 #if HILITE_SEARCH 204 if (search_type & SRCH_FILTER) 205 cmd_putstr("&/"); 206 else 207 #endif 208 if (search_type & SRCH_FORW) 209 cmd_putstr("/"); 210 else 211 cmd_putstr("?"); 212 forw_prompt = 0; 213 } 214 215 static void mca_search(void) 216 { 217 if (incr_search) 218 { 219 /* Remember where the incremental search started. */ 220 get_scrpos(&search_incr_pos, TOP); 221 search_incr_start = search_pos(search_type); 222 search_incr_hshift = hshift; 223 } 224 mca_search1(); 225 set_mlist(ml_search, 0); 226 } 227 228 /* 229 * Set up the display to start a new toggle-option command. 230 */ 231 static void mca_opt_toggle(void) 232 { 233 int no_prompt = (optflag & OPT_NO_PROMPT); 234 int flag = (optflag & ~OPT_NO_PROMPT); 235 constant char *dash = (flag == OPT_NO_TOGGLE) ? "_" : "-"; 236 237 set_mca(A_OPT_TOGGLE); 238 cmd_putstr(dash); 239 if (optgetname) 240 cmd_putstr(dash); 241 if (no_prompt) 242 cmd_putstr("(P)"); 243 switch (flag) 244 { 245 case OPT_UNSET: 246 cmd_putstr("+"); 247 break; 248 case OPT_SET: 249 cmd_putstr("!"); 250 break; 251 } 252 forw_prompt = 0; 253 set_mlist(NULL, CF_OPTION); 254 } 255 256 /* 257 * Execute a multicharacter command. 258 */ 259 static void exec_mca(void) 260 { 261 constant char *cbuf; 262 263 cmd_exec(); 264 cbuf = get_cmdbuf(); 265 if (cbuf == NULL) 266 return; 267 268 switch (mca) 269 { 270 case A_F_SEARCH: 271 case A_B_SEARCH: 272 multi_search(cbuf, (int) number, 0); 273 break; 274 #if HILITE_SEARCH 275 case A_FILTER: 276 search_type ^= SRCH_NO_MATCH; 277 set_filter_pattern(cbuf, search_type); 278 soft_eof = NULL_POSITION; 279 break; 280 #endif 281 case A_FIRSTCMD: 282 /* 283 * Skip leading spaces or + signs in the string. 284 */ 285 while (*cbuf == '+' || *cbuf == ' ') 286 cbuf++; 287 if (every_first_cmd != NULL) 288 free(every_first_cmd); 289 if (*cbuf == '\0') 290 every_first_cmd = NULL; 291 else 292 every_first_cmd = save(cbuf); 293 break; 294 case A_OPT_TOGGLE: 295 toggle_option(curropt, opt_lower, cbuf, optflag); 296 curropt = NULL; 297 break; 298 case A_F_BRACKET: 299 match_brac(cbuf[0], cbuf[1], 1, (int) number); 300 break; 301 case A_B_BRACKET: 302 match_brac(cbuf[1], cbuf[0], 0, (int) number); 303 break; 304 #if EXAMINE 305 case A_EXAMINE: { 306 char *p; 307 if (!secure_allow(SF_EXAMINE)) 308 break; 309 p = save(cbuf); 310 edit_list(p); 311 free(p); 312 #if TAGS 313 /* If tag structure is loaded then clean it up. */ 314 cleantags(); 315 #endif 316 break; } 317 #endif 318 #if SHELL_ESCAPE 319 case A_SHELL: { 320 /* 321 * !! just uses whatever is in shellcmd. 322 * Otherwise, copy cmdbuf to shellcmd, 323 * expanding any special characters ("%" or "#"). 324 */ 325 constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "!done"; 326 if (done_msg == NULL) 327 ++cbuf; 328 if (*cbuf != '!') 329 { 330 if (shellcmd != NULL) 331 free(shellcmd); 332 shellcmd = fexpand(cbuf); 333 } 334 if (!secure_allow(SF_SHELL)) 335 break; 336 if (shellcmd == NULL) 337 shellcmd = ""; 338 lsystem(shellcmd, done_msg); 339 break; } 340 case A_PSHELL: { 341 constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "#done"; 342 if (done_msg == NULL) 343 ++cbuf; 344 if (!secure_allow(SF_SHELL)) 345 break; 346 lsystem(pr_expand(cbuf), done_msg); 347 break; } 348 #endif 349 #if PIPEC 350 case A_PIPE: { 351 constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "|done"; 352 if (done_msg == NULL) 353 ++cbuf; 354 if (!secure_allow(SF_PIPE)) 355 break; 356 (void) pipe_mark(pipec, cbuf); 357 if (done_msg != NULL) 358 error(done_msg, NULL_PARG); 359 break; } 360 #endif 361 } 362 } 363 364 /* 365 * Is a character an erase or kill char? 366 */ 367 static lbool is_erase_char(char c) 368 { 369 return (c == erase_char || c == erase2_char || c == kill_char); 370 } 371 372 /* 373 * Is a character a carriage return or newline? 374 */ 375 static lbool is_newline_char(char c) 376 { 377 return (c == '\n' || c == '\r'); 378 } 379 380 /* 381 * Handle the first char of an option (after the initial dash). 382 */ 383 static int mca_opt_first_char(char c) 384 { 385 int no_prompt = (optflag & OPT_NO_PROMPT); 386 int flag = (optflag & ~OPT_NO_PROMPT); 387 if (flag == OPT_NO_TOGGLE) 388 { 389 switch (c) 390 { 391 case '_': 392 /* "__" = long option name. */ 393 optgetname = TRUE; 394 mca_opt_toggle(); 395 return (MCA_MORE); 396 } 397 } else 398 { 399 switch (c) 400 { 401 case '+': 402 /* "-+" = UNSET. */ 403 optflag = no_prompt | ((flag == OPT_UNSET) ? 404 OPT_TOGGLE : OPT_UNSET); 405 mca_opt_toggle(); 406 return (MCA_MORE); 407 case '!': 408 /* "-!" = SET */ 409 optflag = no_prompt | ((flag == OPT_SET) ? 410 OPT_TOGGLE : OPT_SET); 411 mca_opt_toggle(); 412 return (MCA_MORE); 413 case CONTROL('P'): 414 optflag ^= OPT_NO_PROMPT; 415 mca_opt_toggle(); 416 return (MCA_MORE); 417 case '-': 418 /* "--" = long option name. */ 419 optgetname = TRUE; 420 mca_opt_toggle(); 421 return (MCA_MORE); 422 } 423 } 424 /* Char was not handled here. */ 425 return (NO_MCA); 426 } 427 428 /* 429 * Add a char to a long option name. 430 * See if we've got a match for an option name yet. 431 * If so, display the complete name and stop 432 * accepting chars until user hits RETURN. 433 */ 434 static int mca_opt_nonfirst_char(char c) 435 { 436 constant char *p; 437 constant char *oname; 438 lbool ambig; 439 struct loption *was_curropt; 440 441 if (curropt != NULL) 442 { 443 /* Already have a match for the name. */ 444 if (is_erase_char(c)) 445 return (MCA_DONE); 446 /* {{ Checking for TAB here is ugly. 447 * Also doesn't extend well -- can't do BACKTAB this way 448 * because it's a multichar sequence. }} */ 449 if (c != '\t') 450 return (MCA_MORE); 451 } 452 /* 453 * Add char to cmd buffer and try to match 454 * the option name. 455 */ 456 if (cmd_char(c) == CC_QUIT) 457 return (MCA_DONE); 458 p = get_cmdbuf(); 459 if (p == NULL || p[0] == '\0') 460 return (MCA_MORE); 461 opt_lower = ASCII_IS_LOWER(p[0]); 462 was_curropt = curropt; 463 curropt = findopt_name(&p, &oname, &ambig); 464 if (curropt != NULL) 465 { 466 if (was_curropt == NULL) 467 { 468 /* 469 * Got a match. 470 * Remember the option and 471 * display the full option name. 472 */ 473 cmd_reset(); 474 mca_opt_toggle(); 475 cmd_setstring(oname, !opt_lower); 476 } 477 } else if (!ambig) 478 { 479 bell(); 480 } 481 return (MCA_MORE); 482 } 483 484 /* 485 * Handle a char of an option toggle command. 486 */ 487 static int mca_opt_char(char c) 488 { 489 PARG parg; 490 491 /* 492 * This may be a short option (single char), 493 * or one char of a long option name, 494 * or one char of the option parameter. 495 */ 496 if (curropt == NULL && cmdbuf_empty()) 497 { 498 int ret = mca_opt_first_char(c); 499 if (ret != NO_MCA) 500 return (ret); 501 } 502 if (optgetname) 503 { 504 /* We're getting a long option name. */ 505 if (!is_newline_char(c) && c != '=') 506 return (mca_opt_nonfirst_char(c)); 507 if (curropt == NULL) 508 { 509 parg.p_string = get_cmdbuf(); 510 if (parg.p_string == NULL) 511 return (MCA_MORE); 512 error("There is no --%s option", &parg); 513 return (MCA_DONE); 514 } 515 optgetname = FALSE; 516 cmd_reset(); 517 } else 518 { 519 if (is_erase_char(c)) 520 return (NO_MCA); 521 if (curropt != NULL) 522 /* We're getting the option parameter. */ 523 return (NO_MCA); 524 curropt = findopt(c); 525 if (curropt == NULL) 526 { 527 parg.p_string = propt(c); 528 error("There is no %s option", &parg); 529 return (MCA_DONE); 530 } 531 opt_lower = ASCII_IS_LOWER(c); 532 } 533 /* 534 * If the option which was entered does not take a 535 * parameter, toggle the option immediately, 536 * so user doesn't have to hit RETURN. 537 */ 538 if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE || 539 !opt_has_param(curropt)) 540 { 541 toggle_option(curropt, opt_lower, "", optflag); 542 return (MCA_DONE); 543 } 544 /* 545 * Display a prompt appropriate for the option parameter. 546 */ 547 start_mca(A_OPT_TOGGLE, opt_prompt(curropt), NULL, CF_OPTION); 548 return (MCA_MORE); 549 } 550 551 /* 552 * Normalize search type. 553 */ 554 public int norm_search_type(int st) 555 { 556 /* WRAP and PAST_EOF are mutually exclusive. */ 557 if ((st & (SRCH_PAST_EOF|SRCH_WRAP)) == (SRCH_PAST_EOF|SRCH_WRAP)) 558 st ^= SRCH_PAST_EOF; 559 return st; 560 } 561 562 /* 563 * Handle a char of a search command. 564 */ 565 static int mca_search_char(char c) 566 { 567 int flag = 0; 568 569 /* 570 * Certain characters as the first char of 571 * the pattern have special meaning: 572 * ! Toggle the NO_MATCH flag 573 * * Toggle the PAST_EOF flag 574 * @ Toggle the FIRST_FILE flag 575 */ 576 if (!cmdbuf_empty() || literal_char) 577 { 578 literal_char = FALSE; 579 return (NO_MCA); 580 } 581 582 switch (c) 583 { 584 case '*': 585 if (less_is_more) 586 break; 587 case CONTROL('E'): /* ignore END of file */ 588 if (mca != A_FILTER) 589 flag = SRCH_PAST_EOF; 590 search_type &= ~SRCH_WRAP; 591 break; 592 case '@': 593 if (less_is_more) 594 break; 595 case CONTROL('F'): /* FIRST file */ 596 if (mca != A_FILTER) 597 flag = SRCH_FIRST_FILE; 598 break; 599 case CONTROL('K'): /* KEEP position */ 600 if (mca != A_FILTER) 601 flag = SRCH_NO_MOVE; 602 break; 603 case CONTROL('S'): { /* SUBSEARCH */ 604 char buf[INT_STRLEN_BOUND(int)+24]; 605 SNPRINTF1(buf, sizeof(buf), "Sub-pattern (1-%d):", NUM_SEARCH_COLORS); 606 clear_bot(); 607 cmd_putstr(buf); 608 flush(); 609 c = getcc(); 610 if (c >= '1' && c <= '0'+NUM_SEARCH_COLORS) 611 flag = SRCH_SUBSEARCH(c-'0'); 612 else 613 flag = -1; /* calls mca_search() below to repaint */ 614 break; } 615 case CONTROL('W'): /* WRAP around */ 616 if (mca != A_FILTER) 617 flag = SRCH_WRAP; 618 break; 619 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ 620 flag = SRCH_NO_REGEX; 621 break; 622 case CONTROL('N'): /* NOT match */ 623 case '!': 624 flag = SRCH_NO_MATCH; 625 break; 626 case CONTROL('L'): 627 literal_char = TRUE; 628 flag = -1; 629 break; 630 } 631 632 if (flag != 0) 633 { 634 if (flag != -1) 635 search_type = norm_search_type(search_type ^ flag); 636 mca_search(); 637 return (MCA_MORE); 638 } 639 return (NO_MCA); 640 } 641 642 /* 643 * Handle a character of a multi-character command. 644 */ 645 static int mca_char(char c) 646 { 647 int ret; 648 649 switch (mca) 650 { 651 case 0: 652 /* 653 * We're not in a multicharacter command. 654 */ 655 return (NO_MCA); 656 657 case A_PREFIX: 658 /* 659 * In the prefix of a command. 660 * This not considered a multichar command 661 * (even tho it uses cmdbuf, etc.). 662 * It is handled in the commands() switch. 663 */ 664 return (NO_MCA); 665 666 case A_DIGIT: 667 /* 668 * Entering digits of a number. 669 * Terminated by a non-digit. 670 */ 671 if ((c >= '0' && c <= '9') || c == '.') 672 break; 673 switch (editchar(c, ECF_PEEK|ECF_NOHISTORY|ECF_NOCOMPLETE|ECF_NORIGHTLEFT)) 674 { 675 case A_NOACTION: 676 /* 677 * Ignore this char and get another one. 678 */ 679 return (MCA_MORE); 680 case A_INVALID: 681 /* 682 * Not part of the number. 683 * End the number and treat this char 684 * as a normal command character. 685 */ 686 number = cmd_int(&fraction); 687 clear_mca(); 688 cmd_accept(); 689 return (NO_MCA); 690 } 691 break; 692 693 case A_OPT_TOGGLE: 694 ret = mca_opt_char(c); 695 if (ret != NO_MCA) 696 return (ret); 697 break; 698 699 case A_F_SEARCH: 700 case A_B_SEARCH: 701 case A_FILTER: 702 ret = mca_search_char(c); 703 if (ret != NO_MCA) 704 return (ret); 705 break; 706 707 default: 708 /* Other multicharacter command. */ 709 break; 710 } 711 712 /* 713 * The multichar command is terminated by a newline. 714 */ 715 if (is_newline_char(c)) 716 { 717 if (pasting && no_paste) 718 { 719 /* Ignore pasted input after (and including) the first newline */ 720 start_ignoring_input(); 721 return (MCA_MORE); 722 } 723 /* Execute the command. */ 724 exec_mca(); 725 return (MCA_DONE); 726 } 727 728 /* 729 * Append the char to the command buffer. 730 */ 731 if (cmd_char(c) == CC_QUIT) 732 /* 733 * Abort the multi-char command. 734 */ 735 return (MCA_DONE); 736 737 switch (mca) 738 { 739 case A_F_BRACKET: 740 case A_B_BRACKET: 741 if (len_cmdbuf() >= 2) 742 { 743 /* 744 * Special case for the bracket-matching commands. 745 * Execute the command after getting exactly two 746 * characters from the user. 747 */ 748 exec_mca(); 749 return (MCA_DONE); 750 } 751 break; 752 case A_F_SEARCH: 753 case A_B_SEARCH: 754 if (incr_search) 755 { 756 /* Incremental search: do a search after every input char. */ 757 int st = (search_type & (SRCH_FORW|SRCH_BACK|SRCH_NO_MATCH|SRCH_NO_REGEX|SRCH_NO_MOVE|SRCH_WRAP|SRCH_SUBSEARCH_ALL)); 758 ssize_t save_updown; 759 constant char *pattern = get_cmdbuf(); 760 if (pattern == NULL) 761 return (MCA_MORE); 762 /* Defer searching if more chars of the pattern are available. */ 763 if (ttyin_ready()) 764 return (MCA_MORE); 765 /* 766 * Must save updown_match because mca_search 767 * reinits it. That breaks history scrolling. 768 * {{ This is ugly. mca_search probably shouldn't call set_mlist. }} 769 */ 770 save_updown = save_updown_match(); 771 cmd_exec(); 772 if (*pattern == '\0') 773 { 774 /* User has backspaced to an empty pattern. */ 775 undo_search(1); 776 hshift = search_incr_hshift; 777 jump_loc(search_incr_pos.pos, search_incr_pos.ln); 778 } else 779 { 780 /* 781 * Suppress tty polling while searching. 782 * This avoids a problem where tty input 783 * can cause the search to be interrupted. 784 */ 785 no_poll = TRUE; 786 if (search(st | SRCH_INCR, pattern, 1) != 0) 787 { 788 /* No match, invalid pattern, etc. */ 789 undo_search(1); 790 hshift = search_incr_hshift; 791 jump_loc(search_incr_pos.pos, search_incr_pos.ln); 792 } 793 no_poll = FALSE; 794 } 795 /* Redraw the search prompt and search string. */ 796 if (is_screen_trashed() || !full_screen) 797 { 798 clear(); 799 repaint(); 800 } 801 mca_search1(); 802 restore_updown_match(save_updown); 803 cmd_repaint(NULL); 804 } 805 break; 806 } 807 808 /* 809 * Need another character. 810 */ 811 return (MCA_MORE); 812 } 813 814 /* 815 * Discard any buffered file data. 816 */ 817 static void clear_buffers(void) 818 { 819 if (!(ch_getflags() & CH_CANSEEK)) 820 return; 821 ch_flush(); 822 clr_linenum(); 823 #if HILITE_SEARCH 824 clr_hilite(); 825 #endif 826 set_line_contig_pos(NULL_POSITION); 827 } 828 829 public void screen_trashed_num(int trashed) 830 { 831 screen_trashed_value = trashed; 832 } 833 834 public void screen_trashed(void) 835 { 836 screen_trashed_num(1); 837 } 838 839 public int is_screen_trashed(void) 840 { 841 return screen_trashed_value; 842 } 843 844 /* 845 * Make sure the screen is displayed. 846 */ 847 static void make_display(void) 848 { 849 /* 850 * If not full_screen, we can't rely on scrolling to fill the screen. 851 * We need to clear and repaint screen before any change. 852 */ 853 if (!full_screen && !(quit_if_one_screen && one_screen)) 854 clear(); 855 /* 856 * If nothing is displayed yet, display starting from initial_scrpos. 857 */ 858 if (empty_screen()) 859 { 860 if (initial_scrpos.pos == NULL_POSITION) 861 jump_loc(ch_zero(), 1); 862 else 863 jump_loc(initial_scrpos.pos, initial_scrpos.ln); 864 } else if (is_screen_trashed() || !full_screen) 865 { 866 int save_top_scroll = top_scroll; 867 int save_ignore_eoi = ignore_eoi; 868 top_scroll = 1; 869 ignore_eoi = 0; 870 if (is_screen_trashed() == 2) 871 { 872 /* Special case used by ignore_eoi: re-open the input file 873 * and jump to the end of the file. */ 874 reopen_curr_ifile(); 875 jump_forw(); 876 } 877 repaint(); 878 top_scroll = save_top_scroll; 879 ignore_eoi = save_ignore_eoi; 880 } 881 } 882 883 /* 884 * Display the appropriate prompt. 885 */ 886 static void prompt(void) 887 { 888 constant char *p; 889 890 if (ungot != NULL && !ungot->ug_end_command) 891 { 892 /* 893 * No prompt necessary if commands are from 894 * ungotten chars rather than from the user. 895 */ 896 return; 897 } 898 899 /* 900 * Make sure the screen is displayed. 901 */ 902 make_display(); 903 bottompos = position(BOTTOM_PLUS_ONE); 904 905 /* 906 * If we've hit EOF on the last file and the -E flag is set, quit. 907 */ 908 if (get_quit_at_eof() == OPT_ONPLUS && 909 eof_displayed(FALSE) && !(ch_getflags() & CH_HELPFILE) && 910 next_ifile(curr_ifile) == NULL_IFILE) 911 quit(QUIT_OK); 912 913 /* 914 * If the entire file is displayed and the -F flag is set, quit. 915 */ 916 if (quit_if_one_screen && 917 entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && 918 next_ifile(curr_ifile) == NULL_IFILE) 919 quit(QUIT_OK); 920 quit_if_one_screen = FALSE; /* only get one chance at this */ 921 if (first_cmd_at_prompt != NULL) 922 { 923 ungetsc(first_cmd_at_prompt); 924 first_cmd_at_prompt = NULL; 925 return; 926 } 927 928 #if MSDOS_COMPILER==WIN32C 929 /* 930 * In Win32, display the file name in the window title. 931 */ 932 if (!(ch_getflags() & CH_HELPFILE)) 933 { 934 WCHAR w[MAX_PATH+16]; 935 p = pr_expand("Less?f - %f."); 936 MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w)); 937 SetConsoleTitleW(w); 938 } 939 #endif 940 941 /* 942 * Select the proper prompt and display it. 943 */ 944 /* 945 * If the previous action was a forward movement, 946 * don't clear the bottom line of the display; 947 * just print the prompt since the forward movement guarantees 948 * that we're in the right position to display the prompt. 949 * Clearing the line could cause a problem: for example, if the last 950 * line displayed ended at the right screen edge without a newline, 951 * then clearing would clear the last displayed line rather than 952 * the prompt line. 953 */ 954 if (!forw_prompt) 955 clear_bot(); 956 clear_cmd(); 957 forw_prompt = 0; 958 p = pr_string(); 959 #if HILITE_SEARCH 960 if (is_filtering()) 961 putstr("& "); 962 #endif 963 if (search_wrapped) 964 { 965 if (search_type & SRCH_BACK) 966 error("Search hit top; continuing at bottom", NULL_PARG); 967 else 968 error("Search hit bottom; continuing at top", NULL_PARG); 969 search_wrapped = FALSE; 970 } 971 #if OSC8_LINK 972 if (osc8_uri != NULL) 973 { 974 PARG parg; 975 parg.p_string = osc8_uri; 976 error("Link: %s", &parg); 977 free(osc8_uri); 978 osc8_uri = NULL; 979 } 980 #endif 981 if (p == NULL || *p == '\0') 982 { 983 at_enter(AT_NORMAL|AT_COLOR_PROMPT); 984 putchr(':'); 985 at_exit(); 986 } else 987 { 988 #if MSDOS_COMPILER==WIN32C 989 WCHAR w[MAX_PATH*2]; 990 char a[MAX_PATH*2]; 991 MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w)); 992 WideCharToMultiByte(utf_mode ? CP_UTF8 : GetConsoleOutputCP(), 993 0, w, -1, a, sizeof(a), NULL, NULL); 994 p = a; 995 #endif 996 load_line(p); 997 put_line(FALSE); 998 } 999 clear_eol(); 1000 resume_screen(); 1001 } 1002 1003 /* 1004 * Display the less version message. 1005 */ 1006 public void dispversion(void) 1007 { 1008 PARG parg; 1009 1010 parg.p_string = version; 1011 error("less %s", &parg); 1012 } 1013 1014 /* 1015 * Return a character to complete a partial command, if possible. 1016 */ 1017 static char getcc_end_command(void) 1018 { 1019 int ch; 1020 switch (mca) 1021 { 1022 case A_DIGIT: 1023 /* We have a number but no command. Treat as #g. */ 1024 return ('g'); 1025 case A_F_SEARCH: 1026 case A_B_SEARCH: 1027 case A_FILTER: 1028 /* We have "/string" but no newline. Add the \n. */ 1029 return ('\n'); 1030 default: 1031 /* Some other incomplete command. Let user complete it. */ 1032 if (ungot != NULL) 1033 return ('\0'); 1034 ch = getchr(); 1035 if (ch < 0) ch = '\0'; 1036 return (char) ch; 1037 } 1038 } 1039 1040 /* 1041 * Get a command character from the ungotten stack. 1042 */ 1043 static char get_ungot(lbool *p_end_command) 1044 { 1045 struct ungot *ug = ungot; 1046 char c = ug->ug_char; 1047 if (p_end_command != NULL) 1048 *p_end_command = ug->ug_end_command; 1049 ungot = ug->ug_next; 1050 free(ug); 1051 return c; 1052 } 1053 1054 /* 1055 * Delete all ungotten characters. 1056 */ 1057 public void getcc_clear(void) 1058 { 1059 while (ungot != NULL) 1060 (void) get_ungot(NULL); 1061 } 1062 1063 /* 1064 * Get command character. 1065 * The character normally comes from the keyboard, 1066 * but may come from ungotten characters 1067 * (characters previously given to ungetcc or ungetsc). 1068 */ 1069 static char getccu(void) 1070 { 1071 int c = 0; 1072 while (c == 0 && sigs == 0) 1073 { 1074 if (ungot == NULL) 1075 { 1076 /* Normal case: no ungotten chars. 1077 * Get char from the user. */ 1078 c = getchr(); 1079 if (c < 0) c = '\0'; 1080 } else 1081 { 1082 /* Ungotten chars available: 1083 * Take the top of stack (most recent). */ 1084 lbool end_command; 1085 c = get_ungot(&end_command); 1086 if (end_command) 1087 c = getcc_end_command(); 1088 } 1089 } 1090 return ((char) c); 1091 } 1092 1093 /* 1094 * Get a command character, but if we receive the orig sequence, 1095 * convert it to the repl sequence. 1096 */ 1097 static char getcc_repl(char constant *orig, char constant *repl, char (*gr_getc)(void), void (*gr_ungetc)(char)) 1098 { 1099 char c; 1100 char keys[16]; 1101 size_t ki = 0; 1102 1103 c = (*gr_getc)(); 1104 if (orig == NULL || orig[0] == '\0') 1105 return c; 1106 for (;;) 1107 { 1108 keys[ki] = c; 1109 if (c != orig[ki] || ki >= sizeof(keys)-1) 1110 { 1111 /* This is not orig we have been receiving. 1112 * If we have stashed chars in keys[], 1113 * unget them and return the first one. */ 1114 while (ki > 0) 1115 (*gr_ungetc)(keys[ki--]); 1116 return keys[0]; 1117 } 1118 if (orig[++ki] == '\0') 1119 { 1120 /* We've received the full orig sequence. 1121 * Return the repl sequence. */ 1122 ki = strlen(repl)-1; 1123 while (ki > 0) 1124 (*gr_ungetc)(repl[ki--]); 1125 return repl[0]; 1126 } 1127 /* We've received a partial orig sequence (ki chars of it). 1128 * Get next char and see if it continues to match orig. */ 1129 c = (*gr_getc)(); 1130 } 1131 } 1132 1133 /* 1134 * Get command character. 1135 */ 1136 public char getcc(void) 1137 { 1138 /* Replace kent (keypad Enter) with a newline. */ 1139 return getcc_repl(kent, "\n", getccu, ungetcc); 1140 } 1141 1142 /* 1143 * "Unget" a command character. 1144 * The next getcc() will return this character. 1145 */ 1146 public void ungetcc(char c) 1147 { 1148 struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); 1149 1150 ug->ug_char = c; 1151 ug->ug_next = ungot; 1152 ungot = ug; 1153 } 1154 1155 /* 1156 * "Unget" a command character. 1157 * If any other chars are already ungotten, put this one after those. 1158 */ 1159 static void ungetcc_back1(char c, lbool end_command) 1160 { 1161 struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); 1162 ug->ug_char = c; 1163 ug->ug_end_command = end_command; 1164 ug->ug_next = NULL; 1165 if (ungot == NULL) 1166 ungot = ug; 1167 else 1168 { 1169 struct ungot *pu; 1170 for (pu = ungot; pu->ug_next != NULL; pu = pu->ug_next) 1171 continue; 1172 pu->ug_next = ug; 1173 } 1174 } 1175 1176 public void ungetcc_back(char c) 1177 { 1178 ungetcc_back1(c, FALSE); 1179 } 1180 1181 public void ungetcc_end_command(void) 1182 { 1183 ungetcc_back1('\0', TRUE); 1184 } 1185 1186 /* 1187 * Unget a whole string of command characters. 1188 * The next sequence of getcc()'s will return this string. 1189 */ 1190 public void ungetsc(constant char *s) 1191 { 1192 while (*s != '\0') 1193 ungetcc_back(*s++); 1194 } 1195 1196 /* 1197 * Peek the next command character, without consuming it. 1198 */ 1199 public char peekcc(void) 1200 { 1201 char c = getcc(); 1202 ungetcc(c); 1203 return c; 1204 } 1205 1206 /* 1207 * Search for a pattern, possibly in multiple files. 1208 * If SRCH_FIRST_FILE is set, begin searching at the first file. 1209 * If SRCH_PAST_EOF is set, continue the search thru multiple files. 1210 */ 1211 static void multi_search(constant char *pattern, int n, int silent) 1212 { 1213 int nomore; 1214 IFILE save_ifile; 1215 lbool changed_file; 1216 1217 changed_file = FALSE; 1218 save_ifile = save_curr_ifile(); 1219 1220 if ((search_type & (SRCH_FORW|SRCH_BACK)) == 0) 1221 search_type |= SRCH_FORW; 1222 if (search_type & SRCH_FIRST_FILE) 1223 { 1224 /* 1225 * Start at the first (or last) file 1226 * in the command line list. 1227 */ 1228 if (search_type & SRCH_FORW) 1229 nomore = edit_first(); 1230 else 1231 nomore = edit_last(); 1232 if (nomore) 1233 { 1234 unsave_ifile(save_ifile); 1235 return; 1236 } 1237 changed_file = TRUE; 1238 search_type &= ~SRCH_FIRST_FILE; 1239 } 1240 1241 for (;;) 1242 { 1243 n = search(search_type, pattern, n); 1244 /* 1245 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared 1246 * after being used once. This allows "n" to work after 1247 * using a /@@ search. 1248 */ 1249 search_type &= ~SRCH_NO_MOVE; 1250 last_search_type = search_type; 1251 if (n == 0) 1252 { 1253 /* 1254 * Found it. 1255 */ 1256 unsave_ifile(save_ifile); 1257 return; 1258 } 1259 1260 if (n < 0) 1261 /* 1262 * Some kind of error in the search. 1263 * Error message has been printed by search(). 1264 */ 1265 break; 1266 1267 if ((search_type & SRCH_PAST_EOF) == 0) 1268 /* 1269 * We didn't find a match, but we're 1270 * supposed to search only one file. 1271 */ 1272 break; 1273 /* 1274 * Move on to the next file. 1275 */ 1276 if (search_type & SRCH_FORW) 1277 nomore = edit_next(1); 1278 else 1279 nomore = edit_prev(1); 1280 if (nomore) 1281 break; 1282 changed_file = TRUE; 1283 } 1284 1285 /* 1286 * Didn't find it. 1287 * Print an error message if we haven't already. 1288 */ 1289 if (n > 0 && !silent) 1290 error("Pattern not found", NULL_PARG); 1291 1292 if (changed_file) 1293 { 1294 /* 1295 * Restore the file we were originally viewing. 1296 */ 1297 reedit_ifile(save_ifile); 1298 } else 1299 { 1300 unsave_ifile(save_ifile); 1301 } 1302 } 1303 1304 /* 1305 * Forward forever, or until a highlighted line appears. 1306 */ 1307 static int forw_loop(int until_hilite) 1308 { 1309 POSITION curr_len; 1310 1311 if (ch_getflags() & CH_HELPFILE) 1312 return (A_NOACTION); 1313 1314 cmd_exec(); 1315 jump_forw_buffered(); 1316 curr_len = ch_length(); 1317 highest_hilite = until_hilite ? curr_len : NULL_POSITION; 1318 ignore_eoi = 1; 1319 while (!sigs) 1320 { 1321 if (until_hilite && highest_hilite > curr_len) 1322 { 1323 bell(); 1324 break; 1325 } 1326 make_display(); 1327 forward(1, FALSE, FALSE, FALSE); 1328 } 1329 ignore_eoi = 0; 1330 ch_set_eof(); 1331 1332 /* 1333 * This gets us back in "F mode" after processing 1334 * a non-abort signal (e.g. window-change). 1335 */ 1336 if (sigs && !ABORT_SIGS()) 1337 return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER); 1338 1339 return (A_NOACTION); 1340 } 1341 1342 /* 1343 * Ignore subsequent (pasted) input chars. 1344 */ 1345 public void start_ignoring_input() 1346 { 1347 ignoring_input = TRUE; 1348 #if HAVE_TIME 1349 ignoring_input_time = get_time(); 1350 #endif 1351 } 1352 1353 /* 1354 * Stop ignoring input chars. 1355 */ 1356 public void stop_ignoring_input() 1357 { 1358 ignoring_input = FALSE; 1359 pasting = FALSE; 1360 } 1361 1362 /* 1363 * Are we ignoring input chars? 1364 */ 1365 public lbool is_ignoring_input(int action) 1366 { 1367 if (!ignoring_input) 1368 return FALSE; 1369 if (action == A_END_PASTE) 1370 stop_ignoring_input(); 1371 #if HAVE_TIME 1372 if (get_time() >= ignoring_input_time + MAX_PASTE_IGNORE_SEC) 1373 stop_ignoring_input(); 1374 #endif 1375 /* 1376 * Don't ignore prefix chars so we can parse a full command 1377 * (which might be A_END_PASTE). 1378 */ 1379 return (action != A_PREFIX); 1380 } 1381 1382 /* 1383 * Main command processor. 1384 * Accept and execute commands until a quit command. 1385 */ 1386 public void commands(void) 1387 { 1388 char c; 1389 int action; 1390 constant char *cbuf; 1391 constant char *msg; 1392 int newaction; 1393 int save_jump_sline; 1394 int save_search_type; 1395 constant char *extra; 1396 PARG parg; 1397 IFILE old_ifile; 1398 IFILE new_ifile; 1399 #if TAGS 1400 constant char *tagfile; 1401 #endif 1402 1403 search_type = SRCH_FORW; 1404 wscroll = (sc_height + 1) / 2; 1405 newaction = A_NOACTION; 1406 1407 for (;;) 1408 { 1409 clear_mca(); 1410 cmd_accept(); 1411 number = 0; 1412 curropt = NULL; 1413 1414 /* 1415 * See if any signals need processing. 1416 */ 1417 if (sigs) 1418 { 1419 psignals(); 1420 if (quitting) 1421 quit(QUIT_SAVED_STATUS); 1422 } 1423 1424 /* 1425 * See if window size changed, for systems that don't 1426 * generate SIGWINCH. 1427 */ 1428 check_winch(); 1429 1430 /* 1431 * Display prompt and accept a character. 1432 */ 1433 cmd_reset(); 1434 prompt(); 1435 if (sigs) 1436 continue; 1437 if (newaction == A_NOACTION) 1438 c = getcc(); 1439 1440 again: 1441 if (sigs) 1442 continue; 1443 1444 if (newaction != A_NOACTION) 1445 { 1446 action = newaction; 1447 newaction = A_NOACTION; 1448 } else 1449 { 1450 /* 1451 * If we are in a multicharacter command, call mca_char. 1452 * Otherwise we call fcmd_decode to determine the 1453 * action to be performed. 1454 */ 1455 if (mca) 1456 switch (mca_char(c)) 1457 { 1458 case MCA_MORE: 1459 /* 1460 * Need another character. 1461 */ 1462 c = getcc(); 1463 goto again; 1464 case MCA_DONE: 1465 /* 1466 * Command has been handled by mca_char. 1467 * Start clean with a prompt. 1468 */ 1469 continue; 1470 case NO_MCA: 1471 /* 1472 * Not a multi-char command 1473 * (at least, not anymore). 1474 */ 1475 break; 1476 } 1477 1478 /* 1479 * Decode the command character and decide what to do. 1480 */ 1481 extra = NULL; 1482 if (mca) 1483 { 1484 /* 1485 * We're in a multichar command. 1486 * Add the character to the command buffer 1487 * and display it on the screen. 1488 * If the user backspaces past the start 1489 * of the line, abort the command. 1490 */ 1491 if (cmd_char(c) == CC_QUIT || cmdbuf_empty()) 1492 continue; 1493 cbuf = get_cmdbuf(); 1494 if (cbuf == NULL) 1495 { 1496 c = getcc(); 1497 goto again; 1498 } 1499 action = fcmd_decode(cbuf, &extra); 1500 } else 1501 { 1502 /* 1503 * Don't use cmd_char if we're starting fresh 1504 * at the beginning of a command, because we 1505 * don't want to echo the command until we know 1506 * it is a multichar command. We also don't 1507 * want erase_char/kill_char to be treated 1508 * as line editing characters. 1509 */ 1510 constant char tbuf[2] = { c, '\0' }; 1511 action = fcmd_decode(tbuf, &extra); 1512 } 1513 /* 1514 * If an "extra" string was returned, 1515 * process it as a string of command characters. 1516 */ 1517 if (extra != NULL) 1518 ungetsc(extra); 1519 } 1520 /* 1521 * Clear the cmdbuf string. 1522 * (But not if we're in the prefix of a command, 1523 * because the partial command string is kept there.) 1524 */ 1525 if (action != A_PREFIX) 1526 cmd_reset(); 1527 1528 if (is_ignoring_input(action)) 1529 continue; 1530 1531 switch (action) 1532 { 1533 case A_START_PASTE: 1534 if (no_paste) 1535 start_ignoring_input(); 1536 break; 1537 1538 case A_DIGIT: 1539 /* 1540 * First digit of a number. 1541 */ 1542 start_mca(A_DIGIT, ":", NULL, CF_QUIT_ON_ERASE); 1543 goto again; 1544 1545 case A_F_WINDOW: 1546 /* 1547 * Forward one window (and set the window size). 1548 */ 1549 if (number > 0) 1550 swindow = (int) number; 1551 /* FALLTHRU */ 1552 case A_F_SCREEN: 1553 /* 1554 * Forward one screen. 1555 */ 1556 if (number <= 0) 1557 number = get_swindow(); 1558 cmd_exec(); 1559 if (show_attn) 1560 set_attnpos(bottompos); 1561 forward((int) number, FALSE, TRUE, FALSE); 1562 break; 1563 1564 case A_B_WINDOW: 1565 /* 1566 * Backward one window (and set the window size). 1567 */ 1568 if (number > 0) 1569 swindow = (int) number; 1570 /* FALLTHRU */ 1571 case A_B_SCREEN: 1572 /* 1573 * Backward one screen. 1574 */ 1575 if (number <= 0) 1576 number = get_swindow(); 1577 cmd_exec(); 1578 backward((int) number, FALSE, TRUE, FALSE); 1579 break; 1580 1581 case A_F_LINE: 1582 case A_F_NEWLINE: 1583 1584 /* 1585 * Forward N (default 1) line. 1586 */ 1587 if (number <= 0) 1588 number = 1; 1589 cmd_exec(); 1590 if (show_attn == OPT_ONPLUS && number > 1) 1591 set_attnpos(bottompos); 1592 forward((int) number, FALSE, FALSE, action == A_F_NEWLINE && !chopline); 1593 break; 1594 1595 case A_B_LINE: 1596 case A_B_NEWLINE: 1597 /* 1598 * Backward N (default 1) line. 1599 */ 1600 if (number <= 0) 1601 number = 1; 1602 cmd_exec(); 1603 backward((int) number, FALSE, FALSE, action == A_B_NEWLINE && !chopline); 1604 break; 1605 1606 case A_F_MOUSE: 1607 /* 1608 * Forward wheel_lines lines. 1609 */ 1610 cmd_exec(); 1611 forward(wheel_lines, FALSE, FALSE, FALSE); 1612 break; 1613 1614 case A_B_MOUSE: 1615 /* 1616 * Backward wheel_lines lines. 1617 */ 1618 cmd_exec(); 1619 backward(wheel_lines, FALSE, FALSE, FALSE); 1620 break; 1621 1622 case A_FF_LINE: 1623 /* 1624 * Force forward N (default 1) line. 1625 */ 1626 if (number <= 0) 1627 number = 1; 1628 cmd_exec(); 1629 if (show_attn == OPT_ONPLUS && number > 1) 1630 set_attnpos(bottompos); 1631 forward((int) number, TRUE, FALSE, FALSE); 1632 break; 1633 1634 case A_BF_LINE: 1635 /* 1636 * Force backward N (default 1) line. 1637 */ 1638 if (number <= 0) 1639 number = 1; 1640 cmd_exec(); 1641 backward((int) number, TRUE, FALSE, FALSE); 1642 break; 1643 1644 case A_FF_SCREEN: 1645 /* 1646 * Force forward one screen. 1647 */ 1648 if (number <= 0) 1649 number = get_swindow(); 1650 cmd_exec(); 1651 if (show_attn == OPT_ONPLUS) 1652 set_attnpos(bottompos); 1653 forward((int) number, TRUE, FALSE, FALSE); 1654 break; 1655 1656 case A_BF_SCREEN: 1657 /* 1658 * Force backward one screen. 1659 */ 1660 if (number <= 0) 1661 number = get_swindow(); 1662 cmd_exec(); 1663 backward((int) number, TRUE, FALSE, FALSE); 1664 break; 1665 1666 case A_F_FOREVER: 1667 /* 1668 * Forward forever, ignoring EOF. 1669 */ 1670 if (get_altfilename(curr_ifile) != NULL) 1671 error("Warning: command may not work correctly when file is viewed via LESSOPEN", NULL_PARG); 1672 if (show_attn) 1673 set_attnpos(bottompos); 1674 newaction = forw_loop(0); 1675 break; 1676 1677 case A_F_UNTIL_HILITE: 1678 newaction = forw_loop(1); 1679 break; 1680 1681 case A_F_SCROLL: 1682 /* 1683 * Forward N lines 1684 * (default same as last 'd' or 'u' command). 1685 */ 1686 if (number > 0) 1687 wscroll = (int) number; 1688 cmd_exec(); 1689 if (show_attn == OPT_ONPLUS) 1690 set_attnpos(bottompos); 1691 forward(wscroll, FALSE, FALSE, FALSE); 1692 break; 1693 1694 case A_B_SCROLL: 1695 /* 1696 * Forward N lines 1697 * (default same as last 'd' or 'u' command). 1698 */ 1699 if (number > 0) 1700 wscroll = (int) number; 1701 cmd_exec(); 1702 backward(wscroll, FALSE, FALSE, FALSE); 1703 break; 1704 1705 case A_FREPAINT: 1706 /* 1707 * Flush buffers, then repaint screen. 1708 * Don't flush the buffers on a pipe! 1709 */ 1710 clear_buffers(); 1711 /* FALLTHRU */ 1712 case A_REPAINT: 1713 /* 1714 * Repaint screen. 1715 */ 1716 cmd_exec(); 1717 repaint(); 1718 break; 1719 1720 case A_GOLINE: 1721 /* 1722 * Go to line N, default beginning of file. 1723 * If N <= 0, ignore jump_sline in order to avoid 1724 * empty lines before the beginning of the file. 1725 */ 1726 save_jump_sline = jump_sline; 1727 if (number <= 0) 1728 { 1729 number = 1; 1730 jump_sline = 0; 1731 } 1732 cmd_exec(); 1733 jump_back(number); 1734 jump_sline = save_jump_sline; 1735 break; 1736 1737 case A_PERCENT: 1738 /* 1739 * Go to a specified percentage into the file. 1740 */ 1741 if (number < 0) 1742 { 1743 number = 0; 1744 fraction = 0; 1745 } 1746 if (number > 100 || (number == 100 && fraction != 0)) 1747 { 1748 number = 100; 1749 fraction = 0; 1750 } 1751 cmd_exec(); 1752 jump_percent((int) number, fraction); 1753 break; 1754 1755 case A_GOEND: 1756 /* 1757 * Go to line N, default end of file. 1758 */ 1759 cmd_exec(); 1760 if (number <= 0) 1761 jump_forw(); 1762 else 1763 jump_back(number); 1764 break; 1765 1766 case A_GOEND_BUF: 1767 /* 1768 * Go to line N, default last buffered byte. 1769 */ 1770 cmd_exec(); 1771 if (number <= 0) 1772 jump_forw_buffered(); 1773 else 1774 jump_back(number); 1775 break; 1776 1777 case A_GOPOS: 1778 /* 1779 * Go to a specified byte position in the file. 1780 */ 1781 cmd_exec(); 1782 if (number < 0) 1783 number = 0; 1784 jump_line_loc((POSITION) number, jump_sline); 1785 break; 1786 1787 case A_STAT: 1788 /* 1789 * Print file name, etc. 1790 */ 1791 if (ch_getflags() & CH_HELPFILE) 1792 break; 1793 cmd_exec(); 1794 parg.p_string = eq_message(); 1795 error("%s", &parg); 1796 break; 1797 1798 case A_VERSION: 1799 /* 1800 * Print version number. 1801 */ 1802 cmd_exec(); 1803 dispversion(); 1804 break; 1805 1806 case A_QUIT: 1807 /* 1808 * Exit. 1809 */ 1810 if (curr_ifile != NULL_IFILE && 1811 ch_getflags() & CH_HELPFILE) 1812 { 1813 /* 1814 * Quit while viewing the help file 1815 * just means return to viewing the 1816 * previous file. 1817 */ 1818 hshift = save_hshift; 1819 bs_mode = save_bs_mode; 1820 proc_backspace = save_proc_backspace; 1821 if (edit_prev(1) == 0) 1822 break; 1823 } 1824 if (extra != NULL) 1825 quit(*extra); 1826 quit(QUIT_OK); 1827 break; 1828 1829 /* 1830 * Define abbreviation for a commonly used sequence below. 1831 */ 1832 #define DO_SEARCH() \ 1833 if (number <= 0) number = 1; \ 1834 mca_search(); \ 1835 cmd_exec(); \ 1836 multi_search(NULL, (int) number, 0); 1837 1838 case A_F_SEARCH: 1839 /* 1840 * Search forward for a pattern. 1841 * Get the first char of the pattern. 1842 */ 1843 search_type = SRCH_FORW | def_search_type; 1844 if (number <= 0) 1845 number = 1; 1846 literal_char = FALSE; 1847 mca_search(); 1848 c = getcc(); 1849 goto again; 1850 1851 case A_B_SEARCH: 1852 /* 1853 * Search backward for a pattern. 1854 * Get the first char of the pattern. 1855 */ 1856 search_type = SRCH_BACK | def_search_type; 1857 if (number <= 0) 1858 number = 1; 1859 literal_char = FALSE; 1860 mca_search(); 1861 c = getcc(); 1862 goto again; 1863 1864 case A_OSC8_F_SEARCH: 1865 #if OSC8_LINK 1866 cmd_exec(); 1867 if (number <= 0) 1868 number = 1; 1869 osc8_search(SRCH_FORW, NULL, number); 1870 #else 1871 error("Command not available", NULL_PARG); 1872 #endif 1873 break; 1874 1875 case A_OSC8_B_SEARCH: 1876 #if OSC8_LINK 1877 cmd_exec(); 1878 if (number <= 0) 1879 number = 1; 1880 osc8_search(SRCH_BACK, NULL, number); 1881 #else 1882 error("Command not available", NULL_PARG); 1883 #endif 1884 break; 1885 1886 case A_OSC8_OPEN: 1887 #if OSC8_LINK 1888 if (secure_allow(SF_OSC8_OPEN)) 1889 { 1890 cmd_exec(); 1891 osc8_open(); 1892 break; 1893 } 1894 #endif 1895 error("Command not available", NULL_PARG); 1896 break; 1897 1898 case A_OSC8_JUMP: 1899 #if OSC8_LINK 1900 cmd_exec(); 1901 osc8_jump(); 1902 #else 1903 error("Command not available", NULL_PARG); 1904 #endif 1905 break; 1906 1907 case A_FILTER: 1908 #if HILITE_SEARCH 1909 search_type = SRCH_FORW | SRCH_FILTER; 1910 literal_char = FALSE; 1911 mca_search(); 1912 c = getcc(); 1913 goto again; 1914 #else 1915 error("Command not available", NULL_PARG); 1916 break; 1917 #endif 1918 1919 case A_AGAIN_SEARCH: 1920 /* 1921 * Repeat previous search. 1922 */ 1923 search_type = last_search_type; 1924 DO_SEARCH(); 1925 break; 1926 1927 case A_T_AGAIN_SEARCH: 1928 /* 1929 * Repeat previous search, multiple files. 1930 */ 1931 search_type = last_search_type | SRCH_PAST_EOF; 1932 DO_SEARCH(); 1933 break; 1934 1935 case A_REVERSE_SEARCH: 1936 /* 1937 * Repeat previous search, in reverse direction. 1938 */ 1939 save_search_type = search_type = last_search_type; 1940 search_type = SRCH_REVERSE(search_type); 1941 DO_SEARCH(); 1942 last_search_type = save_search_type; 1943 break; 1944 1945 case A_T_REVERSE_SEARCH: 1946 /* 1947 * Repeat previous search, 1948 * multiple files in reverse direction. 1949 */ 1950 save_search_type = search_type = last_search_type; 1951 search_type = SRCH_REVERSE(search_type) | SRCH_PAST_EOF; 1952 DO_SEARCH(); 1953 last_search_type = save_search_type; 1954 break; 1955 1956 case A_UNDO_SEARCH: 1957 case A_CLR_SEARCH: 1958 /* 1959 * Clear search string highlighting. 1960 */ 1961 undo_search(action == A_CLR_SEARCH); 1962 break; 1963 1964 case A_HELP: 1965 /* 1966 * Help. 1967 */ 1968 if (ch_getflags() & CH_HELPFILE) 1969 break; 1970 cmd_exec(); 1971 save_hshift = hshift; 1972 hshift = 0; 1973 save_bs_mode = bs_mode; 1974 bs_mode = BS_SPECIAL; 1975 save_proc_backspace = proc_backspace; 1976 proc_backspace = OPT_OFF; 1977 (void) edit(FAKE_HELPFILE); 1978 break; 1979 1980 case A_EXAMINE: 1981 /* 1982 * Edit a new file. Get the filename. 1983 */ 1984 #if EXAMINE 1985 if (secure_allow(SF_EXAMINE)) 1986 { 1987 start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); 1988 c = getcc(); 1989 goto again; 1990 } 1991 #endif 1992 error("Command not available", NULL_PARG); 1993 break; 1994 1995 case A_VISUAL: 1996 /* 1997 * Invoke an editor on the input file. 1998 */ 1999 #if EDITOR 2000 if (secure_allow(SF_EDIT)) 2001 { 2002 if (ch_getflags() & CH_HELPFILE) 2003 break; 2004 if (strcmp(get_filename(curr_ifile), "-") == 0) 2005 { 2006 error("Cannot edit standard input", NULL_PARG); 2007 break; 2008 } 2009 if (!no_edit_warn && get_altfilename(curr_ifile) != NULL) 2010 { 2011 error("WARNING: This file was viewed via LESSOPEN", NULL_PARG); 2012 } 2013 start_mca(A_SHELL, "!", ml_shell, 0); 2014 /* 2015 * Expand the editor prototype string 2016 * and pass it to the system to execute. 2017 * (Make sure the screen is displayed so the 2018 * expansion of "+%lm" works.) 2019 */ 2020 make_display(); 2021 cmd_exec(); 2022 lsystem(pr_expand(editproto), NULL); 2023 break; 2024 } 2025 #endif 2026 error("Command not available", NULL_PARG); 2027 break; 2028 2029 case A_NEXT_FILE: 2030 /* 2031 * Examine next file. 2032 */ 2033 #if TAGS 2034 if (ntags()) 2035 { 2036 error("No next file", NULL_PARG); 2037 break; 2038 } 2039 #endif 2040 if (number <= 0) 2041 number = 1; 2042 cmd_exec(); 2043 if (edit_next((int) number)) 2044 { 2045 if (get_quit_at_eof() && eof_displayed(FALSE) && 2046 !(ch_getflags() & CH_HELPFILE)) 2047 quit(QUIT_OK); 2048 parg.p_string = (number > 1) ? "(N-th) " : ""; 2049 error("No %snext file", &parg); 2050 } 2051 break; 2052 2053 case A_PREV_FILE: 2054 /* 2055 * Examine previous file. 2056 */ 2057 #if TAGS 2058 if (ntags()) 2059 { 2060 error("No previous file", NULL_PARG); 2061 break; 2062 } 2063 #endif 2064 if (number <= 0) 2065 number = 1; 2066 cmd_exec(); 2067 if (edit_prev((int) number)) 2068 { 2069 parg.p_string = (number > 1) ? "(N-th) " : ""; 2070 error("No %sprevious file", &parg); 2071 } 2072 break; 2073 2074 case A_NEXT_TAG: 2075 /* 2076 * Jump to the next tag in the current tag list. 2077 */ 2078 #if TAGS 2079 if (number <= 0) 2080 number = 1; 2081 tagfile = nexttag((int) number); 2082 if (tagfile == NULL) 2083 { 2084 error("No next tag", NULL_PARG); 2085 break; 2086 } 2087 cmd_exec(); 2088 if (edit(tagfile) == 0) 2089 { 2090 POSITION pos = tagsearch(); 2091 if (pos != NULL_POSITION) 2092 jump_loc(pos, jump_sline); 2093 } 2094 #else 2095 error("Command not available", NULL_PARG); 2096 #endif 2097 break; 2098 2099 case A_PREV_TAG: 2100 /* 2101 * Jump to the previous tag in the current tag list. 2102 */ 2103 #if TAGS 2104 if (number <= 0) 2105 number = 1; 2106 tagfile = prevtag((int) number); 2107 if (tagfile == NULL) 2108 { 2109 error("No previous tag", NULL_PARG); 2110 break; 2111 } 2112 cmd_exec(); 2113 if (edit(tagfile) == 0) 2114 { 2115 POSITION pos = tagsearch(); 2116 if (pos != NULL_POSITION) 2117 jump_loc(pos, jump_sline); 2118 } 2119 #else 2120 error("Command not available", NULL_PARG); 2121 #endif 2122 break; 2123 2124 case A_INDEX_FILE: 2125 /* 2126 * Examine a particular file. 2127 */ 2128 if (number <= 0) 2129 number = 1; 2130 cmd_exec(); 2131 if (edit_index((int) number)) 2132 error("No such file", NULL_PARG); 2133 break; 2134 2135 case A_REMOVE_FILE: 2136 /* 2137 * Remove a file from the input file list. 2138 */ 2139 if (ch_getflags() & CH_HELPFILE) 2140 break; 2141 old_ifile = curr_ifile; 2142 new_ifile = getoff_ifile(curr_ifile); 2143 cmd_exec(); 2144 if (new_ifile == NULL_IFILE) 2145 { 2146 bell(); 2147 break; 2148 } 2149 if (edit_ifile(new_ifile) != 0) 2150 { 2151 reedit_ifile(old_ifile); 2152 break; 2153 } 2154 del_ifile(old_ifile); 2155 break; 2156 2157 case A_OPT_TOGGLE: 2158 /* 2159 * Change the setting of an option. 2160 */ 2161 optflag = OPT_TOGGLE; 2162 optgetname = FALSE; 2163 mca_opt_toggle(); 2164 c = getcc(); 2165 msg = opt_toggle_disallowed(c); 2166 if (msg != NULL) 2167 { 2168 error(msg, NULL_PARG); 2169 break; 2170 } 2171 goto again; 2172 2173 case A_DISP_OPTION: 2174 /* 2175 * Report the setting of an option. 2176 */ 2177 optflag = OPT_NO_TOGGLE; 2178 optgetname = FALSE; 2179 mca_opt_toggle(); 2180 c = getcc(); 2181 goto again; 2182 2183 case A_FIRSTCMD: 2184 /* 2185 * Set an initial command for new files. 2186 */ 2187 start_mca(A_FIRSTCMD, "+", NULL, 0); 2188 c = getcc(); 2189 goto again; 2190 2191 case A_SHELL: 2192 case A_PSHELL: 2193 /* 2194 * Shell escape. 2195 */ 2196 #if SHELL_ESCAPE 2197 if (secure_allow(SF_SHELL)) 2198 { 2199 start_mca(action, (action == A_SHELL) ? "!" : "#", ml_shell, 0); 2200 c = getcc(); 2201 goto again; 2202 } 2203 #endif 2204 error("Command not available", NULL_PARG); 2205 break; 2206 2207 case A_SETMARK: 2208 case A_SETMARKBOT: 2209 /* 2210 * Set a mark. 2211 */ 2212 if (ch_getflags() & CH_HELPFILE) 2213 { 2214 if (ungot != NULL) 2215 { 2216 /* 2217 * Probably from a lesskey file, in which case there 2218 * is probably an ungotten letter from the "extra" string. 2219 * Eat it so it is not interpreted as a command. 2220 */ 2221 (void) getcc(); 2222 } 2223 break; 2224 } 2225 start_mca(A_SETMARK, "set mark: ", NULL, 0); 2226 c = getcc(); 2227 if (is_erase_char(c) || is_newline_char(c)) 2228 break; 2229 setmark(c, action == A_SETMARKBOT ? BOTTOM : TOP); 2230 repaint(); 2231 break; 2232 2233 case A_CLRMARK: 2234 /* 2235 * Clear a mark. 2236 */ 2237 start_mca(A_CLRMARK, "clear mark: ", NULL, 0); 2238 c = getcc(); 2239 if (is_erase_char(c) || is_newline_char(c)) 2240 break; 2241 clrmark(c); 2242 repaint(); 2243 break; 2244 2245 case A_GOMARK: 2246 /* 2247 * Jump to a marked position. 2248 */ 2249 start_mca(A_GOMARK, "goto mark: ", NULL, 0); 2250 c = getcc(); 2251 if (is_erase_char(c) || is_newline_char(c)) 2252 break; 2253 cmd_exec(); 2254 gomark(c); 2255 break; 2256 2257 case A_PIPE: 2258 /* 2259 * Write part of the input to a pipe to a shell command. 2260 */ 2261 #if PIPEC 2262 if (secure_allow(SF_PIPE)) 2263 { 2264 start_mca(A_PIPE, "|mark: ", NULL, 0); 2265 c = getcc(); 2266 if (is_erase_char(c)) 2267 break; 2268 if (is_newline_char(c)) 2269 c = '.'; 2270 if (badmark(c)) 2271 break; 2272 pipec = c; 2273 start_mca(A_PIPE, "!", ml_shell, 0); 2274 c = getcc(); 2275 goto again; 2276 } 2277 #endif 2278 error("Command not available", NULL_PARG); 2279 break; 2280 2281 case A_B_BRACKET: 2282 case A_F_BRACKET: 2283 start_mca(action, "Brackets: ", NULL, 0); 2284 c = getcc(); 2285 goto again; 2286 2287 case A_LSHIFT: 2288 /* 2289 * Shift view left. 2290 */ 2291 if (number > 0) 2292 shift_count = (int) number; 2293 else 2294 number = (shift_count > 0) ? shift_count : sc_width / 2; 2295 if (number > hshift) 2296 number = hshift; 2297 pos_rehead(); 2298 hshift -= (int) number; 2299 screen_trashed(); 2300 cmd_exec(); 2301 break; 2302 2303 case A_RSHIFT: 2304 /* 2305 * Shift view right. 2306 */ 2307 if (number > 0) 2308 shift_count = (int) number; 2309 else 2310 number = (shift_count > 0) ? shift_count : sc_width / 2; 2311 pos_rehead(); 2312 hshift += (int) number; 2313 screen_trashed(); 2314 cmd_exec(); 2315 break; 2316 2317 case A_LLSHIFT: 2318 /* 2319 * Shift view left to margin. 2320 */ 2321 pos_rehead(); 2322 hshift = 0; 2323 screen_trashed(); 2324 cmd_exec(); 2325 break; 2326 2327 case A_RRSHIFT: 2328 /* 2329 * Shift view right to view rightmost char on screen. 2330 */ 2331 pos_rehead(); 2332 hshift = rrshift(); 2333 screen_trashed(); 2334 cmd_exec(); 2335 break; 2336 2337 case A_PREFIX: 2338 /* 2339 * The command is incomplete (more chars are needed). 2340 * Display the current char, so the user knows 2341 * what's going on, and get another character. 2342 */ 2343 if (mca != A_PREFIX) 2344 { 2345 cmd_reset(); 2346 start_mca(A_PREFIX, " ", NULL, CF_QUIT_ON_ERASE); 2347 (void) cmd_char(c); 2348 } 2349 c = getcc(); 2350 goto again; 2351 2352 case A_NOACTION: 2353 break; 2354 2355 default: 2356 bell(); 2357 break; 2358 } 2359 } 2360 } 2361