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