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