1 /* $FreeBSD$ */ 2 /* 3 * Copyright (C) 1984-2002 Mark Nudelman 4 * 5 * You may distribute under the terms of either the GNU General Public 6 * License or the Less License, as specified in the README file. 7 * 8 * For more information about less, or for information on how to 9 * contact the author, see the README file. 10 */ 11 12 13 /* 14 * User-level command processor. 15 */ 16 17 #include "less.h" 18 #if MSDOS_COMPILER==WIN32C 19 #include <windows.h> 20 #endif 21 #include "position.h" 22 #include "option.h" 23 #include "cmd.h" 24 25 extern int erase_char, kill_char; 26 extern int sigs; 27 extern int quit_at_eof; 28 extern int quit_if_one_screen; 29 extern int squished; 30 extern int hit_eof; 31 extern int sc_width; 32 extern int sc_height; 33 extern int swindow; 34 extern int jump_sline; 35 extern int quitting; 36 extern int wscroll; 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 int more_mode; 43 extern char *every_first_cmd; 44 extern char *curr_altfilename; 45 extern char version[]; 46 extern struct scrpos initial_scrpos; 47 extern IFILE curr_ifile; 48 extern void constant *ml_search; 49 extern void constant *ml_examine; 50 #if SHELL_ESCAPE || PIPEC 51 extern void constant *ml_shell; 52 #endif 53 #if EDITOR 54 extern char *editor; 55 extern char *editproto; 56 #endif 57 extern int screen_trashed; /* The screen has been overwritten */ 58 extern int shift_count; 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 #if PIPEC 73 static char pipec; 74 #endif 75 76 static void multi_search(); 77 78 /* 79 * Move the cursor to lower left before executing a command. 80 * This looks nicer if the command takes a long time before 81 * updating the screen. 82 */ 83 static void 84 cmd_exec() 85 { 86 clear_attn(); 87 lower_left(); 88 flush(); 89 } 90 91 /* 92 * Set up the display to start a new multi-character command. 93 */ 94 static void 95 start_mca(action, prompt, mlist, cmdflags) 96 int action; 97 char *prompt; 98 void *mlist; 99 int cmdflags; 100 { 101 mca = action; 102 clear_cmd(); 103 cmd_putstr(prompt); 104 set_mlist(mlist, cmdflags); 105 } 106 107 public int 108 in_mca() 109 { 110 return (mca != 0 && mca != A_PREFIX); 111 } 112 113 /* 114 * Set up the display to start a new search command. 115 */ 116 static void 117 mca_search() 118 { 119 if (search_type & SRCH_FORW) 120 mca = A_F_SEARCH; 121 else 122 mca = A_B_SEARCH; 123 124 clear_cmd(); 125 126 if (search_type & SRCH_NO_MATCH) 127 cmd_putstr("Non-match "); 128 if (search_type & SRCH_FIRST_FILE) 129 cmd_putstr("First-file "); 130 if (search_type & SRCH_PAST_EOF) 131 cmd_putstr("EOF-ignore "); 132 if (search_type & SRCH_NO_MOVE) 133 cmd_putstr("Keep-pos "); 134 if (search_type & SRCH_NO_REGEX) 135 cmd_putstr("Regex-off "); 136 137 if (search_type & SRCH_FORW) 138 cmd_putstr("/"); 139 else 140 cmd_putstr("?"); 141 set_mlist(ml_search, 0); 142 } 143 144 /* 145 * Set up the display to start a new toggle-option command. 146 */ 147 static void 148 mca_opt_toggle() 149 { 150 int no_prompt; 151 int flag; 152 char *dash; 153 154 no_prompt = (optflag & OPT_NO_PROMPT); 155 flag = (optflag & ~OPT_NO_PROMPT); 156 dash = (flag == OPT_NO_TOGGLE) ? "_" : "-"; 157 158 mca = A_OPT_TOGGLE; 159 clear_cmd(); 160 cmd_putstr(dash); 161 if (optgetname) 162 cmd_putstr(dash); 163 if (no_prompt) 164 cmd_putstr("(P)"); 165 switch (flag) 166 { 167 case OPT_UNSET: 168 cmd_putstr("+"); 169 break; 170 case OPT_SET: 171 cmd_putstr("!"); 172 break; 173 } 174 set_mlist(NULL, 0); 175 } 176 177 /* 178 * Execute a multicharacter command. 179 */ 180 static void 181 exec_mca() 182 { 183 register char *cbuf; 184 185 cmd_exec(); 186 cbuf = get_cmdbuf(); 187 188 switch (mca) 189 { 190 case A_F_SEARCH: 191 case A_B_SEARCH: 192 multi_search(cbuf, (int) number); 193 break; 194 case A_FIRSTCMD: 195 /* 196 * Skip leading spaces or + signs in the string. 197 */ 198 while (*cbuf == '+' || *cbuf == ' ') 199 cbuf++; 200 if (every_first_cmd != NULL) 201 free(every_first_cmd); 202 if (*cbuf == '\0') 203 every_first_cmd = NULL; 204 else 205 every_first_cmd = save(cbuf); 206 break; 207 case A_OPT_TOGGLE: 208 toggle_option(optchar, cbuf, optflag); 209 optchar = '\0'; 210 break; 211 case A_F_BRACKET: 212 match_brac(cbuf[0], cbuf[1], 1, (int) number); 213 break; 214 case A_B_BRACKET: 215 match_brac(cbuf[1], cbuf[0], 0, (int) number); 216 break; 217 #if EXAMINE 218 case A_EXAMINE: 219 if (secure) 220 break; 221 edit_list(cbuf); 222 #if TAGS 223 /* If tag structure is loaded then clean it up. */ 224 cleantags(); 225 #endif 226 break; 227 #endif 228 #if SHELL_ESCAPE 229 case A_SHELL: 230 /* 231 * !! just uses whatever is in shellcmd. 232 * Otherwise, copy cmdbuf to shellcmd, 233 * expanding any special characters ("%" or "#"). 234 */ 235 if (*cbuf != '!') 236 { 237 if (shellcmd != NULL) 238 free(shellcmd); 239 shellcmd = fexpand(cbuf); 240 } 241 242 if (secure) 243 break; 244 if (shellcmd == NULL) 245 lsystem("", "!done"); 246 else 247 lsystem(shellcmd, "!done"); 248 break; 249 #endif 250 #if PIPEC 251 case A_PIPE: 252 if (secure) 253 break; 254 (void) pipe_mark(pipec, cbuf); 255 error("|done", NULL_PARG); 256 break; 257 #endif 258 } 259 } 260 261 /* 262 * Add a character to a multi-character command. 263 */ 264 static int 265 mca_char(c) 266 int c; 267 { 268 char *p; 269 int flag; 270 char buf[3]; 271 PARG parg; 272 273 switch (mca) 274 { 275 case 0: 276 /* 277 * Not in a multicharacter command. 278 */ 279 return (NO_MCA); 280 281 case A_PREFIX: 282 /* 283 * In the prefix of a command. 284 * This not considered a multichar command 285 * (even tho it uses cmdbuf, etc.). 286 * It is handled in the commands() switch. 287 */ 288 return (NO_MCA); 289 290 case A_DIGIT: 291 /* 292 * Entering digits of a number. 293 * Terminated by a non-digit. 294 */ 295 if ((c < '0' || c > '9') && 296 editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID) 297 { 298 /* 299 * Not part of the number. 300 * Treat as a normal command character. 301 */ 302 number = cmd_int(); 303 mca = 0; 304 cmd_accept(); 305 return (NO_MCA); 306 } 307 break; 308 309 case A_OPT_TOGGLE: 310 /* 311 * Special case for the TOGGLE_OPTION command. 312 * If the option letter which was entered is a 313 * single-char option, execute the command immediately, 314 * so user doesn't have to hit RETURN. 315 * If the first char is + or -, this indicates 316 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE. 317 * "--" begins inputting a long option name. 318 */ 319 if (optchar == '\0' && len_cmdbuf() == 0) 320 { 321 flag = (optflag & ~OPT_NO_PROMPT); 322 if (flag == OPT_NO_TOGGLE) 323 { 324 switch (c) 325 { 326 case '_': 327 /* "__" = long option name. */ 328 optgetname = TRUE; 329 mca_opt_toggle(); 330 return (MCA_MORE); 331 } 332 } else 333 { 334 switch (c) 335 { 336 case '+': 337 /* "-+" = UNSET. */ 338 optflag = (flag == OPT_UNSET) ? 339 OPT_TOGGLE : OPT_UNSET; 340 mca_opt_toggle(); 341 return (MCA_MORE); 342 case '!': 343 /* "-!" = SET */ 344 optflag = (flag == OPT_SET) ? 345 OPT_TOGGLE : OPT_SET; 346 mca_opt_toggle(); 347 return (MCA_MORE); 348 case CONTROL('P'): 349 optflag ^= OPT_NO_PROMPT; 350 mca_opt_toggle(); 351 return (MCA_MORE); 352 case '-': 353 /* "--" = long option name. */ 354 optgetname = TRUE; 355 mca_opt_toggle(); 356 return (MCA_MORE); 357 } 358 } 359 } 360 if (optgetname) 361 { 362 /* 363 * We're getting a long option name. 364 * See if we've matched an option name yet. 365 * If so, display the complete name and stop 366 * accepting chars until user hits RETURN. 367 */ 368 struct loption *o; 369 char *oname; 370 int lc; 371 372 if (c == '\n' || c == '\r') 373 { 374 /* 375 * When the user hits RETURN, make sure 376 * we've matched an option name, then 377 * pretend he just entered the equivalent 378 * option letter. 379 */ 380 if (optchar == '\0') 381 { 382 parg.p_string = get_cmdbuf(); 383 error("There is no --%s option", &parg); 384 return (MCA_DONE); 385 } 386 optgetname = FALSE; 387 cmd_reset(); 388 c = optchar; 389 } else 390 { 391 if (optchar != '\0') 392 { 393 /* 394 * Already have a match for the name. 395 * Don't accept anything but erase/kill. 396 */ 397 if (c == erase_char || c == kill_char) 398 return (MCA_DONE); 399 return (MCA_MORE); 400 } 401 /* 402 * Add char to cmd buffer and try to match 403 * the option name. 404 */ 405 if (cmd_char(c) == CC_QUIT) 406 return (MCA_DONE); 407 p = get_cmdbuf(); 408 lc = islower(p[0]); 409 o = findopt_name(&p, &oname, NULL); 410 if (o != NULL) 411 { 412 /* 413 * Got a match. 414 * Remember the option letter and 415 * display the full option name. 416 */ 417 optchar = o->oletter; 418 if (!lc && islower(optchar)) 419 optchar = toupper(optchar); 420 cmd_reset(); 421 mca_opt_toggle(); 422 for (p = oname; *p != '\0'; p++) 423 { 424 c = *p; 425 if (!lc && islower(c)) 426 c = toupper(c); 427 if (cmd_char(c) != CC_OK) 428 return (MCA_DONE); 429 } 430 } 431 return (MCA_MORE); 432 } 433 } else 434 { 435 if (c == erase_char || c == kill_char) 436 break; 437 if (optchar != '\0') 438 /* We already have the option letter. */ 439 break; 440 } 441 442 optchar = c; 443 if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE || 444 single_char_option(c)) 445 { 446 toggle_option(c, "", optflag); 447 return (MCA_DONE); 448 } 449 /* 450 * Display a prompt appropriate for the option letter. 451 */ 452 if ((p = opt_prompt(c)) == NULL) 453 { 454 buf[0] = '-'; 455 buf[1] = c; 456 buf[2] = '\0'; 457 p = buf; 458 } 459 start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0); 460 return (MCA_MORE); 461 462 case A_F_SEARCH: 463 case A_B_SEARCH: 464 /* 465 * Special case for search commands. 466 * Certain characters as the first char of 467 * the pattern have special meaning: 468 * ! Toggle the NO_MATCH flag 469 * * Toggle the PAST_EOF flag 470 * @ Toggle the FIRST_FILE flag 471 */ 472 if (len_cmdbuf() > 0) 473 /* 474 * Only works for the first char of the pattern. 475 */ 476 break; 477 478 flag = 0; 479 switch (c) 480 { 481 case '*': 482 if (more_mode) 483 break; 484 case CONTROL('E'): /* ignore END of file */ 485 flag = SRCH_PAST_EOF; 486 break; 487 case '@': 488 if (more_mode) 489 break; 490 case CONTROL('F'): /* FIRST file */ 491 flag = SRCH_FIRST_FILE; 492 break; 493 case CONTROL('K'): /* KEEP position */ 494 flag = SRCH_NO_MOVE; 495 break; 496 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ 497 flag = SRCH_NO_REGEX; 498 break; 499 case CONTROL('N'): /* NOT match */ 500 case '!': 501 flag = SRCH_NO_MATCH; 502 break; 503 } 504 if (flag != 0) 505 { 506 search_type ^= flag; 507 mca_search(); 508 return (MCA_MORE); 509 } 510 break; 511 } 512 513 /* 514 * Any other multicharacter command 515 * is terminated by a newline. 516 */ 517 if (c == '\n' || c == '\r') 518 { 519 /* 520 * Execute the command. 521 */ 522 exec_mca(); 523 return (MCA_DONE); 524 } 525 526 /* 527 * Append the char to the command buffer. 528 */ 529 if (cmd_char(c) == CC_QUIT) 530 /* 531 * Abort the multi-char command. 532 */ 533 return (MCA_DONE); 534 535 if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2) 536 { 537 /* 538 * Special case for the bracket-matching commands. 539 * Execute the command after getting exactly two 540 * characters from the user. 541 */ 542 exec_mca(); 543 return (MCA_DONE); 544 } 545 546 /* 547 * Need another character. 548 */ 549 return (MCA_MORE); 550 } 551 552 /* 553 * Make sure the screen is displayed. 554 */ 555 static void 556 make_display() 557 { 558 /* 559 * If nothing is displayed yet, display starting from initial_scrpos. 560 */ 561 if (empty_screen()) 562 { 563 if (initial_scrpos.pos == NULL_POSITION) 564 /* 565 * {{ Maybe this should be: 566 * jump_loc(ch_zero(), jump_sline); 567 * but this behavior seems rather unexpected 568 * on the first screen. }} 569 */ 570 jump_loc(ch_zero(), 1); 571 else 572 jump_loc(initial_scrpos.pos, initial_scrpos.ln); 573 } else if (screen_trashed) 574 { 575 int save_top_scroll; 576 save_top_scroll = top_scroll; 577 top_scroll = 1; 578 repaint(); 579 top_scroll = save_top_scroll; 580 } 581 } 582 583 /* 584 * Display the appropriate prompt. 585 */ 586 static void 587 prompt() 588 { 589 register char *p; 590 591 if (ungotp != NULL && ungotp > ungot) 592 { 593 /* 594 * No prompt necessary if commands are from 595 * ungotten chars rather than from the user. 596 */ 597 return; 598 } 599 600 /* 601 * Make sure the screen is displayed. 602 */ 603 make_display(); 604 bottompos = position(BOTTOM_PLUS_ONE); 605 606 /* 607 * If the -E flag is set and we've hit EOF on the last file, quit. 608 */ 609 if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) && 610 hit_eof && !(ch_getflags() & CH_HELPFILE) && 611 next_ifile(curr_ifile) == NULL_IFILE) 612 quit(QUIT_OK); 613 quit_if_one_screen = FALSE; 614 #if 0 /* This doesn't work well because some "te"s clear the screen. */ 615 /* 616 * If the -e flag is set and we've hit EOF on the last file, 617 * and the file is squished (shorter than the screen), quit. 618 */ 619 if (quit_at_eof && squished && 620 next_ifile(curr_ifile) == NULL_IFILE) 621 quit(QUIT_OK); 622 #endif 623 624 #if MSDOS_COMPILER==WIN32C 625 /* 626 * In Win32, display the file name in the window title. 627 */ 628 if (!(ch_getflags() & CH_HELPFILE)) 629 SetConsoleTitle(pr_expand("Less?f - %f.", 0)); 630 #endif 631 /* 632 * Select the proper prompt and display it. 633 */ 634 clear_cmd(); 635 p = pr_string(); 636 if (p == NULL) 637 putchr(':'); 638 else 639 { 640 so_enter(); 641 putstr(p); 642 so_exit(); 643 } 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 if (ch_getflags() & CH_HELPFILE) 1084 break; 1085 cmd_exec(); 1086 jump_forw(); 1087 ignore_eoi = 1; 1088 hit_eof = 0; 1089 while (!sigs) 1090 forward(1, 0, 0); 1091 ignore_eoi = 0; 1092 /* 1093 * This gets us back in "F mode" after processing 1094 * a non-abort signal (e.g. window-change). 1095 */ 1096 if (sigs && !ABORT_SIGS()) 1097 newaction = A_F_FOREVER; 1098 break; 1099 1100 case A_F_SCROLL: 1101 /* 1102 * Forward N lines 1103 * (default same as last 'd' or 'u' command). 1104 */ 1105 if (number > 0) 1106 wscroll = (int) number; 1107 cmd_exec(); 1108 if (show_attn == OPT_ONPLUS) 1109 set_attnpos(bottompos); 1110 forward(wscroll, 0, 0); 1111 break; 1112 1113 case A_B_SCROLL: 1114 /* 1115 * Forward N lines 1116 * (default same as last 'd' or 'u' command). 1117 */ 1118 if (number > 0) 1119 wscroll = (int) number; 1120 cmd_exec(); 1121 backward(wscroll, 0, 0); 1122 break; 1123 1124 case A_FREPAINT: 1125 /* 1126 * Flush buffers, then repaint screen. 1127 * Don't flush the buffers on a pipe! 1128 */ 1129 if (ch_getflags() & CH_CANSEEK) 1130 { 1131 ch_flush(); 1132 clr_linenum(); 1133 #if HILITE_SEARCH 1134 clr_hilite(); 1135 #endif 1136 } 1137 /* FALLTHRU */ 1138 case A_REPAINT: 1139 /* 1140 * Repaint screen. 1141 */ 1142 cmd_exec(); 1143 repaint(); 1144 break; 1145 1146 case A_GOLINE: 1147 /* 1148 * Go to line N, default beginning of file. 1149 */ 1150 if (number <= 0) 1151 number = 1; 1152 cmd_exec(); 1153 jump_back(number); 1154 break; 1155 1156 case A_PERCENT: 1157 /* 1158 * Go to a specified percentage into the file. 1159 */ 1160 if (number < 0) 1161 number = 0; 1162 if (number > 100) 1163 number = 100; 1164 cmd_exec(); 1165 jump_percent((int) number); 1166 break; 1167 1168 case A_GOEND: 1169 /* 1170 * Go to line N, default end of file. 1171 */ 1172 cmd_exec(); 1173 if (number <= 0) 1174 jump_forw(); 1175 else 1176 jump_back(number); 1177 break; 1178 1179 case A_GOPOS: 1180 /* 1181 * Go to a specified byte position in the file. 1182 */ 1183 cmd_exec(); 1184 if (number < 0) 1185 number = 0; 1186 jump_line_loc((POSITION) number, jump_sline); 1187 break; 1188 1189 case A_STAT: 1190 /* 1191 * Print file name, etc. 1192 */ 1193 if (ch_getflags() & CH_HELPFILE) 1194 break; 1195 cmd_exec(); 1196 parg.p_string = eq_message(); 1197 error("%s", &parg); 1198 break; 1199 1200 case A_VERSION: 1201 /* 1202 * Print version number, without the "@(#)". 1203 */ 1204 cmd_exec(); 1205 dispversion(); 1206 break; 1207 1208 case A_QUIT: 1209 /* 1210 * Exit. 1211 */ 1212 if (curr_ifile != NULL_IFILE && 1213 ch_getflags() & CH_HELPFILE) 1214 { 1215 /* 1216 * Quit while viewing the help file 1217 * just means return to viewing the 1218 * previous file. 1219 */ 1220 if (edit_prev(1) == 0) 1221 break; 1222 } 1223 if (extra != NULL) 1224 quit(*extra); 1225 quit(QUIT_OK); 1226 break; 1227 1228 /* 1229 * Define abbreviation for a commonly used sequence below. 1230 */ 1231 #define DO_SEARCH() if (number <= 0) number = 1; \ 1232 mca_search(); \ 1233 cmd_exec(); \ 1234 multi_search((char *)NULL, (int) number); 1235 1236 1237 case A_F_SEARCH: 1238 /* 1239 * Search forward for a pattern. 1240 * Get the first char of the pattern. 1241 */ 1242 search_type = SRCH_FORW; 1243 if (number <= 0) 1244 number = 1; 1245 mca_search(); 1246 c = getcc(); 1247 goto again; 1248 1249 case A_B_SEARCH: 1250 /* 1251 * Search backward for a pattern. 1252 * Get the first char of the pattern. 1253 */ 1254 search_type = SRCH_BACK; 1255 if (number <= 0) 1256 number = 1; 1257 mca_search(); 1258 c = getcc(); 1259 goto again; 1260 1261 case A_AGAIN_SEARCH: 1262 /* 1263 * Repeat previous search. 1264 */ 1265 DO_SEARCH(); 1266 break; 1267 1268 case A_T_AGAIN_SEARCH: 1269 /* 1270 * Repeat previous search, multiple files. 1271 */ 1272 search_type |= SRCH_PAST_EOF; 1273 DO_SEARCH(); 1274 break; 1275 1276 case A_REVERSE_SEARCH: 1277 /* 1278 * Repeat previous search, in reverse direction. 1279 */ 1280 save_search_type = search_type; 1281 search_type = SRCH_REVERSE(search_type); 1282 DO_SEARCH(); 1283 search_type = save_search_type; 1284 break; 1285 1286 case A_T_REVERSE_SEARCH: 1287 /* 1288 * Repeat previous search, 1289 * multiple files in reverse direction. 1290 */ 1291 save_search_type = search_type; 1292 search_type = SRCH_REVERSE(search_type); 1293 search_type |= SRCH_PAST_EOF; 1294 DO_SEARCH(); 1295 search_type = save_search_type; 1296 break; 1297 1298 case A_UNDO_SEARCH: 1299 undo_search(); 1300 break; 1301 1302 case A_HELP: 1303 /* 1304 * Help. 1305 */ 1306 if (ch_getflags() & CH_HELPFILE) 1307 break; 1308 cmd_exec(); 1309 (void) edit(FAKE_HELPFILE); 1310 break; 1311 1312 case A_EXAMINE: 1313 #if EXAMINE 1314 /* 1315 * Edit a new file. Get the filename. 1316 */ 1317 if (secure) 1318 { 1319 error("Command not available", NULL_PARG); 1320 break; 1321 } 1322 start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); 1323 c = getcc(); 1324 goto again; 1325 #else 1326 error("Command not available", NULL_PARG); 1327 break; 1328 #endif 1329 1330 case A_VISUAL: 1331 /* 1332 * Invoke an editor on the input file. 1333 */ 1334 #if EDITOR 1335 if (secure) 1336 { 1337 error("Command not available", NULL_PARG); 1338 break; 1339 } 1340 if (ch_getflags() & CH_HELPFILE) 1341 break; 1342 if (strcmp(get_filename(curr_ifile), "-") == 0) 1343 { 1344 error("Cannot edit standard input", NULL_PARG); 1345 break; 1346 } 1347 if (curr_altfilename != NULL) 1348 { 1349 error("Cannot edit file processed with LESSOPEN", 1350 NULL_PARG); 1351 break; 1352 } 1353 start_mca(A_SHELL, "!", ml_shell, 0); 1354 /* 1355 * Expand the editor prototype string 1356 * and pass it to the system to execute. 1357 * (Make sure the screen is displayed so the 1358 * expansion of "+%lm" works.) 1359 */ 1360 make_display(); 1361 cmd_exec(); 1362 lsystem(pr_expand(editproto, 0), (char*)NULL); 1363 break; 1364 #else 1365 error("Command not available", NULL_PARG); 1366 break; 1367 #endif 1368 1369 case A_NEXT_FILE: 1370 /* 1371 * Examine next file. 1372 */ 1373 #if TAGS 1374 if (ntags()) 1375 { 1376 error("No next file", NULL_PARG); 1377 break; 1378 } 1379 #endif 1380 if (number <= 0) 1381 number = 1; 1382 if (edit_next((int) number)) 1383 { 1384 if (quit_at_eof && hit_eof && 1385 !(ch_getflags() & CH_HELPFILE)) 1386 quit(QUIT_OK); 1387 parg.p_string = (number > 1) ? "(N-th) " : ""; 1388 error("No %snext file", &parg); 1389 } 1390 break; 1391 1392 case A_PREV_FILE: 1393 /* 1394 * Examine previous file. 1395 */ 1396 #if TAGS 1397 if (ntags()) 1398 { 1399 error("No previous file", NULL_PARG); 1400 break; 1401 } 1402 #endif 1403 if (number <= 0) 1404 number = 1; 1405 if (edit_prev((int) number)) 1406 { 1407 parg.p_string = (number > 1) ? "(N-th) " : ""; 1408 error("No %sprevious file", &parg); 1409 } 1410 break; 1411 1412 case A_NEXT_TAG: 1413 #if TAGS 1414 if (number <= 0) 1415 number = 1; 1416 tagfile = nexttag((int) number); 1417 if (tagfile == NULL) 1418 { 1419 error("No next tag", NULL_PARG); 1420 break; 1421 } 1422 if (edit(tagfile) == 0) 1423 { 1424 POSITION pos = tagsearch(); 1425 if (pos != NULL_POSITION) 1426 jump_loc(pos, jump_sline); 1427 } 1428 #else 1429 error("Command not available", NULL_PARG); 1430 #endif 1431 break; 1432 1433 case A_PREV_TAG: 1434 #if TAGS 1435 if (number <= 0) 1436 number = 1; 1437 tagfile = prevtag((int) number); 1438 if (tagfile == NULL) 1439 { 1440 error("No previous tag", NULL_PARG); 1441 break; 1442 } 1443 if (edit(tagfile) == 0) 1444 { 1445 POSITION pos = tagsearch(); 1446 if (pos != NULL_POSITION) 1447 jump_loc(pos, jump_sline); 1448 } 1449 #else 1450 error("Command not available", NULL_PARG); 1451 #endif 1452 break; 1453 1454 case A_INDEX_FILE: 1455 /* 1456 * Examine a particular file. 1457 */ 1458 if (number <= 0) 1459 number = 1; 1460 if (edit_index((int) number)) 1461 error("No such file", NULL_PARG); 1462 break; 1463 1464 case A_REMOVE_FILE: 1465 if (ch_getflags() & CH_HELPFILE) 1466 break; 1467 old_ifile = curr_ifile; 1468 new_ifile = getoff_ifile(curr_ifile); 1469 if (new_ifile == NULL_IFILE) 1470 { 1471 bell(); 1472 break; 1473 } 1474 if (edit_ifile(new_ifile) != 0) 1475 { 1476 reedit_ifile(old_ifile); 1477 break; 1478 } 1479 del_ifile(old_ifile); 1480 break; 1481 1482 case A_OPT_TOGGLE: 1483 optflag = OPT_TOGGLE; 1484 optgetname = FALSE; 1485 mca_opt_toggle(); 1486 c = getcc(); 1487 goto again; 1488 1489 case A_DISP_OPTION: 1490 /* 1491 * Report a flag setting. 1492 */ 1493 optflag = OPT_NO_TOGGLE; 1494 optgetname = FALSE; 1495 mca_opt_toggle(); 1496 c = getcc(); 1497 goto again; 1498 1499 case A_FIRSTCMD: 1500 /* 1501 * Set an initial command for new files. 1502 */ 1503 start_mca(A_FIRSTCMD, "+", (void*)NULL, 0); 1504 c = getcc(); 1505 goto again; 1506 1507 case A_SHELL: 1508 /* 1509 * Shell escape. 1510 */ 1511 #if SHELL_ESCAPE 1512 if (secure) 1513 { 1514 error("Command not available", NULL_PARG); 1515 break; 1516 } 1517 start_mca(A_SHELL, "!", ml_shell, 0); 1518 c = getcc(); 1519 goto again; 1520 #else 1521 error("Command not available", NULL_PARG); 1522 break; 1523 #endif 1524 1525 case A_SETMARK: 1526 /* 1527 * Set a mark. 1528 */ 1529 if (ch_getflags() & CH_HELPFILE) 1530 break; 1531 start_mca(A_SETMARK, "mark: ", (void*)NULL, 0); 1532 c = getcc(); 1533 if (c == erase_char || c == kill_char || 1534 c == '\n' || c == '\r') 1535 break; 1536 setmark(c); 1537 break; 1538 1539 case A_GOMARK: 1540 /* 1541 * Go to a mark. 1542 */ 1543 start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); 1544 c = getcc(); 1545 if (c == erase_char || c == kill_char || 1546 c == '\n' || c == '\r') 1547 break; 1548 gomark(c); 1549 break; 1550 1551 case A_PIPE: 1552 #if PIPEC 1553 if (secure) 1554 { 1555 error("Command not available", NULL_PARG); 1556 break; 1557 } 1558 start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); 1559 c = getcc(); 1560 if (c == erase_char || c == kill_char) 1561 break; 1562 if (c == '\n' || c == '\r') 1563 c = '.'; 1564 if (badmark(c)) 1565 break; 1566 pipec = c; 1567 start_mca(A_PIPE, "!", ml_shell, 0); 1568 c = getcc(); 1569 goto again; 1570 #else 1571 error("Command not available", NULL_PARG); 1572 break; 1573 #endif 1574 1575 case A_B_BRACKET: 1576 case A_F_BRACKET: 1577 start_mca(action, "Brackets: ", (void*)NULL, 0); 1578 c = getcc(); 1579 goto again; 1580 1581 case A_LSHIFT: 1582 if (number > 0) 1583 shift_count = number; 1584 else 1585 number = (shift_count > 0) ? 1586 shift_count : sc_width / 2; 1587 if (number > hshift) 1588 number = hshift; 1589 hshift -= number; 1590 screen_trashed = 1; 1591 break; 1592 1593 case A_RSHIFT: 1594 if (number > 0) 1595 shift_count = number; 1596 else 1597 number = (shift_count > 0) ? 1598 shift_count : sc_width / 2; 1599 hshift += number; 1600 screen_trashed = 1; 1601 break; 1602 1603 case A_PREFIX: 1604 /* 1605 * The command is incomplete (more chars are needed). 1606 * Display the current char, so the user knows 1607 * what's going on, and get another character. 1608 */ 1609 if (mca != A_PREFIX) 1610 { 1611 cmd_reset(); 1612 start_mca(A_PREFIX, " ", (void*)NULL, 1613 CF_QUIT_ON_ERASE); 1614 (void) cmd_char(c); 1615 } 1616 c = getcc(); 1617 goto again; 1618 1619 case A_NOACTION: 1620 break; 1621 1622 default: 1623 bell(); 1624 break; 1625 } 1626 } 1627 } 1628