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