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