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