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