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