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