1 /* $FreeBSD$ */ 2 /* 3 * Copyright (C) 1984-2000 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 int 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, 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, number); 213 break; 214 case A_B_BRACKET: 215 match_brac(cbuf[1], cbuf[0], 0, number); 216 break; 217 #if EXAMINE 218 case A_EXAMINE: 219 if (secure) 220 break; 221 edit_list(cbuf); 222 /* If tag structure is loaded then clean it up. */ 223 cleantags(); 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 option *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 '*': 480 if (more_mode) 481 break; 482 case CONTROL('E'): /* ignore END of file */ 483 flag = SRCH_PAST_EOF; 484 break; 485 case '@': 486 if (more_mode) 487 break; 488 case CONTROL('F'): /* FIRST file */ 489 flag = SRCH_FIRST_FILE; 490 break; 491 case CONTROL('K'): /* KEEP position */ 492 flag = SRCH_NO_MOVE; 493 break; 494 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ 495 flag = SRCH_NO_REGEX; 496 break; 497 case CONTROL('N'): /* NOT match */ 498 case '!': 499 flag = SRCH_NO_MATCH; 500 break; 501 } 502 if (flag != 0) 503 { 504 search_type ^= flag; 505 mca_search(); 506 return (MCA_MORE); 507 } 508 break; 509 } 510 511 /* 512 * Any other multicharacter command 513 * is terminated by a newline. 514 */ 515 if (c == '\n' || c == '\r') 516 { 517 /* 518 * Execute the command. 519 */ 520 exec_mca(); 521 return (MCA_DONE); 522 } 523 524 /* 525 * Append the char to the command buffer. 526 */ 527 if (cmd_char(c) == CC_QUIT) 528 /* 529 * Abort the multi-char command. 530 */ 531 return (MCA_DONE); 532 533 if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2) 534 { 535 /* 536 * Special case for the bracket-matching commands. 537 * Execute the command after getting exactly two 538 * characters from the user. 539 */ 540 exec_mca(); 541 return (MCA_DONE); 542 } 543 544 /* 545 * Need another character. 546 */ 547 return (MCA_MORE); 548 } 549 550 /* 551 * Make sure the screen is displayed. 552 */ 553 static void 554 make_display() 555 { 556 /* 557 * If nothing is displayed yet, display starting from initial_scrpos. 558 */ 559 if (empty_screen()) 560 { 561 if (initial_scrpos.pos == NULL_POSITION) 562 /* 563 * {{ Maybe this should be: 564 * jump_loc(ch_zero(), jump_sline); 565 * but this behavior seems rather unexpected 566 * on the first screen. }} 567 */ 568 jump_loc(ch_zero(), 1); 569 else 570 jump_loc(initial_scrpos.pos, initial_scrpos.ln); 571 } else if (screen_trashed) 572 { 573 int save_top_scroll; 574 save_top_scroll = top_scroll; 575 top_scroll = 1; 576 repaint(); 577 top_scroll = save_top_scroll; 578 } 579 } 580 581 /* 582 * Display the appropriate prompt. 583 */ 584 static void 585 prompt() 586 { 587 register char *p; 588 589 if (ungotp != NULL && ungotp > ungot) 590 { 591 /* 592 * No prompt necessary if commands are from 593 * ungotten chars rather than from the user. 594 */ 595 return; 596 } 597 598 /* 599 * Make sure the screen is displayed. 600 */ 601 make_display(); 602 bottompos = position(BOTTOM_PLUS_ONE); 603 604 /* 605 * If the -E flag is set and we've hit EOF on the last file, quit. 606 */ 607 if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) && 608 hit_eof && !(ch_getflags() & CH_HELPFILE) && 609 next_ifile(curr_ifile) == NULL_IFILE) 610 quit(QUIT_OK); 611 quit_if_one_screen = FALSE; 612 #if 0 /* This doesn't work well because some "te"s clear the screen. */ 613 /* 614 * If the -e flag is set and we've hit EOF on the last file, 615 * and the file is squished (shorter than the screen), quit. 616 */ 617 if (quit_at_eof && squished && 618 next_ifile(curr_ifile) == NULL_IFILE) 619 quit(QUIT_OK); 620 #endif 621 622 #if MSDOS_COMPILER==WIN32C 623 /* 624 * In Win32, display the file name in the window title. 625 */ 626 if (!(ch_getflags() & CH_HELPFILE)) 627 SetConsoleTitle(pr_expand("Less?f - %f.", 0)); 628 #endif 629 /* 630 * Select the proper prompt and display it. 631 */ 632 clear_cmd(); 633 p = pr_string(); 634 if (p == NULL) 635 putchr(':'); 636 else 637 { 638 so_enter(); 639 putstr(p); 640 so_exit(); 641 } 642 } 643 644 /* 645 * Display the less version message. 646 */ 647 public void 648 dispversion() 649 { 650 PARG parg; 651 652 parg.p_string = version; 653 error("less %s", &parg); 654 } 655 656 /* 657 * Get command character. 658 * The character normally comes from the keyboard, 659 * but may come from ungotten characters 660 * (characters previously given to ungetcc or ungetsc). 661 */ 662 public int 663 getcc() 664 { 665 if (ungotp == NULL) 666 /* 667 * Normal case: no ungotten chars, so get one from the user. 668 */ 669 return (getchr()); 670 671 if (ungotp > ungot) 672 /* 673 * Return the next ungotten char. 674 */ 675 return (*--ungotp); 676 677 /* 678 * We have just run out of ungotten chars. 679 */ 680 ungotp = NULL; 681 if (len_cmdbuf() == 0 || !empty_screen()) 682 return (getchr()); 683 /* 684 * Command is incomplete, so try to complete it. 685 */ 686 switch (mca) 687 { 688 case A_DIGIT: 689 /* 690 * We have a number but no command. Treat as #g. 691 */ 692 return ('g'); 693 694 case A_F_SEARCH: 695 case A_B_SEARCH: 696 /* 697 * We have "/string" but no newline. Add the \n. 698 */ 699 return ('\n'); 700 701 default: 702 /* 703 * Some other incomplete command. Let user complete it. 704 */ 705 return (getchr()); 706 } 707 } 708 709 /* 710 * "Unget" a command character. 711 * The next getcc() will return this character. 712 */ 713 public void 714 ungetcc(c) 715 int c; 716 { 717 if (ungotp == NULL) 718 ungotp = ungot; 719 if (ungotp >= ungot + sizeof(ungot)) 720 { 721 error("ungetcc overflow", NULL_PARG); 722 quit(QUIT_ERROR); 723 } 724 *ungotp++ = c; 725 } 726 727 /* 728 * Unget a whole string of command characters. 729 * The next sequence of getcc()'s will return this string. 730 */ 731 public void 732 ungetsc(s) 733 char *s; 734 { 735 register char *p; 736 737 for (p = s + strlen(s) - 1; p >= s; p--) 738 ungetcc(*p); 739 } 740 741 /* 742 * Search for a pattern, possibly in multiple files. 743 * If SRCH_FIRST_FILE is set, begin searching at the first file. 744 * If SRCH_PAST_EOF is set, continue the search thru multiple files. 745 */ 746 static void 747 multi_search(pattern, n) 748 char *pattern; 749 int n; 750 { 751 register int nomore; 752 IFILE save_ifile; 753 int changed_file; 754 755 changed_file = 0; 756 save_ifile = save_curr_ifile(); 757 758 if (search_type & SRCH_FIRST_FILE) 759 { 760 /* 761 * Start at the first (or last) file 762 * in the command line list. 763 */ 764 if (search_type & SRCH_FORW) 765 nomore = edit_first(); 766 else 767 nomore = edit_last(); 768 if (nomore) 769 { 770 unsave_ifile(save_ifile); 771 return; 772 } 773 changed_file = 1; 774 search_type &= ~SRCH_FIRST_FILE; 775 } 776 777 for (;;) 778 { 779 n = search(search_type, pattern, n); 780 /* 781 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared 782 * after being used once. This allows "n" to work after 783 * using a /@@ search. 784 */ 785 search_type &= ~SRCH_NO_MOVE; 786 if (n == 0) 787 { 788 /* 789 * Found it. 790 */ 791 unsave_ifile(save_ifile); 792 return; 793 } 794 795 if (n < 0) 796 /* 797 * Some kind of error in the search. 798 * Error message has been printed by search(). 799 */ 800 break; 801 802 if ((search_type & SRCH_PAST_EOF) == 0) 803 /* 804 * We didn't find a match, but we're 805 * supposed to search only one file. 806 */ 807 break; 808 /* 809 * Move on to the next file. 810 */ 811 if (search_type & SRCH_FORW) 812 nomore = edit_next(1); 813 else 814 nomore = edit_prev(1); 815 if (nomore) 816 break; 817 changed_file = 1; 818 } 819 820 /* 821 * Didn't find it. 822 * Print an error message if we haven't already. 823 */ 824 if (n > 0) 825 error("Pattern not found", NULL_PARG); 826 827 if (changed_file) 828 { 829 /* 830 * Restore the file we were originally viewing. 831 */ 832 reedit_ifile(save_ifile); 833 } 834 } 835 836 /* 837 * Main command processor. 838 * Accept and execute commands until a quit command. 839 */ 840 public void 841 commands() 842 { 843 register int c; 844 register int action; 845 register char *cbuf; 846 int newaction; 847 int save_search_type; 848 char *extra; 849 char tbuf[2]; 850 PARG parg; 851 IFILE old_ifile; 852 IFILE new_ifile; 853 char *tagfile; 854 855 search_type = SRCH_FORW; 856 wscroll = (sc_height + 1) / 2; 857 newaction = A_NOACTION; 858 859 for (;;) 860 { 861 mca = 0; 862 cmd_accept(); 863 number = 0; 864 optchar = '\0'; 865 866 /* 867 * See if any signals need processing. 868 */ 869 if (sigs) 870 { 871 psignals(); 872 if (quitting) 873 quit(QUIT_SAVED_STATUS); 874 } 875 876 /* 877 * See if window size changed, for systems that don't 878 * generate SIGWINCH. 879 */ 880 check_winch(); 881 882 /* 883 * Display prompt and accept a character. 884 */ 885 cmd_reset(); 886 prompt(); 887 if (sigs) 888 continue; 889 if (newaction == A_NOACTION) 890 c = getcc(); 891 892 again: 893 if (sigs) 894 continue; 895 896 if (newaction != A_NOACTION) 897 { 898 action = newaction; 899 newaction = A_NOACTION; 900 } else 901 { 902 /* 903 * If we are in a multicharacter command, call mca_char. 904 * Otherwise we call fcmd_decode to determine the 905 * action to be performed. 906 */ 907 if (mca) 908 switch (mca_char(c)) 909 { 910 case MCA_MORE: 911 /* 912 * Need another character. 913 */ 914 c = getcc(); 915 goto again; 916 case MCA_DONE: 917 /* 918 * Command has been handled by mca_char. 919 * Start clean with a prompt. 920 */ 921 continue; 922 case NO_MCA: 923 /* 924 * Not a multi-char command 925 * (at least, not anymore). 926 */ 927 break; 928 } 929 930 /* 931 * Decode the command character and decide what to do. 932 */ 933 if (mca) 934 { 935 /* 936 * We're in a multichar command. 937 * Add the character to the command buffer 938 * and display it on the screen. 939 * If the user backspaces past the start 940 * of the line, abort the command. 941 */ 942 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) 943 continue; 944 cbuf = get_cmdbuf(); 945 } else 946 { 947 /* 948 * Don't use cmd_char if we're starting fresh 949 * at the beginning of a command, because we 950 * don't want to echo the command until we know 951 * it is a multichar command. We also don't 952 * want erase_char/kill_char to be treated 953 * as line editing characters. 954 */ 955 tbuf[0] = c; 956 tbuf[1] = '\0'; 957 cbuf = tbuf; 958 } 959 extra = NULL; 960 action = fcmd_decode(cbuf, &extra); 961 /* 962 * If an "extra" string was returned, 963 * process it as a string of command characters. 964 */ 965 if (extra != NULL) 966 ungetsc(extra); 967 } 968 /* 969 * Clear the cmdbuf string. 970 * (But not if we're in the prefix of a command, 971 * because the partial command string is kept there.) 972 */ 973 if (action != A_PREFIX) 974 cmd_reset(); 975 976 switch (action) 977 { 978 case A_DIGIT: 979 /* 980 * First digit of a number. 981 */ 982 start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE); 983 goto again; 984 985 case A_F_WINDOW: 986 /* 987 * Forward one window (and set the window size). 988 */ 989 if (number > 0) 990 swindow = number; 991 /* FALLTHRU */ 992 case A_F_SCREEN: 993 /* 994 * Forward one screen. 995 */ 996 if (number <= 0) 997 number = get_swindow(); 998 cmd_exec(); 999 if (show_attn) 1000 set_attnpos(bottompos); 1001 forward(number, 0, 1); 1002 break; 1003 1004 case A_B_WINDOW: 1005 /* 1006 * Backward one window (and set the window size). 1007 */ 1008 if (number > 0) 1009 swindow = number; 1010 /* FALLTHRU */ 1011 case A_B_SCREEN: 1012 /* 1013 * Backward one screen. 1014 */ 1015 if (number <= 0) 1016 number = get_swindow(); 1017 cmd_exec(); 1018 backward(number, 0, 1); 1019 break; 1020 1021 case A_F_LINE: 1022 /* 1023 * Forward N (default 1) line. 1024 */ 1025 if (number <= 0) 1026 number = 1; 1027 cmd_exec(); 1028 if (show_attn == OPT_ONPLUS && number > 1) 1029 set_attnpos(bottompos); 1030 forward(number, 0, 0); 1031 break; 1032 1033 case A_B_LINE: 1034 /* 1035 * Backward N (default 1) line. 1036 */ 1037 if (number <= 0) 1038 number = 1; 1039 cmd_exec(); 1040 backward(number, 0, 0); 1041 break; 1042 1043 case A_FF_LINE: 1044 /* 1045 * Force forward N (default 1) line. 1046 */ 1047 if (number <= 0) 1048 number = 1; 1049 cmd_exec(); 1050 if (show_attn == OPT_ONPLUS && number > 1) 1051 set_attnpos(bottompos); 1052 forward(number, 1, 0); 1053 break; 1054 1055 case A_BF_LINE: 1056 /* 1057 * Force backward N (default 1) line. 1058 */ 1059 if (number <= 0) 1060 number = 1; 1061 cmd_exec(); 1062 backward(number, 1, 0); 1063 break; 1064 1065 case A_FF_SCREEN: 1066 /* 1067 * Force forward one screen. 1068 */ 1069 if (number <= 0) 1070 number = get_swindow(); 1071 cmd_exec(); 1072 if (show_attn == OPT_ONPLUS) 1073 set_attnpos(bottompos); 1074 forward(number, 1, 0); 1075 break; 1076 1077 case A_F_FOREVER: 1078 /* 1079 * Forward forever, ignoring EOF. 1080 */ 1081 if (ch_getflags() & CH_HELPFILE) 1082 break; 1083 cmd_exec(); 1084 jump_forw(); 1085 ignore_eoi = 1; 1086 hit_eof = 0; 1087 while (!sigs) 1088 forward(1, 0, 0); 1089 ignore_eoi = 0; 1090 /* 1091 * This gets us back in "F mode" after processing 1092 * a non-abort signal (e.g. window-change). 1093 */ 1094 if (sigs && !ABORT_SIGS()) 1095 newaction = A_F_FOREVER; 1096 break; 1097 1098 case A_F_SCROLL: 1099 /* 1100 * Forward N lines 1101 * (default same as last 'd' or 'u' command). 1102 */ 1103 if (number > 0) 1104 wscroll = number; 1105 cmd_exec(); 1106 if (show_attn == OPT_ONPLUS) 1107 set_attnpos(bottompos); 1108 forward(wscroll, 0, 0); 1109 break; 1110 1111 case A_B_SCROLL: 1112 /* 1113 * Forward N lines 1114 * (default same as last 'd' or 'u' command). 1115 */ 1116 if (number > 0) 1117 wscroll = number; 1118 cmd_exec(); 1119 backward(wscroll, 0, 0); 1120 break; 1121 1122 case A_FREPAINT: 1123 /* 1124 * Flush buffers, then repaint screen. 1125 * Don't flush the buffers on a pipe! 1126 */ 1127 if (ch_getflags() & CH_CANSEEK) 1128 { 1129 ch_flush(); 1130 clr_linenum(); 1131 #if HILITE_SEARCH 1132 clr_hilite(); 1133 #endif 1134 } 1135 /* FALLTHRU */ 1136 case A_REPAINT: 1137 /* 1138 * Repaint screen. 1139 */ 1140 cmd_exec(); 1141 repaint(); 1142 break; 1143 1144 case A_GOLINE: 1145 /* 1146 * Go to line N, default beginning of file. 1147 */ 1148 if (number <= 0) 1149 number = 1; 1150 cmd_exec(); 1151 jump_back(number); 1152 break; 1153 1154 case A_PERCENT: 1155 /* 1156 * Go to a specified percentage into the file. 1157 */ 1158 if (number < 0) 1159 number = 0; 1160 if (number > 100) 1161 number = 100; 1162 cmd_exec(); 1163 jump_percent(number); 1164 break; 1165 1166 case A_GOEND: 1167 /* 1168 * Go to line N, default end of file. 1169 */ 1170 cmd_exec(); 1171 if (number <= 0) 1172 jump_forw(); 1173 else 1174 jump_back(number); 1175 break; 1176 1177 case A_GOPOS: 1178 /* 1179 * Go to a specified byte position in the file. 1180 */ 1181 cmd_exec(); 1182 if (number < 0) 1183 number = 0; 1184 jump_line_loc((POSITION)number, jump_sline); 1185 break; 1186 1187 case A_STAT: 1188 /* 1189 * Print file name, etc. 1190 */ 1191 if (ch_getflags() & CH_HELPFILE) 1192 break; 1193 cmd_exec(); 1194 parg.p_string = eq_message(); 1195 error("%s", &parg); 1196 break; 1197 1198 case A_VERSION: 1199 /* 1200 * Print version number, without the "@(#)". 1201 */ 1202 cmd_exec(); 1203 dispversion(); 1204 break; 1205 1206 case A_QUIT: 1207 /* 1208 * Exit. 1209 */ 1210 if (curr_ifile != NULL_IFILE && 1211 ch_getflags() & CH_HELPFILE) 1212 { 1213 /* 1214 * Quit while viewing the help file 1215 * just means return to viewing the 1216 * previous file. 1217 */ 1218 if (edit_prev(1) == 0) 1219 break; 1220 } 1221 if (extra != NULL) 1222 quit(*extra); 1223 quit(QUIT_OK); 1224 break; 1225 1226 /* 1227 * Define abbreviation for a commonly used sequence below. 1228 */ 1229 #define DO_SEARCH() if (number <= 0) number = 1; \ 1230 mca_search(); \ 1231 cmd_exec(); \ 1232 multi_search((char *)NULL, number); 1233 1234 1235 case A_F_SEARCH: 1236 /* 1237 * Search forward for a pattern. 1238 * Get the first char of the pattern. 1239 */ 1240 search_type = SRCH_FORW; 1241 if (number <= 0) 1242 number = 1; 1243 mca_search(); 1244 c = getcc(); 1245 goto again; 1246 1247 case A_B_SEARCH: 1248 /* 1249 * Search backward for a pattern. 1250 * Get the first char of the pattern. 1251 */ 1252 search_type = SRCH_BACK; 1253 if (number <= 0) 1254 number = 1; 1255 mca_search(); 1256 c = getcc(); 1257 goto again; 1258 1259 case A_AGAIN_SEARCH: 1260 /* 1261 * Repeat previous search. 1262 */ 1263 DO_SEARCH(); 1264 break; 1265 1266 case A_T_AGAIN_SEARCH: 1267 /* 1268 * Repeat previous search, multiple files. 1269 */ 1270 search_type |= SRCH_PAST_EOF; 1271 DO_SEARCH(); 1272 break; 1273 1274 case A_REVERSE_SEARCH: 1275 /* 1276 * Repeat previous search, in reverse direction. 1277 */ 1278 save_search_type = search_type; 1279 search_type = SRCH_REVERSE(search_type); 1280 DO_SEARCH(); 1281 search_type = save_search_type; 1282 break; 1283 1284 case A_T_REVERSE_SEARCH: 1285 /* 1286 * Repeat previous search, 1287 * multiple files in reverse direction. 1288 */ 1289 save_search_type = search_type; 1290 search_type = SRCH_REVERSE(search_type); 1291 search_type |= SRCH_PAST_EOF; 1292 DO_SEARCH(); 1293 search_type = save_search_type; 1294 break; 1295 1296 case A_UNDO_SEARCH: 1297 undo_search(); 1298 break; 1299 1300 case A_HELP: 1301 /* 1302 * Help. 1303 */ 1304 if (ch_getflags() & CH_HELPFILE) 1305 break; 1306 cmd_exec(); 1307 (void) edit(FAKE_HELPFILE); 1308 break; 1309 1310 case A_EXAMINE: 1311 #if EXAMINE 1312 /* 1313 * Edit a new file. Get the filename. 1314 */ 1315 if (secure) 1316 { 1317 error("Command not available", NULL_PARG); 1318 break; 1319 } 1320 start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); 1321 c = getcc(); 1322 goto again; 1323 #else 1324 error("Command not available", NULL_PARG); 1325 break; 1326 #endif 1327 1328 case A_VISUAL: 1329 /* 1330 * Invoke an editor on the input file. 1331 */ 1332 #if EDITOR 1333 if (secure) 1334 { 1335 error("Command not available", NULL_PARG); 1336 break; 1337 } 1338 if (ch_getflags() & CH_HELPFILE) 1339 break; 1340 if (strcmp(get_filename(curr_ifile), "-") == 0) 1341 { 1342 error("Cannot edit standard input", NULL_PARG); 1343 break; 1344 } 1345 if (curr_altfilename != NULL) 1346 { 1347 error("Cannot edit file processed with LESSOPEN", 1348 NULL_PARG); 1349 break; 1350 } 1351 start_mca(A_SHELL, "!", ml_shell, 0); 1352 /* 1353 * Expand the editor prototype string 1354 * and pass it to the system to execute. 1355 * (Make sure the screen is displayed so the 1356 * expansion of "+%lm" works.) 1357 */ 1358 make_display(); 1359 cmd_exec(); 1360 lsystem(pr_expand(editproto, 0), (char*)NULL); 1361 break; 1362 #else 1363 error("Command not available", NULL_PARG); 1364 break; 1365 #endif 1366 1367 case A_NEXT_FILE: 1368 /* 1369 * Examine next file. 1370 */ 1371 if (ntags()) 1372 { 1373 error("No next file", NULL_PARG); 1374 break; 1375 } 1376 if (number <= 0) 1377 number = 1; 1378 if (edit_next(number)) 1379 { 1380 if (quit_at_eof && hit_eof && 1381 !(ch_getflags() & CH_HELPFILE)) 1382 quit(QUIT_OK); 1383 parg.p_string = (number > 1) ? "(N-th) " : ""; 1384 error("No %snext file", &parg); 1385 } 1386 break; 1387 1388 case A_PREV_FILE: 1389 /* 1390 * Examine previous file. 1391 */ 1392 if (ntags()) 1393 { 1394 error("No previous file", NULL_PARG); 1395 break; 1396 } 1397 if (number <= 0) 1398 number = 1; 1399 if (edit_prev(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 (number <= 0) 1408 number = 1; 1409 tagfile = nexttag(number); 1410 if (tagfile == NULL) 1411 { 1412 error("No next tag", NULL_PARG); 1413 break; 1414 } 1415 if (edit(tagfile) == 0) 1416 { 1417 POSITION pos = tagsearch(); 1418 if (pos != NULL_POSITION) 1419 jump_loc(pos, jump_sline); 1420 } 1421 break; 1422 1423 case A_PREV_TAG: 1424 if (number <= 0) 1425 number = 1; 1426 tagfile = prevtag(number); 1427 if (tagfile == NULL) 1428 { 1429 error("No previous tag", NULL_PARG); 1430 break; 1431 } 1432 if (edit(tagfile) == 0) 1433 { 1434 POSITION pos = tagsearch(); 1435 if (pos != NULL_POSITION) 1436 jump_loc(pos, jump_sline); 1437 } 1438 break; 1439 1440 case A_INDEX_FILE: 1441 /* 1442 * Examine a particular file. 1443 */ 1444 if (number <= 0) 1445 number = 1; 1446 if (edit_index(number)) 1447 error("No such file", NULL_PARG); 1448 break; 1449 1450 case A_REMOVE_FILE: 1451 if (ch_getflags() & CH_HELPFILE) 1452 break; 1453 old_ifile = curr_ifile; 1454 new_ifile = getoff_ifile(curr_ifile); 1455 if (new_ifile == NULL_IFILE) 1456 { 1457 bell(); 1458 break; 1459 } 1460 if (edit_ifile(new_ifile) != 0) 1461 { 1462 reedit_ifile(old_ifile); 1463 break; 1464 } 1465 del_ifile(old_ifile); 1466 break; 1467 1468 case A_OPT_TOGGLE: 1469 optflag = OPT_TOGGLE; 1470 optgetname = FALSE; 1471 mca_opt_toggle(); 1472 c = getcc(); 1473 goto again; 1474 1475 case A_DISP_OPTION: 1476 /* 1477 * Report a flag setting. 1478 */ 1479 optflag = OPT_NO_TOGGLE; 1480 optgetname = FALSE; 1481 mca_opt_toggle(); 1482 c = getcc(); 1483 goto again; 1484 1485 case A_FIRSTCMD: 1486 /* 1487 * Set an initial command for new files. 1488 */ 1489 start_mca(A_FIRSTCMD, "+", (void*)NULL, 0); 1490 c = getcc(); 1491 goto again; 1492 1493 case A_SHELL: 1494 /* 1495 * Shell escape. 1496 */ 1497 #if SHELL_ESCAPE 1498 if (secure) 1499 { 1500 error("Command not available", NULL_PARG); 1501 break; 1502 } 1503 start_mca(A_SHELL, "!", ml_shell, 0); 1504 c = getcc(); 1505 goto again; 1506 #else 1507 error("Command not available", NULL_PARG); 1508 break; 1509 #endif 1510 1511 case A_SETMARK: 1512 /* 1513 * Set a mark. 1514 */ 1515 if (ch_getflags() & CH_HELPFILE) 1516 break; 1517 start_mca(A_SETMARK, "mark: ", (void*)NULL, 0); 1518 c = getcc(); 1519 if (c == erase_char || c == kill_char || 1520 c == '\n' || c == '\r') 1521 break; 1522 setmark(c); 1523 break; 1524 1525 case A_GOMARK: 1526 /* 1527 * Go to a mark. 1528 */ 1529 start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); 1530 c = getcc(); 1531 if (c == erase_char || c == kill_char || 1532 c == '\n' || c == '\r') 1533 break; 1534 gomark(c); 1535 break; 1536 1537 case A_PIPE: 1538 #if PIPEC 1539 if (secure) 1540 { 1541 error("Command not available", NULL_PARG); 1542 break; 1543 } 1544 start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); 1545 c = getcc(); 1546 if (c == erase_char || c == kill_char) 1547 break; 1548 if (c == '\n' || c == '\r') 1549 c = '.'; 1550 if (badmark(c)) 1551 break; 1552 pipec = c; 1553 start_mca(A_PIPE, "!", ml_shell, 0); 1554 c = getcc(); 1555 goto again; 1556 #else 1557 error("Command not available", NULL_PARG); 1558 break; 1559 #endif 1560 1561 case A_B_BRACKET: 1562 case A_F_BRACKET: 1563 start_mca(action, "Brackets: ", (void*)NULL, 0); 1564 c = getcc(); 1565 goto again; 1566 1567 case A_LSHIFT: 1568 if (number > 0) 1569 shift_count = number; 1570 else 1571 number = (shift_count > 0) ? 1572 shift_count : sc_width / 2; 1573 if (number > hshift) 1574 number = hshift; 1575 hshift -= number; 1576 screen_trashed = 1; 1577 break; 1578 1579 case A_RSHIFT: 1580 if (number > 0) 1581 shift_count = number; 1582 else 1583 number = (shift_count > 0) ? 1584 shift_count : sc_width / 2; 1585 hshift += number; 1586 screen_trashed = 1; 1587 break; 1588 1589 case A_PREFIX: 1590 /* 1591 * The command is incomplete (more chars are needed). 1592 * Display the current char, so the user knows 1593 * what's going on, and get another character. 1594 */ 1595 if (mca != A_PREFIX) 1596 { 1597 cmd_reset(); 1598 start_mca(A_PREFIX, " ", (void*)NULL, 1599 CF_QUIT_ON_ERASE); 1600 (void) cmd_char(c); 1601 } 1602 c = getcc(); 1603 goto again; 1604 1605 case A_NOACTION: 1606 break; 1607 1608 default: 1609 bell(); 1610 break; 1611 } 1612 } 1613 } 1614