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