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