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