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