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