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