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