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