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