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