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