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