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