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