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 * Discard any buffered file data. 562 */ 563 static void 564 clear_buffers() 565 { 566 if (!(ch_getflags() & CH_CANSEEK)) 567 return; 568 ch_flush(); 569 clr_linenum(); 570 #if HILITE_SEARCH 571 clr_hilite(); 572 #endif 573 } 574 575 /* 576 * Make sure the screen is displayed. 577 */ 578 static void 579 make_display() 580 { 581 /* 582 * If nothing is displayed yet, display starting from initial_scrpos. 583 */ 584 if (empty_screen()) 585 { 586 if (initial_scrpos.pos == NULL_POSITION) 587 /* 588 * {{ Maybe this should be: 589 * jump_loc(ch_zero(), jump_sline); 590 * but this behavior seems rather unexpected 591 * on the first screen. }} 592 */ 593 jump_loc(ch_zero(), 1); 594 else 595 jump_loc(initial_scrpos.pos, initial_scrpos.ln); 596 } else if (screen_trashed) 597 { 598 int save_top_scroll = top_scroll; 599 int save_ignore_eoi = ignore_eoi; 600 top_scroll = 1; 601 ignore_eoi = 0; 602 if (screen_trashed == 2) 603 { 604 /* Special case used by ignore_eoi: re-open the input file 605 * and jump to the end of the file. */ 606 reopen_curr_ifile(); 607 jump_forw(); 608 } 609 repaint(); 610 top_scroll = save_top_scroll; 611 ignore_eoi = save_ignore_eoi; 612 } 613 } 614 615 /* 616 * Display the appropriate prompt. 617 */ 618 static void 619 prompt() 620 { 621 register char *p; 622 623 if (ungotp != NULL && ungotp > ungot) 624 { 625 /* 626 * No prompt necessary if commands are from 627 * ungotten chars rather than from the user. 628 */ 629 return; 630 } 631 632 /* 633 * Make sure the screen is displayed. 634 */ 635 make_display(); 636 bottompos = position(BOTTOM_PLUS_ONE); 637 638 /* 639 * If we've hit EOF on the last file, and the -E flag is set 640 * (or -F is set and this is the first prompt), then quit. 641 * {{ Relying on "first prompt" to detect a single-screen file 642 * fails if +G is used, for example. }} 643 */ 644 if ((get_quit_at_eof() == OPT_ONPLUS || quit_if_one_screen) && 645 hit_eof && !(ch_getflags() & CH_HELPFILE) && 646 next_ifile(curr_ifile) == NULL_IFILE) 647 quit(QUIT_OK); 648 quit_if_one_screen = FALSE; 649 #if 0 /* This doesn't work well because some "te"s clear the screen. */ 650 /* 651 * If the -e flag is set and we've hit EOF on the last file, 652 * and the file is squished (shorter than the screen), quit. 653 */ 654 if (get_quit_at_eof() && squished && 655 next_ifile(curr_ifile) == NULL_IFILE) 656 quit(QUIT_OK); 657 #endif 658 659 #if MSDOS_COMPILER==WIN32C 660 /* 661 * In Win32, display the file name in the window title. 662 */ 663 if (!(ch_getflags() & CH_HELPFILE)) 664 SetConsoleTitle(pr_expand("Less?f - %f.", 0)); 665 #endif 666 /* 667 * Select the proper prompt and display it. 668 */ 669 /* 670 * If the previous action was a forward movement, 671 * don't clear the bottom line of the display; 672 * just print the prompt since the forward movement guarantees 673 * that we're in the right position to display the prompt. 674 * Clearing the line could cause a problem: for example, if the last 675 * line displayed ended at the right screen edge without a newline, 676 * then clearing would clear the last displayed line rather than 677 * the prompt line. 678 */ 679 if (!forw_prompt) 680 clear_bot(); 681 clear_cmd(); 682 forw_prompt = 0; 683 p = pr_string(); 684 if (p == NULL || *p == '\0') 685 putchr(':'); 686 else 687 { 688 at_enter(AT_STANDOUT); 689 putstr(p); 690 at_exit(); 691 } 692 clear_eol(); 693 } 694 695 /* 696 * Display the less version message. 697 */ 698 public void 699 dispversion() 700 { 701 PARG parg; 702 703 parg.p_string = version; 704 error("less %s", &parg); 705 } 706 707 /* 708 * Get command character. 709 * The character normally comes from the keyboard, 710 * but may come from ungotten characters 711 * (characters previously given to ungetcc or ungetsc). 712 */ 713 public int 714 getcc() 715 { 716 if (ungotp == NULL) 717 /* 718 * Normal case: no ungotten chars, so get one from the user. 719 */ 720 return (getchr()); 721 722 if (ungotp > ungot) 723 /* 724 * Return the next ungotten char. 725 */ 726 return (*--ungotp); 727 728 /* 729 * We have just run out of ungotten chars. 730 */ 731 ungotp = NULL; 732 if (len_cmdbuf() == 0 || !empty_screen()) 733 return (getchr()); 734 /* 735 * Command is incomplete, so try to complete it. 736 */ 737 switch (mca) 738 { 739 case A_DIGIT: 740 /* 741 * We have a number but no command. Treat as #g. 742 */ 743 return ('g'); 744 745 case A_F_SEARCH: 746 case A_B_SEARCH: 747 /* 748 * We have "/string" but no newline. Add the \n. 749 */ 750 return ('\n'); 751 752 default: 753 /* 754 * Some other incomplete command. Let user complete it. 755 */ 756 return (getchr()); 757 } 758 } 759 760 /* 761 * "Unget" a command character. 762 * The next getcc() will return this character. 763 */ 764 public void 765 ungetcc(c) 766 int c; 767 { 768 if (ungotp == NULL) 769 ungotp = ungot; 770 if (ungotp >= ungot + sizeof(ungot)) 771 { 772 error("ungetcc overflow", NULL_PARG); 773 quit(QUIT_ERROR); 774 } 775 *ungotp++ = c; 776 } 777 778 /* 779 * Unget a whole string of command characters. 780 * The next sequence of getcc()'s will return this string. 781 */ 782 public void 783 ungetsc(s) 784 char *s; 785 { 786 register char *p; 787 788 for (p = s + strlen(s) - 1; p >= s; p--) 789 ungetcc(*p); 790 } 791 792 /* 793 * Search for a pattern, possibly in multiple files. 794 * If SRCH_FIRST_FILE is set, begin searching at the first file. 795 * If SRCH_PAST_EOF is set, continue the search thru multiple files. 796 */ 797 static void 798 multi_search(pattern, n) 799 char *pattern; 800 int n; 801 { 802 register int nomore; 803 IFILE save_ifile; 804 int changed_file; 805 806 changed_file = 0; 807 save_ifile = save_curr_ifile(); 808 809 if (search_type & SRCH_FIRST_FILE) 810 { 811 /* 812 * Start at the first (or last) file 813 * in the command line list. 814 */ 815 if (search_type & SRCH_FORW) 816 nomore = edit_first(); 817 else 818 nomore = edit_last(); 819 if (nomore) 820 { 821 unsave_ifile(save_ifile); 822 return; 823 } 824 changed_file = 1; 825 search_type &= ~SRCH_FIRST_FILE; 826 } 827 828 for (;;) 829 { 830 n = search(search_type, pattern, n); 831 /* 832 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared 833 * after being used once. This allows "n" to work after 834 * using a /@@ search. 835 */ 836 search_type &= ~SRCH_NO_MOVE; 837 if (n == 0) 838 { 839 /* 840 * Found it. 841 */ 842 unsave_ifile(save_ifile); 843 return; 844 } 845 846 if (n < 0) 847 /* 848 * Some kind of error in the search. 849 * Error message has been printed by search(). 850 */ 851 break; 852 853 if ((search_type & SRCH_PAST_EOF) == 0) 854 /* 855 * We didn't find a match, but we're 856 * supposed to search only one file. 857 */ 858 break; 859 /* 860 * Move on to the next file. 861 */ 862 if (search_type & SRCH_FORW) 863 nomore = edit_next(1); 864 else 865 nomore = edit_prev(1); 866 if (nomore) 867 break; 868 changed_file = 1; 869 } 870 871 /* 872 * Didn't find it. 873 * Print an error message if we haven't already. 874 */ 875 if (n > 0) 876 error("Pattern not found", NULL_PARG); 877 878 if (changed_file) 879 { 880 /* 881 * Restore the file we were originally viewing. 882 */ 883 reedit_ifile(save_ifile); 884 } else 885 { 886 unsave_ifile(save_ifile); 887 } 888 } 889 890 /* 891 * Main command processor. 892 * Accept and execute commands until a quit command. 893 */ 894 public void 895 commands() 896 { 897 register int c; 898 register int action; 899 register char *cbuf; 900 int newaction; 901 int save_search_type; 902 char *extra; 903 char tbuf[2]; 904 PARG parg; 905 IFILE old_ifile; 906 IFILE new_ifile; 907 char *tagfile; 908 909 search_type = SRCH_FORW; 910 wscroll = (sc_height + 1) / 2; 911 newaction = A_NOACTION; 912 913 for (;;) 914 { 915 mca = 0; 916 cmd_accept(); 917 number = 0; 918 optchar = '\0'; 919 920 /* 921 * See if any signals need processing. 922 */ 923 if (sigs) 924 { 925 psignals(); 926 if (quitting) 927 quit(QUIT_SAVED_STATUS); 928 } 929 930 /* 931 * See if window size changed, for systems that don't 932 * generate SIGWINCH. 933 */ 934 check_winch(); 935 936 /* 937 * Display prompt and accept a character. 938 */ 939 cmd_reset(); 940 prompt(); 941 if (sigs) 942 continue; 943 if (newaction == A_NOACTION) 944 c = getcc(); 945 946 again: 947 if (sigs) 948 continue; 949 950 if (newaction != A_NOACTION) 951 { 952 action = newaction; 953 newaction = A_NOACTION; 954 } else 955 { 956 /* 957 * If we are in a multicharacter command, call mca_char. 958 * Otherwise we call fcmd_decode to determine the 959 * action to be performed. 960 */ 961 if (mca) 962 switch (mca_char(c)) 963 { 964 case MCA_MORE: 965 /* 966 * Need another character. 967 */ 968 c = getcc(); 969 goto again; 970 case MCA_DONE: 971 /* 972 * Command has been handled by mca_char. 973 * Start clean with a prompt. 974 */ 975 continue; 976 case NO_MCA: 977 /* 978 * Not a multi-char command 979 * (at least, not anymore). 980 */ 981 break; 982 } 983 984 /* 985 * Decode the command character and decide what to do. 986 */ 987 if (mca) 988 { 989 /* 990 * We're in a multichar command. 991 * Add the character to the command buffer 992 * and display it on the screen. 993 * If the user backspaces past the start 994 * of the line, abort the command. 995 */ 996 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) 997 continue; 998 cbuf = get_cmdbuf(); 999 } else 1000 { 1001 /* 1002 * Don't use cmd_char if we're starting fresh 1003 * at the beginning of a command, because we 1004 * don't want to echo the command until we know 1005 * it is a multichar command. We also don't 1006 * want erase_char/kill_char to be treated 1007 * as line editing characters. 1008 */ 1009 tbuf[0] = c; 1010 tbuf[1] = '\0'; 1011 cbuf = tbuf; 1012 } 1013 extra = NULL; 1014 action = fcmd_decode(cbuf, &extra); 1015 /* 1016 * If an "extra" string was returned, 1017 * process it as a string of command characters. 1018 */ 1019 if (extra != NULL) 1020 ungetsc(extra); 1021 } 1022 /* 1023 * Clear the cmdbuf string. 1024 * (But not if we're in the prefix of a command, 1025 * because the partial command string is kept there.) 1026 */ 1027 if (action != A_PREFIX) 1028 cmd_reset(); 1029 1030 switch (action) 1031 { 1032 case A_DIGIT: 1033 /* 1034 * First digit of a number. 1035 */ 1036 start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE); 1037 goto again; 1038 1039 case A_F_WINDOW: 1040 /* 1041 * Forward one window (and set the window size). 1042 */ 1043 if (number > 0) 1044 swindow = (int) number; 1045 /* FALLTHRU */ 1046 case A_F_SCREEN: 1047 /* 1048 * Forward one screen. 1049 */ 1050 if (number <= 0) 1051 number = get_swindow(); 1052 cmd_exec(); 1053 if (show_attn) 1054 set_attnpos(bottompos); 1055 forward((int) number, 0, 1); 1056 break; 1057 1058 case A_B_WINDOW: 1059 /* 1060 * Backward one window (and set the window size). 1061 */ 1062 if (number > 0) 1063 swindow = (int) number; 1064 /* FALLTHRU */ 1065 case A_B_SCREEN: 1066 /* 1067 * Backward one screen. 1068 */ 1069 if (number <= 0) 1070 number = get_swindow(); 1071 cmd_exec(); 1072 backward((int) number, 0, 1); 1073 break; 1074 1075 case A_F_LINE: 1076 /* 1077 * Forward N (default 1) line. 1078 */ 1079 if (number <= 0) 1080 number = 1; 1081 cmd_exec(); 1082 if (show_attn == OPT_ONPLUS && number > 1) 1083 set_attnpos(bottompos); 1084 forward((int) number, 0, 0); 1085 break; 1086 1087 case A_B_LINE: 1088 /* 1089 * Backward N (default 1) line. 1090 */ 1091 if (number <= 0) 1092 number = 1; 1093 cmd_exec(); 1094 backward((int) number, 0, 0); 1095 break; 1096 1097 case A_FF_LINE: 1098 /* 1099 * Force forward N (default 1) line. 1100 */ 1101 if (number <= 0) 1102 number = 1; 1103 cmd_exec(); 1104 if (show_attn == OPT_ONPLUS && number > 1) 1105 set_attnpos(bottompos); 1106 forward((int) number, 1, 0); 1107 break; 1108 1109 case A_BF_LINE: 1110 /* 1111 * Force backward N (default 1) line. 1112 */ 1113 if (number <= 0) 1114 number = 1; 1115 cmd_exec(); 1116 backward((int) number, 1, 0); 1117 break; 1118 1119 case A_FF_SCREEN: 1120 /* 1121 * Force forward one screen. 1122 */ 1123 if (number <= 0) 1124 number = get_swindow(); 1125 cmd_exec(); 1126 if (show_attn == OPT_ONPLUS) 1127 set_attnpos(bottompos); 1128 forward((int) number, 1, 0); 1129 break; 1130 1131 case A_F_FOREVER: 1132 /* 1133 * Forward forever, ignoring EOF. 1134 */ 1135 if (ch_getflags() & CH_HELPFILE) 1136 break; 1137 cmd_exec(); 1138 jump_forw(); 1139 ignore_eoi = 1; 1140 hit_eof = 0; 1141 while (!sigs) 1142 { 1143 make_display(); 1144 forward(1, 0, 0); 1145 } 1146 ignore_eoi = 0; 1147 /* 1148 * This gets us back in "F mode" after processing 1149 * a non-abort signal (e.g. window-change). 1150 */ 1151 if (sigs && !ABORT_SIGS()) 1152 newaction = A_F_FOREVER; 1153 break; 1154 1155 case A_F_SCROLL: 1156 /* 1157 * Forward N lines 1158 * (default same as last 'd' or 'u' command). 1159 */ 1160 if (number > 0) 1161 wscroll = (int) number; 1162 cmd_exec(); 1163 if (show_attn == OPT_ONPLUS) 1164 set_attnpos(bottompos); 1165 forward(wscroll, 0, 0); 1166 break; 1167 1168 case A_B_SCROLL: 1169 /* 1170 * Forward N lines 1171 * (default same as last 'd' or 'u' command). 1172 */ 1173 if (number > 0) 1174 wscroll = (int) number; 1175 cmd_exec(); 1176 backward(wscroll, 0, 0); 1177 break; 1178 1179 case A_FREPAINT: 1180 /* 1181 * Flush buffers, then repaint screen. 1182 * Don't flush the buffers on a pipe! 1183 */ 1184 clear_buffers(); 1185 /* FALLTHRU */ 1186 case A_REPAINT: 1187 /* 1188 * Repaint screen. 1189 */ 1190 cmd_exec(); 1191 repaint(); 1192 break; 1193 1194 case A_GOLINE: 1195 /* 1196 * Go to line N, default beginning of file. 1197 */ 1198 if (number <= 0) 1199 number = 1; 1200 cmd_exec(); 1201 jump_back(number); 1202 break; 1203 1204 case A_PERCENT: 1205 /* 1206 * Go to a specified percentage into the file. 1207 */ 1208 if (number < 0) 1209 { 1210 number = 0; 1211 fraction = 0; 1212 } 1213 if (number > 100) 1214 { 1215 number = 100; 1216 fraction = 0; 1217 } 1218 cmd_exec(); 1219 jump_percent((int) number, fraction); 1220 break; 1221 1222 case A_GOEND: 1223 /* 1224 * Go to line N, default end of file. 1225 */ 1226 cmd_exec(); 1227 if (number <= 0) 1228 jump_forw(); 1229 else 1230 jump_back(number); 1231 break; 1232 1233 case A_GOPOS: 1234 /* 1235 * Go to a specified byte position in the file. 1236 */ 1237 cmd_exec(); 1238 if (number < 0) 1239 number = 0; 1240 jump_line_loc((POSITION) number, jump_sline); 1241 break; 1242 1243 case A_STAT: 1244 /* 1245 * Print file name, etc. 1246 */ 1247 if (ch_getflags() & CH_HELPFILE) 1248 break; 1249 cmd_exec(); 1250 parg.p_string = eq_message(); 1251 error("%s", &parg); 1252 break; 1253 1254 case A_VERSION: 1255 /* 1256 * Print version number, without the "@(#)". 1257 */ 1258 cmd_exec(); 1259 dispversion(); 1260 break; 1261 1262 case A_QUIT: 1263 /* 1264 * Exit. 1265 */ 1266 if (curr_ifile != NULL_IFILE && 1267 ch_getflags() & CH_HELPFILE) 1268 { 1269 /* 1270 * Quit while viewing the help file 1271 * just means return to viewing the 1272 * previous file. 1273 */ 1274 hshift = save_hshift; 1275 if (edit_prev(1) == 0) 1276 break; 1277 } 1278 if (extra != NULL) 1279 quit(*extra); 1280 quit(QUIT_OK); 1281 break; 1282 1283 /* 1284 * Define abbreviation for a commonly used sequence below. 1285 */ 1286 #define DO_SEARCH() \ 1287 if (number <= 0) number = 1; \ 1288 mca_search(); \ 1289 cmd_exec(); \ 1290 multi_search((char *)NULL, (int) number); 1291 1292 1293 case A_F_SEARCH: 1294 /* 1295 * Search forward for a pattern. 1296 * Get the first char of the pattern. 1297 */ 1298 search_type = SRCH_FORW; 1299 if (number <= 0) 1300 number = 1; 1301 mca_search(); 1302 c = getcc(); 1303 goto again; 1304 1305 case A_B_SEARCH: 1306 /* 1307 * Search backward for a pattern. 1308 * Get the first char of the pattern. 1309 */ 1310 search_type = SRCH_BACK; 1311 if (number <= 0) 1312 number = 1; 1313 mca_search(); 1314 c = getcc(); 1315 goto again; 1316 1317 case A_AGAIN_SEARCH: 1318 /* 1319 * Repeat previous search. 1320 */ 1321 DO_SEARCH(); 1322 break; 1323 1324 case A_T_AGAIN_SEARCH: 1325 /* 1326 * Repeat previous search, multiple files. 1327 */ 1328 search_type |= SRCH_PAST_EOF; 1329 DO_SEARCH(); 1330 break; 1331 1332 case A_REVERSE_SEARCH: 1333 /* 1334 * Repeat previous search, in reverse direction. 1335 */ 1336 save_search_type = search_type; 1337 search_type = SRCH_REVERSE(search_type); 1338 DO_SEARCH(); 1339 search_type = save_search_type; 1340 break; 1341 1342 case A_T_REVERSE_SEARCH: 1343 /* 1344 * Repeat previous search, 1345 * multiple files in reverse direction. 1346 */ 1347 save_search_type = search_type; 1348 search_type = SRCH_REVERSE(search_type); 1349 search_type |= SRCH_PAST_EOF; 1350 DO_SEARCH(); 1351 search_type = save_search_type; 1352 break; 1353 1354 case A_UNDO_SEARCH: 1355 undo_search(); 1356 break; 1357 1358 case A_HELP: 1359 /* 1360 * Help. 1361 */ 1362 if (ch_getflags() & CH_HELPFILE) 1363 break; 1364 cmd_exec(); 1365 save_hshift = hshift; 1366 hshift = 0; 1367 (void) edit(FAKE_HELPFILE); 1368 break; 1369 1370 case A_EXAMINE: 1371 #if EXAMINE 1372 /* 1373 * Edit a new file. Get the filename. 1374 */ 1375 if (secure) 1376 { 1377 error("Command not available", NULL_PARG); 1378 break; 1379 } 1380 start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); 1381 c = getcc(); 1382 goto again; 1383 #else 1384 error("Command not available", NULL_PARG); 1385 break; 1386 #endif 1387 1388 case A_VISUAL: 1389 /* 1390 * Invoke an editor on the input file. 1391 */ 1392 #if EDITOR 1393 if (secure) 1394 { 1395 error("Command not available", NULL_PARG); 1396 break; 1397 } 1398 if (ch_getflags() & CH_HELPFILE) 1399 break; 1400 if (strcmp(get_filename(curr_ifile), "-") == 0) 1401 { 1402 error("Cannot edit standard input", NULL_PARG); 1403 break; 1404 } 1405 if (curr_altfilename != NULL) 1406 { 1407 error("WARNING: This file was viewed via LESSOPEN", 1408 NULL_PARG); 1409 } 1410 start_mca(A_SHELL, "!", ml_shell, 0); 1411 /* 1412 * Expand the editor prototype string 1413 * and pass it to the system to execute. 1414 * (Make sure the screen is displayed so the 1415 * expansion of "+%lm" works.) 1416 */ 1417 make_display(); 1418 cmd_exec(); 1419 lsystem(pr_expand(editproto, 0), (char*)NULL); 1420 break; 1421 #else 1422 error("Command not available", NULL_PARG); 1423 break; 1424 #endif 1425 1426 case A_NEXT_FILE: 1427 /* 1428 * Examine next file. 1429 */ 1430 #if TAGS 1431 if (ntags()) 1432 { 1433 error("No next file", NULL_PARG); 1434 break; 1435 } 1436 #endif 1437 if (number <= 0) 1438 number = 1; 1439 if (edit_next((int) number)) 1440 { 1441 if (get_quit_at_eof() && hit_eof && 1442 !(ch_getflags() & CH_HELPFILE)) 1443 quit(QUIT_OK); 1444 parg.p_string = (number > 1) ? "(N-th) " : ""; 1445 error("No %snext file", &parg); 1446 } 1447 break; 1448 1449 case A_PREV_FILE: 1450 /* 1451 * Examine previous file. 1452 */ 1453 #if TAGS 1454 if (ntags()) 1455 { 1456 error("No previous file", NULL_PARG); 1457 break; 1458 } 1459 #endif 1460 if (number <= 0) 1461 number = 1; 1462 if (edit_prev((int) number)) 1463 { 1464 parg.p_string = (number > 1) ? "(N-th) " : ""; 1465 error("No %sprevious file", &parg); 1466 } 1467 break; 1468 1469 case A_NEXT_TAG: 1470 #if TAGS 1471 if (number <= 0) 1472 number = 1; 1473 tagfile = nexttag((int) number); 1474 if (tagfile == NULL) 1475 { 1476 error("No next 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_PREV_TAG: 1491 #if TAGS 1492 if (number <= 0) 1493 number = 1; 1494 tagfile = prevtag((int) number); 1495 if (tagfile == NULL) 1496 { 1497 error("No previous tag", NULL_PARG); 1498 break; 1499 } 1500 if (edit(tagfile) == 0) 1501 { 1502 POSITION pos = tagsearch(); 1503 if (pos != NULL_POSITION) 1504 jump_loc(pos, jump_sline); 1505 } 1506 #else 1507 error("Command not available", NULL_PARG); 1508 #endif 1509 break; 1510 1511 case A_INDEX_FILE: 1512 /* 1513 * Examine a particular file. 1514 */ 1515 if (number <= 0) 1516 number = 1; 1517 if (edit_index((int) number)) 1518 error("No such file", NULL_PARG); 1519 break; 1520 1521 case A_REMOVE_FILE: 1522 if (ch_getflags() & CH_HELPFILE) 1523 break; 1524 old_ifile = curr_ifile; 1525 new_ifile = getoff_ifile(curr_ifile); 1526 if (new_ifile == NULL_IFILE) 1527 { 1528 bell(); 1529 break; 1530 } 1531 if (edit_ifile(new_ifile) != 0) 1532 { 1533 reedit_ifile(old_ifile); 1534 break; 1535 } 1536 del_ifile(old_ifile); 1537 break; 1538 1539 case A_OPT_TOGGLE: 1540 optflag = OPT_TOGGLE; 1541 optgetname = FALSE; 1542 mca_opt_toggle(); 1543 c = getcc(); 1544 goto again; 1545 1546 case A_DISP_OPTION: 1547 /* 1548 * Report a flag setting. 1549 */ 1550 optflag = OPT_NO_TOGGLE; 1551 optgetname = FALSE; 1552 mca_opt_toggle(); 1553 c = getcc(); 1554 goto again; 1555 1556 case A_FIRSTCMD: 1557 /* 1558 * Set an initial command for new files. 1559 */ 1560 start_mca(A_FIRSTCMD, "+", (void*)NULL, 0); 1561 c = getcc(); 1562 goto again; 1563 1564 case A_SHELL: 1565 /* 1566 * Shell escape. 1567 */ 1568 #if SHELL_ESCAPE 1569 if (secure) 1570 { 1571 error("Command not available", NULL_PARG); 1572 break; 1573 } 1574 start_mca(A_SHELL, "!", ml_shell, 0); 1575 c = getcc(); 1576 goto again; 1577 #else 1578 error("Command not available", NULL_PARG); 1579 break; 1580 #endif 1581 1582 case A_SETMARK: 1583 /* 1584 * Set a mark. 1585 */ 1586 if (ch_getflags() & CH_HELPFILE) 1587 break; 1588 start_mca(A_SETMARK, "mark: ", (void*)NULL, 0); 1589 c = getcc(); 1590 if (c == erase_char || c == erase2_char || 1591 c == kill_char || c == '\n' || c == '\r') 1592 break; 1593 setmark(c); 1594 break; 1595 1596 case A_GOMARK: 1597 /* 1598 * Go to a mark. 1599 */ 1600 start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); 1601 c = getcc(); 1602 if (c == erase_char || c == erase2_char || 1603 c == kill_char || c == '\n' || c == '\r') 1604 break; 1605 gomark(c); 1606 break; 1607 1608 case A_PIPE: 1609 #if PIPEC 1610 if (secure) 1611 { 1612 error("Command not available", NULL_PARG); 1613 break; 1614 } 1615 start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); 1616 c = getcc(); 1617 if (c == erase_char || c == erase2_char || c == kill_char) 1618 break; 1619 if (c == '\n' || c == '\r') 1620 c = '.'; 1621 if (badmark(c)) 1622 break; 1623 pipec = c; 1624 start_mca(A_PIPE, "!", ml_shell, 0); 1625 c = getcc(); 1626 goto again; 1627 #else 1628 error("Command not available", NULL_PARG); 1629 break; 1630 #endif 1631 1632 case A_B_BRACKET: 1633 case A_F_BRACKET: 1634 start_mca(action, "Brackets: ", (void*)NULL, 0); 1635 c = getcc(); 1636 goto again; 1637 1638 case A_LSHIFT: 1639 if (number > 0) 1640 shift_count = number; 1641 else 1642 number = (shift_count > 0) ? 1643 shift_count : sc_width / 2; 1644 if (number > hshift) 1645 number = hshift; 1646 hshift -= number; 1647 screen_trashed = 1; 1648 break; 1649 1650 case A_RSHIFT: 1651 if (number > 0) 1652 shift_count = number; 1653 else 1654 number = (shift_count > 0) ? 1655 shift_count : sc_width / 2; 1656 hshift += number; 1657 screen_trashed = 1; 1658 break; 1659 1660 case A_PREFIX: 1661 /* 1662 * The command is incomplete (more chars are needed). 1663 * Display the current char, so the user knows 1664 * what's going on, and get another character. 1665 */ 1666 if (mca != A_PREFIX) 1667 { 1668 cmd_reset(); 1669 start_mca(A_PREFIX, " ", (void*)NULL, 1670 CF_QUIT_ON_ERASE); 1671 (void) cmd_char(c); 1672 } 1673 c = getcc(); 1674 goto again; 1675 1676 case A_NOACTION: 1677 break; 1678 1679 default: 1680 bell(); 1681 break; 1682 } 1683 } 1684 } 1685