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