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