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