1 /* 2 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice in the documentation and/or other materials provided with 12 * the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 28 /* 29 * User-level command processor. 30 */ 31 32 #include "less.h" 33 #include "position.h" 34 #include "option.h" 35 #include "cmd.h" 36 37 extern int erase_char, kill_char; 38 extern int sigs; 39 extern int quit_at_eof; 40 extern int hit_eof; 41 extern int sc_width; 42 extern int sc_height; 43 extern int swindow; 44 extern int jump_sline; 45 extern int quitting; 46 extern int wscroll; 47 extern int nohelp; 48 extern int top_scroll; 49 extern int ignore_eoi; 50 extern char *every_first_cmd; 51 extern char *curr_altfilename; 52 extern char version[]; 53 extern struct scrpos initial_scrpos; 54 extern IFILE curr_ifile; 55 #if CMD_HISTORY 56 extern void *ml_search; 57 extern void *ml_examine; 58 #if SHELL_ESCAPE || PIPEC 59 extern void *ml_shell; 60 #endif 61 #else 62 /* No CMD_HISTORY */ 63 #define ml_search NULL 64 #define ml_examine NULL 65 #define ml_shell NULL 66 #endif 67 #if EDITOR 68 extern char *editor; 69 extern char *editproto; 70 #endif 71 extern int screen_trashed; /* The screen has been overwritten */ 72 extern int be_helpful; 73 74 public int helpprompt; 75 76 static char ungot[100]; 77 static char *ungotp = NULL; 78 #if SHELL_ESCAPE 79 static char *shellcmd = NULL; /* For holding last shell command for "!!" */ 80 #endif 81 static int mca; /* The multicharacter command (action) */ 82 static int search_type; /* The previous type of search */ 83 static int number; /* The number typed by the user */ 84 static char optchar; 85 static int optflag; 86 #if PIPEC 87 static char pipec; 88 #endif 89 90 static void multi_search(); 91 92 /* 93 * Move the cursor to lower left before executing a command. 94 * This looks nicer if the command takes a long time before 95 * updating the screen. 96 */ 97 static void 98 cmd_exec() 99 { 100 lower_left(); 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) 109 int action; 110 char *prompt; 111 void *mlist; 112 { 113 mca = action; 114 clear_bot(); 115 cmd_putstr(prompt); 116 #if CMD_HISTORY 117 set_mlist(mlist); 118 #endif 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 (search_type & SRCH_FORW) 134 mca = A_F_SEARCH; 135 else 136 mca = A_B_SEARCH; 137 138 clear_bot(); 139 140 if (search_type & SRCH_FIRST_FILE) 141 cmd_putstr("@"); 142 143 if (search_type & SRCH_PAST_EOF) 144 cmd_putstr("*"); 145 146 if (search_type & SRCH_NOMATCH) 147 cmd_putstr("!"); 148 149 if (search_type & SRCH_FORW) 150 cmd_putstr("/"); 151 else 152 cmd_putstr("?"); 153 #if CMD_HISTORY 154 set_mlist(ml_search); 155 #endif 156 } 157 158 /* 159 * Execute a multicharacter command. 160 */ 161 static void 162 exec_mca() 163 { 164 register char *cbuf; 165 166 cmd_exec(); 167 cbuf = get_cmdbuf(); 168 169 switch (mca) 170 { 171 case A_F_SEARCH: 172 case A_B_SEARCH: 173 multi_search(cbuf, number); 174 break; 175 case A_FIRSTCMD: 176 /* 177 * Skip leading spaces or + signs in the string. 178 */ 179 while (*cbuf == '+' || *cbuf == ' ') 180 cbuf++; 181 if (every_first_cmd != NULL) 182 free(every_first_cmd); 183 if (*cbuf == '\0') 184 every_first_cmd = NULL; 185 else 186 every_first_cmd = save(cbuf); 187 break; 188 case A_OPT_TOGGLE: 189 toggle_option(optchar, cbuf, optflag); 190 optchar = '\0'; 191 break; 192 case A_F_BRACKET: 193 match_brac(cbuf[0], cbuf[1], 1, number); 194 break; 195 case A_B_BRACKET: 196 match_brac(cbuf[1], cbuf[0], 0, number); 197 break; 198 #if EXAMINE 199 case A_EXAMINE: 200 edit_list(cbuf); 201 break; 202 #endif 203 #if SHELL_ESCAPE 204 case A_SHELL: 205 /* 206 * !! just uses whatever is in shellcmd. 207 * Otherwise, copy cmdbuf to shellcmd, 208 * expanding any special characters ("%" or "#"). 209 */ 210 if (*cbuf != '!') 211 { 212 if (shellcmd != NULL) 213 free(shellcmd); 214 shellcmd = fexpand(cbuf); 215 } 216 217 if (shellcmd == NULL) 218 lsystem(""); 219 else 220 lsystem(shellcmd); 221 error("!done", NULL_PARG); 222 break; 223 #endif 224 #if PIPEC 225 case A_PIPE: 226 (void) pipe_mark(pipec, cbuf); 227 error("|done", NULL_PARG); 228 break; 229 #endif 230 } 231 } 232 233 /* 234 * Add a character to a multi-character command. 235 */ 236 static int 237 mca_char(c) 238 int c; 239 { 240 char *p; 241 int flag; 242 char buf[3]; 243 244 switch (mca) 245 { 246 case 0: 247 /* 248 * Not in a multicharacter command. 249 */ 250 return (NO_MCA); 251 252 case A_PREFIX: 253 /* 254 * In the prefix of a command. 255 * This not considered a multichar command 256 * (even tho it uses cmdbuf, etc.). 257 * It is handled in the commands() switch. 258 */ 259 return (NO_MCA); 260 261 case A_DIGIT: 262 /* 263 * Entering digits of a number. 264 * Terminated by a non-digit. 265 */ 266 if ((c < '0' || c > '9') && 267 editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID) 268 { 269 /* 270 * Not part of the number. 271 * Treat as a normal command character. 272 */ 273 number = cmd_int(); 274 mca = 0; 275 cmd_accept(); 276 return (NO_MCA); 277 } 278 break; 279 280 case A_OPT_TOGGLE: 281 /* 282 * Special case for the TOGGLE_OPTION command. 283 * If the option letter which was entered is a 284 * single-char option, execute the command immediately, 285 * so user doesn't have to hit RETURN. 286 * If the first char is + or -, this indicates 287 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE. 288 */ 289 if (c == erase_char || c == kill_char) 290 break; 291 if (optchar != '\0' && optchar != '+' && optchar != '-') 292 /* 293 * We already have the option letter. 294 */ 295 break; 296 switch (c) 297 { 298 case '+': 299 optflag = OPT_UNSET; 300 break; 301 case '-': 302 optflag = OPT_SET; 303 break; 304 default: 305 optchar = c; 306 if (optflag != OPT_TOGGLE || single_char_option(c)) 307 { 308 toggle_option(c, "", optflag); 309 return (MCA_DONE); 310 } 311 break; 312 } 313 if (optchar == '+' || optchar == '-') 314 { 315 optchar = c; 316 break; 317 } 318 /* 319 * Display a prompt appropriate for the option letter. 320 */ 321 if ((p = opt_prompt(c)) == NULL) 322 { 323 buf[0] = '-'; 324 buf[1] = c; 325 buf[2] = '\0'; 326 p = buf; 327 } 328 start_mca(A_OPT_TOGGLE, p, (void*)NULL); 329 return (MCA_MORE); 330 331 case A_F_SEARCH: 332 case A_B_SEARCH: 333 /* 334 * Special case for search commands. 335 * Certain characters as the first char of 336 * the pattern have special meaning: 337 * ! Toggle the NOMATCH flag 338 * * Toggle the PAST_EOF flag 339 * @ Toggle the FIRST_FILE flag 340 */ 341 if (len_cmdbuf() > 0) 342 /* 343 * Only works for the first char of the pattern. 344 */ 345 break; 346 347 flag = 0; 348 switch (c) 349 { 350 case '!': 351 flag = SRCH_NOMATCH; 352 break; 353 case '@': 354 flag = SRCH_FIRST_FILE; 355 break; 356 case '*': 357 flag = SRCH_PAST_EOF; 358 break; 359 } 360 if (flag != 0) 361 { 362 search_type ^= flag; 363 mca_search(); 364 return (MCA_MORE); 365 } 366 break; 367 } 368 369 /* 370 * Any other multicharacter command 371 * is terminated by a newline. 372 */ 373 if (c == '\n' || c == '\r') 374 { 375 /* 376 * Execute the command. 377 */ 378 exec_mca(); 379 return (MCA_DONE); 380 } 381 /* 382 * Append the char to the command buffer. 383 */ 384 if (cmd_char(c) == CC_QUIT) 385 /* 386 * Abort the multi-char command. 387 */ 388 return (MCA_DONE); 389 390 if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2) 391 { 392 /* 393 * Special case for the bracket-matching commands. 394 * Execute the command after getting exactly two 395 * characters from the user. 396 */ 397 exec_mca(); 398 return (MCA_DONE); 399 } 400 401 /* 402 * Need another character. 403 */ 404 return (MCA_MORE); 405 } 406 407 /* 408 * Display the appropriate prompt. 409 */ 410 static void 411 prompt() 412 { 413 register char *p; 414 415 if (ungotp != NULL && ungotp > ungot) 416 { 417 /* 418 * No prompt necessary if commands are from 419 * ungotten chars rather than from the user. 420 */ 421 return; 422 } 423 424 /* 425 * If nothing is displayed yet, display starting from initial_scrpos. 426 */ 427 if (empty_screen()) 428 { 429 if (initial_scrpos.pos == NULL_POSITION) 430 /* 431 * {{ Maybe this should be: 432 * jump_loc(ch_zero(), jump_sline); 433 * but this behavior seems rather unexpected 434 * on the first screen. }} 435 */ 436 jump_loc(ch_zero(), 1); 437 else 438 jump_loc(initial_scrpos.pos, initial_scrpos.ln); 439 } else if (screen_trashed) 440 { 441 int save_top_scroll; 442 save_top_scroll = top_scroll; 443 top_scroll = 1; 444 repaint(); 445 top_scroll = save_top_scroll; 446 } 447 448 /* 449 * If the -E flag is set and we've hit EOF on the last file, quit. 450 */ 451 if (quit_at_eof == OPT_ONPLUS && hit_eof && 452 next_ifile(curr_ifile) == NULL_IFILE) 453 quit(QUIT_OK); 454 455 /* 456 * Select the proper prompt and display it. 457 */ 458 clear_bot(); 459 if (helpprompt) { 460 so_enter(); 461 putstr("[Press 'h' for instructions.]"); 462 so_exit(); 463 helpprompt = 0; 464 } else { 465 p = pr_string(); 466 if (p == NULL) 467 putchr(':'); 468 else 469 { 470 so_enter(); 471 putstr(p); 472 if (be_helpful) 473 putstr(" [Press space to continue, 'q' to quit.]"); 474 so_exit(); 475 } 476 } 477 } 478 479 public void 480 dispversion() 481 { 482 PARG parg; 483 484 parg.p_string = version; 485 error("less version %s", &parg); 486 } 487 488 /* 489 * Get command character. 490 * The character normally comes from the keyboard, 491 * but may come from ungotten characters 492 * (characters previously given to ungetcc or ungetsc). 493 */ 494 public int 495 getcc() 496 { 497 if (ungotp == NULL) 498 /* 499 * Normal case: no ungotten chars, so get one from the user. 500 */ 501 return (getchr()); 502 503 if (ungotp > ungot) 504 /* 505 * Return the next ungotten char. 506 */ 507 return (*--ungotp); 508 509 /* 510 * We have just run out of ungotten chars. 511 */ 512 ungotp = NULL; 513 if (len_cmdbuf() == 0 || !empty_screen()) 514 return (getchr()); 515 /* 516 * Command is incomplete, so try to complete it. 517 */ 518 switch (mca) 519 { 520 case A_DIGIT: 521 /* 522 * We have a number but no command. Treat as #g. 523 */ 524 return ('g'); 525 526 case A_F_SEARCH: 527 case A_B_SEARCH: 528 /* 529 * We have "/string" but no newline. Add the \n. 530 */ 531 return ('\n'); 532 533 default: 534 /* 535 * Some other incomplete command. Let user complete it. 536 */ 537 return (getchr()); 538 } 539 } 540 541 /* 542 * "Unget" a command character. 543 * The next getcc() will return this character. 544 */ 545 public void 546 ungetcc(c) 547 int c; 548 { 549 if (ungotp == NULL) 550 ungotp = ungot; 551 if (ungotp >= ungot + sizeof(ungot)) 552 { 553 error("ungetcc overflow", NULL_PARG); 554 quit(QUIT_ERROR); 555 } 556 *ungotp++ = c; 557 } 558 559 /* 560 * Unget a whole string of command characters. 561 * The next sequence of getcc()'s will return this string. 562 */ 563 public void 564 ungetsc(s) 565 char *s; 566 { 567 register char *p; 568 569 for (p = s + strlen(s) - 1; p >= s; p--) 570 ungetcc(*p); 571 } 572 573 /* 574 * Search for a pattern, possibly in multiple files. 575 * If SRCH_FIRST_FILE is set, begin searching at the first file. 576 * If SRCH_PAST_EOF is set, continue the search thru multiple files. 577 */ 578 static void 579 multi_search(pattern, n) 580 char *pattern; 581 int n; 582 { 583 register int nomore; 584 IFILE save_ifile; 585 int changed_file; 586 587 changed_file = 0; 588 save_ifile = curr_ifile; 589 590 if (search_type & SRCH_FIRST_FILE) 591 { 592 /* 593 * Start at the first (or last) file 594 * in the command line list. 595 */ 596 if (search_type & SRCH_FORW) 597 nomore = edit_first(); 598 else 599 nomore = edit_last(); 600 if (nomore) 601 return; 602 changed_file = 1; 603 search_type &= ~SRCH_FIRST_FILE; 604 } 605 606 for (;;) 607 { 608 if ((n = search(search_type, pattern, n)) == 0) 609 /* 610 * Found it. 611 */ 612 return; 613 614 if (n < 0) 615 /* 616 * Some kind of error in the search. 617 * Error message has been printed by search(). 618 */ 619 break; 620 621 if ((search_type & SRCH_PAST_EOF) == 0) 622 /* 623 * We didn't find a match, but we're 624 * supposed to search only one file. 625 */ 626 break; 627 /* 628 * Move on to the next file. 629 */ 630 if (search_type & SRCH_FORW) 631 nomore = edit_next(1); 632 else 633 nomore = edit_prev(1); 634 if (nomore) 635 break; 636 changed_file = 1; 637 } 638 639 /* 640 * Didn't find it. 641 * Print an error message if we haven't already. 642 */ 643 if (n > 0) 644 error("Pattern not found", NULL_PARG); 645 646 if (changed_file) 647 { 648 /* 649 * Restore the file we were originally viewing. 650 */ 651 if (edit_ifile(save_ifile)) 652 quit(QUIT_ERROR); 653 } 654 } 655 656 /* 657 * Main command processor. 658 * Accept and execute commands until a quit command. 659 */ 660 public void 661 commands() 662 { 663 register int c; 664 register int action; 665 register char *cbuf; 666 int save_search_type; 667 char *s; 668 char tbuf[2]; 669 PARG parg; 670 671 search_type = SRCH_FORW; 672 wscroll = (sc_height + 1) / 2; 673 674 for (;;) 675 { 676 mca = 0; 677 cmd_accept(); 678 number = 0; 679 optchar = '\0'; 680 681 /* 682 * See if any signals need processing. 683 */ 684 if (sigs) 685 { 686 psignals(); 687 if (quitting) 688 quit(QUIT_SAVED_STATUS); 689 } 690 691 /* 692 * Display prompt and accept a character. 693 */ 694 cmd_reset(); 695 prompt(); 696 if (sigs) 697 continue; 698 c = getcc(); 699 700 again: 701 if (sigs) 702 continue; 703 704 /* 705 * If we are in a multicharacter command, call mca_char. 706 * Otherwise we call fcmd_decode to determine the 707 * action to be performed. 708 */ 709 if (mca) 710 switch (mca_char(c)) 711 { 712 case MCA_MORE: 713 /* 714 * Need another character. 715 */ 716 c = getcc(); 717 goto again; 718 case MCA_DONE: 719 /* 720 * Command has been handled by mca_char. 721 * Start clean with a prompt. 722 */ 723 continue; 724 case NO_MCA: 725 /* 726 * Not a multi-char command 727 * (at least, not anymore). 728 */ 729 break; 730 } 731 732 /* 733 * Decode the command character and decide what to do. 734 */ 735 if (mca) 736 { 737 /* 738 * We're in a multichar command. 739 * Add the character to the command buffer 740 * and display it on the screen. 741 * If the user backspaces past the start 742 * of the line, abort the command. 743 */ 744 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) 745 continue; 746 cbuf = get_cmdbuf(); 747 } else 748 { 749 /* 750 * Don't use cmd_char if we're starting fresh 751 * at the beginning of a command, because we 752 * don't want to echo the command until we know 753 * it is a multichar command. We also don't 754 * want erase_char/kill_char to be treated 755 * as line editing characters. 756 */ 757 tbuf[0] = c; 758 tbuf[1] = '\0'; 759 cbuf = tbuf; 760 } 761 s = NULL; 762 action = fcmd_decode(cbuf, &s); 763 /* 764 * If an "extra" string was returned, 765 * process it as a string of command characters. 766 */ 767 if (s != NULL) 768 ungetsc(s); 769 /* 770 * Clear the cmdbuf string. 771 * (But not if we're in the prefix of a command, 772 * because the partial command string is kept there.) 773 */ 774 if (action != A_PREFIX) 775 cmd_reset(); 776 777 switch (action) 778 { 779 case A_DIGIT: 780 /* 781 * First digit of a number. 782 */ 783 start_mca(A_DIGIT, ":", (void*)NULL); 784 goto again; 785 786 case A_F_WINDOW: 787 /* 788 * Forward one window (and set the window size). 789 */ 790 if (number > 0) 791 swindow = number; 792 /* FALLTHRU */ 793 case A_F_SCREEN: 794 /* 795 * Forward one screen. 796 */ 797 if (number <= 0) 798 number = get_swindow(); 799 cmd_exec(); 800 forward(number, 0, 1); 801 break; 802 803 case A_B_WINDOW: 804 /* 805 * Backward one window (and set the window size). 806 */ 807 if (number > 0) 808 swindow = number; 809 /* FALLTHRU */ 810 case A_B_SCREEN: 811 /* 812 * Backward one screen. 813 */ 814 if (number <= 0) 815 number = get_swindow(); 816 cmd_exec(); 817 backward(number, 0, 1); 818 break; 819 820 case A_F_LINE: 821 /* 822 * Forward N (default 1) line. 823 */ 824 if (number <= 0) 825 number = 1; 826 cmd_exec(); 827 forward(number, 0, 0); 828 break; 829 830 case A_B_LINE: 831 /* 832 * Backward N (default 1) line. 833 */ 834 if (number <= 0) 835 number = 1; 836 cmd_exec(); 837 backward(number, 0, 0); 838 break; 839 840 case A_FF_LINE: 841 /* 842 * Force forward N (default 1) line. 843 */ 844 if (number <= 0) 845 number = 1; 846 cmd_exec(); 847 forward(number, 1, 0); 848 break; 849 850 case A_BF_LINE: 851 /* 852 * Force backward N (default 1) line. 853 */ 854 if (number <= 0) 855 number = 1; 856 cmd_exec(); 857 backward(number, 1, 0); 858 break; 859 860 case A_F_FOREVER: 861 /* 862 * Forward forever, ignoring EOF. 863 */ 864 cmd_exec(); 865 jump_forw(); 866 ignore_eoi = 1; 867 hit_eof = 0; 868 while (!ABORT_SIGS()) 869 forward(1, 0, 0); 870 ignore_eoi = 0; 871 break; 872 873 case A_F_SCROLL: 874 /* 875 * Forward N lines 876 * (default same as last 'd' or 'u' command). 877 */ 878 if (number > 0) 879 wscroll = number; 880 cmd_exec(); 881 forward(wscroll, 0, 0); 882 break; 883 884 case A_B_SCROLL: 885 /* 886 * Forward N lines 887 * (default same as last 'd' or 'u' command). 888 */ 889 if (number > 0) 890 wscroll = number; 891 cmd_exec(); 892 backward(wscroll, 0, 0); 893 break; 894 895 case A_FREPAINT: 896 /* 897 * Flush buffers, then repaint screen. 898 * Don't flush the buffers on a pipe! 899 */ 900 if (ch_getflags() & CH_CANSEEK) 901 { 902 ch_flush(); 903 clr_linenum(); 904 } 905 /* FALLTHRU */ 906 case A_REPAINT: 907 /* 908 * Repaint screen. 909 */ 910 cmd_exec(); 911 repaint(); 912 break; 913 914 case A_GOLINE: 915 /* 916 * Go to line N, default beginning of file. 917 */ 918 if (number <= 0) 919 number = 1; 920 cmd_exec(); 921 jump_back(number); 922 break; 923 924 case A_PERCENT: 925 /* 926 * Go to a specified percentage into the file. 927 */ 928 if (number < 0) 929 number = 0; 930 if (number > 100) 931 number = 100; 932 cmd_exec(); 933 jump_percent(number); 934 break; 935 936 case A_GOEND: 937 /* 938 * Go to line N, default end of file. 939 */ 940 cmd_exec(); 941 if (number <= 0) 942 jump_forw(); 943 else 944 jump_back(number); 945 break; 946 947 case A_GOPOS: 948 /* 949 * Go to a specified byte position in the file. 950 */ 951 cmd_exec(); 952 if (number < 0) 953 number = 0; 954 jump_line_loc((POSITION)number, jump_sline); 955 break; 956 957 case A_STAT: 958 /* 959 * Print file name, etc. 960 */ 961 cmd_exec(); 962 parg.p_string = eq_message(); 963 error("%s", &parg); 964 break; 965 966 case A_VERSION: 967 /* 968 * Print version number, without the "@(#)". 969 */ 970 cmd_exec(); 971 dispversion(); 972 break; 973 974 case A_QUIT: 975 /* 976 * Exit. 977 */ 978 quit(QUIT_OK); 979 980 /* 981 * Define abbreviation for a commonly used sequence below. 982 */ 983 #define DO_SEARCH() if (number <= 0) number = 1; \ 984 mca_search(); \ 985 cmd_exec(); \ 986 multi_search((char *)NULL, number); 987 988 989 case A_F_SEARCH: 990 /* 991 * Search forward for a pattern. 992 * Get the first char of the pattern. 993 */ 994 search_type = SRCH_FORW; 995 if (number <= 0) 996 number = 1; 997 mca_search(); 998 c = getcc(); 999 goto again; 1000 1001 case A_B_SEARCH: 1002 /* 1003 * Search backward for a pattern. 1004 * Get the first char of the pattern. 1005 */ 1006 search_type = SRCH_BACK; 1007 if (number <= 0) 1008 number = 1; 1009 mca_search(); 1010 c = getcc(); 1011 goto again; 1012 1013 case A_AGAIN_SEARCH: 1014 /* 1015 * Repeat previous search. 1016 */ 1017 DO_SEARCH(); 1018 break; 1019 1020 case A_T_AGAIN_SEARCH: 1021 /* 1022 * Repeat previous search, multiple files. 1023 */ 1024 search_type |= SRCH_PAST_EOF; 1025 DO_SEARCH(); 1026 break; 1027 1028 case A_REVERSE_SEARCH: 1029 /* 1030 * Repeat previous search, in reverse direction. 1031 */ 1032 save_search_type = search_type; 1033 search_type = SRCH_REVERSE(search_type); 1034 DO_SEARCH(); 1035 search_type = save_search_type; 1036 break; 1037 1038 case A_T_REVERSE_SEARCH: 1039 /* 1040 * Repeat previous search, 1041 * multiple files in reverse direction. 1042 */ 1043 save_search_type = search_type; 1044 search_type = SRCH_REVERSE(search_type); 1045 search_type |= SRCH_PAST_EOF; 1046 DO_SEARCH(); 1047 search_type = save_search_type; 1048 break; 1049 1050 case A_UNDO_SEARCH: 1051 undo_search(); 1052 break; 1053 1054 case A_HELP: 1055 /* 1056 * Help. 1057 */ 1058 if (nohelp) 1059 { 1060 bell(); 1061 break; 1062 } 1063 clear_bot(); 1064 putstr(" help"); 1065 cmd_exec(); 1066 help(0); 1067 break; 1068 1069 case A_EXAMINE: 1070 #if EXAMINE 1071 /* 1072 * Edit a new file. Get the filename. 1073 */ 1074 start_mca(A_EXAMINE, "Examine: ", ml_examine); 1075 c = getcc(); 1076 goto again; 1077 #else 1078 error("Command not available", NULL_PARG); 1079 break; 1080 #endif 1081 1082 case A_VISUAL: 1083 /* 1084 * Invoke an editor on the input file. 1085 */ 1086 #if EDITOR 1087 if (strcmp(get_filename(curr_ifile), "-") == 0) 1088 { 1089 error("Cannot edit standard input", NULL_PARG); 1090 break; 1091 } 1092 if (curr_altfilename != NULL) 1093 { 1094 error("Cannot edit file processed with LESSOPEN", 1095 NULL_PARG); 1096 break; 1097 } 1098 /* 1099 * Expand the editor prototype string 1100 * and pass it to the system to execute. 1101 */ 1102 cmd_exec(); 1103 lsystem(pr_expand(editproto, 0)); 1104 /* 1105 * Re-edit the file, since data may have changed. 1106 * Some editors even recreate the file, so flushing 1107 * buffers is not sufficient. 1108 */ 1109 if (edit_ifile(curr_ifile)) 1110 quit(QUIT_ERROR); 1111 break; 1112 #else 1113 error("Command not available", NULL_PARG); 1114 break; 1115 #endif 1116 1117 case A_NEXT_FILE: 1118 /* 1119 * Examine next file. 1120 */ 1121 if (number <= 0) 1122 number = 1; 1123 if (edit_next(number)) 1124 { 1125 if (quit_at_eof && hit_eof) 1126 quit(QUIT_OK); 1127 parg.p_string = (number > 1) ? "(N-th) " : ""; 1128 error("No %snext file", &parg); 1129 } 1130 break; 1131 1132 case A_PREV_FILE: 1133 /* 1134 * Examine previous file. 1135 */ 1136 if (number <= 0) 1137 number = 1; 1138 if (edit_prev(number)) 1139 { 1140 parg.p_string = (number > 1) ? "(N-th) " : ""; 1141 error("No %sprevious file", &parg); 1142 } 1143 break; 1144 1145 case A_INDEX_FILE: 1146 /* 1147 * Examine a particular file. 1148 */ 1149 if (number <= 0) 1150 number = 1; 1151 if (edit_index(number)) 1152 error("No such file", NULL_PARG); 1153 break; 1154 1155 case A_OPT_TOGGLE: 1156 start_mca(A_OPT_TOGGLE, "-", (void*)NULL); 1157 optflag = OPT_TOGGLE; 1158 c = getcc(); 1159 goto again; 1160 1161 case A_DISP_OPTION: 1162 /* 1163 * Report a flag setting. 1164 */ 1165 start_mca(A_DISP_OPTION, "_", (void*)NULL); 1166 c = getcc(); 1167 if (c == erase_char || c == kill_char) 1168 break; 1169 toggle_option(c, "", OPT_NO_TOGGLE); 1170 break; 1171 1172 case A_FIRSTCMD: 1173 /* 1174 * Set an initial command for new files. 1175 */ 1176 start_mca(A_FIRSTCMD, "+", (void*)NULL); 1177 c = getcc(); 1178 goto again; 1179 1180 case A_SHELL: 1181 /* 1182 * Shell escape. 1183 */ 1184 #if SHELL_ESCAPE 1185 start_mca(A_SHELL, "!", ml_shell); 1186 c = getcc(); 1187 goto again; 1188 #else 1189 error("Command not available", NULL_PARG); 1190 break; 1191 #endif 1192 1193 case A_SETMARK: 1194 /* 1195 * Set a mark. 1196 */ 1197 start_mca(A_SETMARK, "mark: ", (void*)NULL); 1198 c = getcc(); 1199 if (c == erase_char || c == kill_char || 1200 c == '\n' || c == '\r') 1201 break; 1202 setmark(c); 1203 break; 1204 1205 case A_GOMARK: 1206 /* 1207 * Go to a mark. 1208 */ 1209 start_mca(A_GOMARK, "goto mark: ", (void*)NULL); 1210 c = getcc(); 1211 if (c == erase_char || c == kill_char || 1212 c == '\n' || c == '\r') 1213 break; 1214 gomark(c); 1215 break; 1216 1217 case A_PIPE: 1218 #if PIPEC 1219 start_mca(A_PIPE, "|mark: ", (void*)NULL); 1220 c = getcc(); 1221 if (c == erase_char || c == kill_char) 1222 break; 1223 if (c == '\n' || c == '\r') 1224 c = '.'; 1225 if (badmark(c)) 1226 break; 1227 pipec = c; 1228 start_mca(A_PIPE, "!", ml_shell); 1229 c = getcc(); 1230 goto again; 1231 #else 1232 error("Command not available", NULL_PARG); 1233 break; 1234 #endif 1235 1236 case A_B_BRACKET: 1237 case A_F_BRACKET: 1238 start_mca(action, "Brackets: ", (void*)NULL); 1239 c = getcc(); 1240 goto again; 1241 1242 case A_PREFIX: 1243 /* 1244 * The command is incomplete (more chars are needed). 1245 * Display the current char, so the user knows 1246 * what's going on, and get another character. 1247 */ 1248 if (mca != A_PREFIX) 1249 { 1250 start_mca(A_PREFIX, " ", (void*)NULL); 1251 cmd_reset(); 1252 (void) cmd_char(c); 1253 } 1254 c = getcc(); 1255 goto again; 1256 1257 case A_NOACTION: 1258 break; 1259 1260 default: 1261 if (be_helpful) 1262 helpprompt = 1; 1263 else 1264 bell(); 1265 break; 1266 } 1267 } 1268 } 1269