1 /* 2 * Copyright (C) 1984-2026 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 11 /* 12 * User-level command processor. 13 */ 14 15 #include "less.h" 16 #if MSDOS_COMPILER==WIN32C 17 #include <windows.h> 18 #endif 19 #include "position.h" 20 #include "option.h" 21 #include "cmd.h" 22 23 extern int erase_char, erase2_char, kill_char; 24 extern int sigs; 25 extern int quit_if_one_screen; 26 extern lbool one_screen; 27 extern int sc_width; 28 extern int sc_height; 29 extern char *kent; 30 extern lbool kent_mapped; 31 extern int swindow; 32 extern int jump_sline; 33 extern lbool quitting; 34 extern int wscroll; 35 extern int top_scroll; 36 extern lbool ignore_eoi; 37 extern int hshift; 38 extern int bs_mode; 39 extern int proc_backspace; 40 extern int show_attn; 41 extern lbool less_is_more; 42 extern int chopline; 43 extern POSITION highest_hilite; 44 extern char *every_first_cmd; 45 extern char version[]; 46 extern struct scrpos initial_scrpos; 47 extern IFILE curr_ifile; 48 extern void *ml_search; 49 extern void *ml_examine; 50 extern int wheel_lines; 51 extern int def_search_type; 52 extern int hilite_target; 53 extern lbool search_wrapped; 54 extern int no_paste; 55 extern lbool pasting; 56 extern int no_edit_warn; 57 extern lbool read_error; 58 extern POSITION soft_eof; 59 extern POSITION search_incr_start; 60 extern char *first_cmd_at_prompt; 61 extern lbool prompting; 62 #if SHELL_ESCAPE || PIPEC 63 extern void *ml_shell; 64 #endif 65 #if EDITOR 66 extern constant char *editproto; 67 #endif 68 #if OSC8_LINK 69 extern char *osc8_uri; 70 #endif 71 extern int shift_count; 72 extern lbool forw_prompt; 73 extern int incr_search; 74 extern lbool full_screen; 75 #if MSDOS_COMPILER==WIN32C 76 extern int utf_mode; 77 extern unsigned less_acp; 78 #endif 79 80 #if SHELL_ESCAPE 81 static char *shellcmd = NULL; /* For holding last shell command for "!!" */ 82 #endif 83 static int mca; /* The multicharacter command (action) */ 84 static int search_type; /* The previous type of search */ 85 static int last_search_type; /* Type of last executed search */ 86 static LINENUM number; /* The number typed by the user */ 87 static long fraction; /* The fractional part of the number */ 88 static struct loption *curropt; 89 static lbool opt_lower; 90 static int optflag; 91 static lbool optgetname; 92 static POSITION toppos; 93 static POSITION bottompos; 94 static int save_hshift; 95 static int save_bs_mode; 96 static int save_proc_backspace; 97 static int screen_trashed_value = 0; 98 static lbool literal_char = FALSE; 99 static lbool ignoring_input = FALSE; 100 static struct scrpos search_incr_pos = { NULL_POSITION, 0 }; 101 static int search_incr_hshift; 102 #if HAVE_TIME 103 static time_type ignoring_input_time; 104 #endif 105 #if PIPEC 106 static POSITION pipe_pos1; 107 static POSITION pipe_pos2; 108 #endif 109 110 /* Stack of ungotten chars (via ungetcc) */ 111 struct ungot { 112 struct ungot *ug_next; 113 char ug_char; 114 lbool ug_end_command; 115 }; 116 static struct ungot* ungot = NULL; 117 118 static void multi_search(constant char *pattern, int n, int silent); 119 120 /* 121 * Move the cursor to start of prompt line before executing a command. 122 * This looks nicer if the command takes a long time before 123 * updating the screen. 124 */ 125 public void cmd_exec(void) 126 { 127 clear_attn(); 128 clear_bot(); 129 flush(); 130 } 131 132 /* 133 * Indicate we are reading a multi-character command. 134 */ 135 static void set_mca(int action) 136 { 137 mca = action; 138 clear_bot(); 139 clear_cmd(); 140 } 141 142 /* 143 * Indicate we are not reading a multi-character command. 144 */ 145 static void clear_mca(void) 146 { 147 if (mca == 0) 148 return; 149 mca = 0; 150 } 151 152 /* 153 * Set up the display to start a new multi-character command. 154 */ 155 static void start_mca(int action, constant char *prompt, void *mlist, int cmdflags) 156 { 157 set_mca(action); 158 cmd_putstr(prompt); 159 set_mlist(mlist, cmdflags); 160 } 161 162 public lbool in_mca(void) 163 { 164 return (mca != 0 && mca != A_PREFIX); 165 } 166 167 /* 168 * Set up the display to start a new search command. 169 */ 170 static void mca_search1(void) 171 { 172 int i; 173 174 #if HILITE_SEARCH 175 if (search_type & SRCH_FILTER) 176 set_mca(A_FILTER); 177 else 178 #endif 179 if (search_type & SRCH_FORW) 180 set_mca(A_F_SEARCH); 181 else 182 set_mca(A_B_SEARCH); 183 184 if (search_type & SRCH_NO_MATCH) 185 cmd_putstr(LM(Non_match)); 186 if (search_type & SRCH_FIRST_FILE) 187 cmd_putstr(LM(First_file)); 188 if (search_type & SRCH_PAST_EOF) 189 cmd_putstr(LM(EOF_ignore)); 190 if (search_type & SRCH_NO_MOVE) 191 cmd_putstr(LM(Keep_pos)); 192 if (search_type & SRCH_NO_REGEX) 193 cmd_putstr(LM(Regex_off)); 194 if (search_type & SRCH_WRAP) 195 cmd_putstr(LM(Wrap)); 196 for (i = 1; i <= NUM_SEARCH_COLORS; i++) 197 { 198 if (search_type & SRCH_SUBSEARCH(i)) 199 { 200 static char *buf = NULL; 201 static size_t buflen; 202 if (buf == NULL) 203 { 204 buflen = INT_STRLEN_BOUND(int)+strlen(LM(Sub_X)+1); 205 buf = ecalloc(buflen, sizeof(char)); 206 } 207 SNPRINTF1(buf, buflen, LM(Sub_X), i); 208 cmd_putstr(buf); 209 } 210 } 211 if (literal_char) 212 cmd_putstr(LM(Lit)); 213 214 #if HILITE_SEARCH 215 if (search_type & SRCH_FILTER) 216 cmd_putstr("&/"); 217 else 218 #endif 219 if (search_type & SRCH_FORW) 220 cmd_putstr("/"); 221 else 222 cmd_putstr("?"); 223 forw_prompt = FALSE; 224 } 225 226 static void mca_search(void) 227 { 228 if (incr_search) 229 { 230 /* Remember where the incremental search started. */ 231 get_scrpos(&search_incr_pos, TOP); 232 search_incr_start = search_pos(search_type); 233 search_incr_hshift = hshift; 234 } 235 mca_search1(); 236 set_mlist(ml_search, 0); 237 } 238 239 /* 240 * Set up the display to start a new toggle-option command. 241 */ 242 static void mca_opt_toggle(void) 243 { 244 int no_prompt = (optflag & OPT_NO_PROMPT); 245 int flag = (optflag & ~OPT_NO_PROMPT); 246 constant char *dash = (flag == OPT_NO_TOGGLE) ? "_" : "-"; 247 248 set_mca(A_OPT_TOGGLE); 249 cmd_putstr(dash); 250 if (optgetname) 251 cmd_putstr(dash); 252 if (no_prompt) 253 cmd_putstr("(P)"); 254 switch (flag) 255 { 256 case OPT_UNSET: 257 cmd_putstr("+"); 258 break; 259 case OPT_SET: 260 cmd_putstr("!"); 261 break; 262 } 263 forw_prompt = FALSE; 264 set_mlist(NULL, CF_OPTION); 265 } 266 267 /* 268 * Execute a multicharacter command. 269 */ 270 static void exec_mca(void) 271 { 272 constant char *cbuf; 273 274 cmd_exec(); 275 cbuf = get_cmdbuf(); 276 if (cbuf == NULL) 277 return; 278 279 switch (mca) 280 { 281 case A_F_SEARCH: 282 case A_B_SEARCH: 283 multi_search(cbuf, (int) number, 0); 284 break; 285 #if HILITE_SEARCH 286 case A_FILTER: 287 search_type ^= SRCH_NO_MATCH; 288 set_filter_pattern(cbuf, search_type); 289 soft_eof = NULL_POSITION; 290 break; 291 #endif 292 case A_FIRSTCMD: 293 /* 294 * Skip leading spaces or + signs in the string. 295 */ 296 while (*cbuf == '+' || *cbuf == ' ') 297 cbuf++; 298 if (every_first_cmd != NULL) 299 free(every_first_cmd); 300 if (*cbuf == '\0') 301 every_first_cmd = NULL; 302 else 303 every_first_cmd = save(cbuf); 304 break; 305 case A_OPT_TOGGLE: 306 toggle_option(curropt, opt_lower, cbuf, optflag); 307 curropt = NULL; 308 break; 309 case A_F_BRACKET: 310 match_brac(cbuf[0], cbuf[1], TRUE, (int) number); 311 break; 312 case A_B_BRACKET: 313 match_brac(cbuf[1], cbuf[0], FALSE, (int) number); 314 break; 315 #if EXAMINE 316 case A_EXAMINE: { 317 char *p; 318 if (!secure_allow(SF_EXAMINE)) 319 break; 320 p = save(cbuf); 321 edit_list(p); 322 free(p); 323 #if TAGS 324 /* If tag structure is loaded then clean it up. */ 325 cleantags(); 326 #endif 327 break; } 328 #endif 329 #if SHELL_ESCAPE 330 case A_SHELL: { 331 /* 332 * !! just uses whatever is in shellcmd. 333 * Otherwise, copy cmdbuf to shellcmd, 334 * expanding any special characters ("%" or "#"). 335 */ 336 constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "!done"; 337 if (done_msg == NULL) 338 ++cbuf; 339 if (strcmp(cbuf, "!") != 0) 340 { 341 if (shellcmd != NULL) 342 free(shellcmd); 343 shellcmd = fexpand(cbuf); 344 } 345 if (!secure_allow(SF_SHELL)) 346 break; 347 lsystem(shellcmd == NULL ? "" : shellcmd, done_msg); 348 break; } 349 case A_PSHELL: { 350 constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "#done"; 351 if (done_msg == NULL) 352 ++cbuf; 353 if (!secure_allow(SF_SHELL)) 354 break; 355 lsystem(pr_expand(cbuf), done_msg); 356 break; } 357 #endif 358 #if PIPEC 359 case A_PIPE: { 360 constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "|done"; 361 if (done_msg == NULL) 362 ++cbuf; 363 if (!secure_allow(SF_PIPE)) 364 break; 365 (void) pipe_pos(cbuf, pipe_pos1, pipe_pos2); 366 if (done_msg != NULL) 367 error(done_msg, NULL_PARG); 368 break; } 369 #endif 370 } 371 } 372 373 /* 374 * Is a character an erase or kill char? 375 */ 376 static lbool is_erase_char(char c) 377 { 378 return (c == erase_char || c == erase2_char || c == kill_char); 379 } 380 381 /* 382 * Is a character a carriage return or newline? 383 */ 384 static lbool is_newline_char(char c) 385 { 386 return (c == '\n' || c == '\r'); 387 } 388 389 /* 390 * Handle the first char of an option (after the initial dash). 391 */ 392 static int mca_opt_first_char(char c) 393 { 394 int no_prompt = (optflag & OPT_NO_PROMPT); 395 int flag = (optflag & ~OPT_NO_PROMPT); 396 if (flag == OPT_NO_TOGGLE) 397 { 398 switch (c) 399 { 400 case '_': 401 /* "__" = long option name. */ 402 optgetname = TRUE; 403 mca_opt_toggle(); 404 return (MCA_MORE); 405 } 406 } else 407 { 408 switch (c) 409 { 410 case '+': 411 /* "-+" = UNSET. */ 412 optflag = no_prompt | ((flag == OPT_UNSET) ? 413 OPT_TOGGLE : OPT_UNSET); 414 mca_opt_toggle(); 415 return (MCA_MORE); 416 case '!': 417 /* "-!" = SET */ 418 optflag = no_prompt | ((flag == OPT_SET) ? 419 OPT_TOGGLE : OPT_SET); 420 mca_opt_toggle(); 421 return (MCA_MORE); 422 case CONTROL('P'): 423 optflag ^= OPT_NO_PROMPT; 424 mca_opt_toggle(); 425 return (MCA_MORE); 426 case '-': 427 /* "--" = long option name. */ 428 optgetname = TRUE; 429 mca_opt_toggle(); 430 return (MCA_MORE); 431 } 432 } 433 /* Char was not handled here. */ 434 return (NO_MCA); 435 } 436 437 /* 438 * Add a char to a long option name. 439 * See if we've got a match for an option name yet. 440 * If so, display the complete name and stop 441 * accepting chars until user hits RETURN. 442 */ 443 static int mca_opt_nonfirst_char(char c) 444 { 445 constant char *p; 446 constant char *oname; 447 lbool ambig; 448 struct loption *was_curropt; 449 450 if (curropt != NULL) 451 { 452 /* Already have a match for the name. */ 453 if (is_erase_char(c)) 454 return (MCA_DONE); 455 /* {{ Checking for TAB here is ugly. 456 * Also doesn't extend well -- can't do BACKTAB this way 457 * because it's a multichar sequence. }} */ 458 if (c != '\t') 459 return (MCA_MORE); 460 } 461 /* 462 * Add char to cmd buffer and try to match 463 * the option name. 464 */ 465 if (cmd_char(c) == CC_QUIT) 466 return (MCA_DONE); 467 p = get_cmdbuf(); 468 if (p == NULL || p[0] == '\0') 469 return (MCA_MORE); 470 opt_lower = ASCII_IS_LOWER(p[0]); 471 was_curropt = curropt; 472 curropt = findopt_name(&p, &oname, &ambig); 473 if (curropt != NULL) 474 { 475 if (was_curropt == NULL) 476 { 477 /* 478 * Got a match. 479 * Remember the option and 480 * display the full option name. 481 */ 482 cmd_reset(); 483 mca_opt_toggle(); 484 cmd_setstring(oname, !opt_lower); 485 } 486 } else if (!ambig) 487 { 488 lbell(); 489 } 490 return (MCA_MORE); 491 } 492 493 /* 494 * Handle a char of an option toggle command. 495 */ 496 static int mca_opt_char(char c) 497 { 498 PARG parg; 499 500 /* 501 * This may be a short option (single char), 502 * or one char of a long option name, 503 * or one char of the option parameter. 504 */ 505 if (curropt == NULL && cmdbuf_empty()) 506 { 507 int ret = mca_opt_first_char(c); 508 if (ret != NO_MCA) 509 return (ret); 510 } 511 if (optgetname) 512 { 513 /* We're getting a long option name. */ 514 if (!is_newline_char(c) && c != '=') 515 return (mca_opt_nonfirst_char(c)); 516 if (curropt == NULL) 517 { 518 parg.p_string = get_cmdbuf(); 519 if (parg.p_string == NULL) 520 return (MCA_MORE); 521 error(LM(There_is_no__X_option), &parg); 522 return (MCA_DONE); 523 } 524 optgetname = FALSE; 525 cmd_reset(); 526 } else 527 { 528 if (is_erase_char(c)) 529 return (NO_MCA); 530 if (curropt != NULL) 531 /* We're getting the option parameter. */ 532 return (NO_MCA); 533 curropt = findopt(c); 534 if (curropt == NULL) 535 { 536 parg.p_string = propt(c); 537 error(LM(There_is_no_X_option), &parg); 538 return (MCA_DONE); 539 } 540 opt_lower = ASCII_IS_LOWER(c); 541 } 542 /* 543 * If the option which was entered does not take a 544 * parameter, toggle the option immediately, 545 * so user doesn't have to hit RETURN. 546 */ 547 if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE || 548 !opt_has_param(curropt)) 549 { 550 toggle_option(curropt, opt_lower, "", optflag); 551 return (MCA_DONE); 552 } 553 /* 554 * Display a prompt appropriate for the option parameter. 555 */ 556 start_mca(A_OPT_TOGGLE, opt_prompt(curropt), NULL, CF_OPTION); 557 return (MCA_MORE); 558 } 559 560 /* 561 * Normalize search type. 562 */ 563 public int norm_search_type(int st) 564 { 565 /* WRAP and PAST_EOF are mutually exclusive. */ 566 if ((st & (SRCH_PAST_EOF|SRCH_WRAP)) == (SRCH_PAST_EOF|SRCH_WRAP)) 567 st ^= SRCH_PAST_EOF; 568 return st; 569 } 570 571 /* 572 * Handle a char of a search command. 573 */ 574 static int mca_search_char(char c) 575 { 576 int flag = 0; 577 578 /* 579 * Certain characters as the first char of 580 * the pattern have special meaning: 581 * ! Toggle the NO_MATCH flag 582 * * Toggle the PAST_EOF flag 583 * @ Toggle the FIRST_FILE flag 584 */ 585 if (!cmdbuf_empty() || literal_char) 586 { 587 lbool was_literal_char = literal_char; 588 literal_char = FALSE; 589 if (was_literal_char) 590 mca_search1(); 591 return (NO_MCA); 592 } 593 594 switch (c) 595 { 596 case '*': 597 if (less_is_more) 598 break; 599 case CONTROL('E'): /* ignore END of file */ 600 if (mca != A_FILTER) 601 flag = SRCH_PAST_EOF; 602 search_type &= ~SRCH_WRAP; 603 break; 604 case '@': 605 if (less_is_more) 606 break; 607 case CONTROL('F'): /* FIRST file */ 608 if (mca != A_FILTER) 609 flag = SRCH_FIRST_FILE; 610 break; 611 case CONTROL('K'): /* KEEP position */ 612 if (mca != A_FILTER) 613 flag = SRCH_NO_MOVE; 614 break; 615 case CONTROL('S'): { /* SUBSEARCH */ 616 static char *buf = NULL; 617 static size_t buflen; 618 if (buf == NULL) 619 { 620 buflen = INT_STRLEN_BOUND(int)+strlen(LM(Sub_pattern_1_X))+1; 621 buf = ecalloc(buflen, sizeof(char)); 622 } 623 SNPRINTF1(buf, buflen, LM(Sub_pattern_1_X), NUM_SEARCH_COLORS); 624 clear_bot(); 625 cmd_putstr(buf); 626 flush(); 627 c = getcc(); 628 if (c >= '1' && c <= '0'+NUM_SEARCH_COLORS) 629 flag = SRCH_SUBSEARCH(c-'0'); 630 else 631 flag = -1; /* calls mca_search() below to repaint */ 632 break; } 633 case CONTROL('W'): /* WRAP around */ 634 if (mca != A_FILTER) 635 flag = SRCH_WRAP; 636 break; 637 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ 638 flag = SRCH_NO_REGEX; 639 break; 640 case CONTROL('N'): /* NOT match */ 641 case '!': 642 flag = SRCH_NO_MATCH; 643 break; 644 case CONTROL('L'): 645 literal_char = TRUE; 646 flag = -1; 647 break; 648 } 649 650 if (flag != 0) 651 { 652 if (flag != -1) 653 search_type = norm_search_type(search_type ^ flag); 654 mca_search(); 655 return (MCA_MORE); 656 } 657 return (NO_MCA); 658 } 659 660 /* 661 * Jump back to the starting position of an incremental search. 662 */ 663 static void jump_search_incr_pos(void) 664 { 665 if (search_incr_pos.pos == NULL_POSITION) 666 return; 667 hshift = search_incr_hshift; 668 jump_loc(search_incr_pos.pos, search_incr_pos.ln); 669 } 670 671 /* 672 * Handle a character of a multi-character command. 673 */ 674 static int mca_char(char c) 675 { 676 int ret; 677 678 switch (mca) 679 { 680 case 0: 681 /* 682 * We're not in a multicharacter command. 683 */ 684 return (NO_MCA); 685 686 case A_PREFIX: 687 /* 688 * In the prefix of a command. 689 * This not considered a multichar command 690 * (even tho it uses cmdbuf, etc.). 691 * It is handled in the commands() switch. 692 */ 693 return (NO_MCA); 694 695 case A_DIGIT: 696 /* 697 * Entering digits of a number. 698 * Terminated by a non-digit. 699 */ 700 if ((c >= '0' && c <= '9') || c == '.') 701 break; 702 switch (editchar(c, ECF_PEEK|ECF_NOHISTORY|ECF_NOCOMPLETE|ECF_NORIGHTLEFT)) 703 { 704 case A_NOACTION: 705 /* 706 * Ignore this char and get another one. 707 */ 708 return (MCA_MORE); 709 case A_INVALID: 710 /* 711 * Not part of the number. 712 * End the number and treat this char 713 * as a normal command character. 714 */ 715 number = cmd_int(&fraction); 716 clear_mca(); 717 cmd_accept(); 718 return (NO_MCA); 719 } 720 break; 721 722 case A_OPT_TOGGLE: 723 ret = mca_opt_char(c); 724 if (ret != NO_MCA) 725 return (ret); 726 break; 727 728 case A_F_SEARCH: 729 case A_B_SEARCH: 730 case A_FILTER: 731 ret = mca_search_char(c); 732 if (ret != NO_MCA) 733 return (ret); 734 break; 735 736 default: 737 /* Other multicharacter command. */ 738 break; 739 } 740 741 /* 742 * The multichar command is terminated by a newline. 743 */ 744 if (is_newline_char(c)) 745 { 746 if (pasting && no_paste) 747 { 748 /* Ignore pasted input after (and including) the first newline */ 749 start_ignoring_input(); 750 return (MCA_MORE); 751 } 752 /* Execute the command. */ 753 exec_mca(); 754 return (MCA_DONE); 755 } 756 757 /* 758 * Append the char to the command buffer. 759 */ 760 if (cmd_char(c) == CC_QUIT) 761 /* 762 * Abort the multi-char command. 763 */ 764 return (MCA_DONE); 765 766 switch (mca) 767 { 768 case A_F_BRACKET: 769 case A_B_BRACKET: 770 if (len_cmdbuf() >= 2) 771 { 772 /* 773 * Special case for the bracket-matching commands. 774 * Execute the command after getting exactly two 775 * characters from the user. 776 */ 777 exec_mca(); 778 return (MCA_DONE); 779 } 780 break; 781 case A_F_SEARCH: 782 case A_B_SEARCH: 783 if (incr_search) 784 { 785 /* Incremental search: do a search after every input char. */ 786 int st = (search_type & (SRCH_FORW|SRCH_BACK|SRCH_NO_MATCH|SRCH_NO_REGEX|SRCH_NO_MOVE|SRCH_WRAP|SRCH_SUBSEARCH_ALL)); 787 ssize_t save_updown; 788 constant char *pattern = get_cmdbuf(); 789 if (pattern == NULL) 790 return (MCA_MORE); 791 /* Defer searching if more chars of the pattern are available. */ 792 if (ttyin_ready()) 793 return (MCA_MORE); 794 /* 795 * Must save updown_match because mca_search 796 * reinits it. That breaks history scrolling. 797 * {{ This is ugly. mca_search probably shouldn't call set_mlist. }} 798 */ 799 save_updown = save_updown_match(); 800 cmd_exec(); 801 if (*pattern == '\0') 802 { 803 /* User has backspaced to an empty pattern. */ 804 undo_search(TRUE); 805 jump_search_incr_pos(); 806 } else 807 { 808 if (search(st | SRCH_INCR, pattern, 1) != 0) 809 { 810 /* No match, invalid pattern, etc. */ 811 undo_search(TRUE); 812 jump_search_incr_pos(); 813 } 814 } 815 /* Redraw the search prompt and search string. */ 816 if (is_screen_trashed() || !full_screen) 817 { 818 lclear(); 819 repaint(); 820 } 821 mca_search1(); 822 restore_updown_match(save_updown); 823 cmd_repaint(NULL); 824 } 825 break; 826 } 827 828 /* 829 * Need another character. 830 */ 831 return (MCA_MORE); 832 } 833 834 /* 835 * Discard any buffered file data. 836 */ 837 static void clear_buffers(void) 838 { 839 if (!(ch_getflags() & CH_CANSEEK)) 840 return; 841 ch_flush(); 842 clr_linenum(); 843 #if HILITE_SEARCH 844 clr_hilite(); 845 #endif 846 set_line_contig_pos(NULL_POSITION); 847 } 848 849 public void screen_trashed_num(int trashed) 850 { 851 screen_trashed_value = trashed; 852 } 853 854 public void screen_trashed(void) 855 { 856 screen_trashed_num(1); 857 } 858 859 public int is_screen_trashed(void) 860 { 861 return screen_trashed_value; 862 } 863 864 /* 865 * Make sure the screen is displayed. 866 */ 867 static void make_display(void) 868 { 869 /* 870 * If nothing is displayed yet, display starting from initial_scrpos. 871 */ 872 if (empty_screen()) 873 { 874 if (initial_scrpos.pos == NULL_POSITION) 875 jump_loc(ch_zero(), 1); 876 else 877 jump_loc(initial_scrpos.pos, initial_scrpos.ln); 878 } else if (is_screen_trashed() || !full_screen) 879 { 880 int save_top_scroll = top_scroll; 881 lbool save_ignore_eoi = ignore_eoi; 882 top_scroll = 1; 883 ignore_eoi = FALSE; 884 if (is_screen_trashed() == 2) 885 { 886 /* Special case used by ignore_eoi: re-open the input file 887 * and jump to the end of the file. */ 888 reopen_curr_ifile(); 889 jump_forw(); 890 } 891 repaint(); 892 top_scroll = save_top_scroll; 893 ignore_eoi = save_ignore_eoi; 894 } 895 } 896 897 /* 898 * Display any message that needs to be displayed before the prompt. 899 */ 900 static void prompt_message(void) 901 { 902 if (read_error) 903 { 904 error(LM(read_error), NULL_PARG); 905 read_error = FALSE; 906 } 907 if (search_wrapped) 908 { 909 if (search_type & SRCH_BACK) 910 error(LM(Search_hit_top), NULL_PARG); 911 else 912 error(LM(Search_hit_bottom), NULL_PARG); 913 search_wrapped = FALSE; 914 } 915 #if OSC8_LINK 916 if (osc8_uri != NULL) 917 { 918 PARG parg; 919 parg.p_string = osc8_uri; 920 error(LM(Link_X), &parg); 921 free(osc8_uri); 922 osc8_uri = NULL; 923 } 924 #endif 925 } 926 927 /* 928 * Display the appropriate prompt. 929 */ 930 static void prompt(void) 931 { 932 constant char *p; 933 int attr; 934 #if MSDOS_COMPILER==WIN32C 935 WCHAR w[MAX_PATH*2]; 936 char a[MAX_PATH*2]; 937 #endif 938 939 if (ungot != NULL && !ungot->ug_end_command) 940 { 941 /* 942 * No prompt necessary if commands are from 943 * ungotten chars rather than from the user. 944 */ 945 return; 946 } 947 948 /* 949 * Make sure the screen is displayed. 950 */ 951 make_display(); 952 953 if (hilite_target) 954 draw_target_attn(TRUE); /* Redraw target line for --hilite-target. */ 955 toppos = position(TOP); 956 bottompos = position(BOTTOM_PLUS_ONE); 957 958 /* 959 * If we've hit EOF on the last file and the -E flag is set, quit. 960 */ 961 if (get_quit_at_eof() == OPT_ONPLUS && 962 eof_displayed(FALSE) && !(ch_getflags() & CH_HELPFILE) && 963 next_ifile(curr_ifile) == NULL_IFILE) 964 quit(QUIT_OK); 965 966 /* 967 * If the entire file is displayed and the -F flag is set, quit. 968 */ 969 if (quit_if_one_screen && 970 entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && 971 next_ifile(curr_ifile) == NULL_IFILE) 972 quit(QUIT_OK); 973 quit_if_one_screen = 0; /* only get one chance at this */ 974 if (first_cmd_at_prompt != NULL) 975 { 976 ungetsc(first_cmd_at_prompt); 977 first_cmd_at_prompt = NULL; 978 return; 979 } 980 981 #if MSDOS_COMPILER==WIN32C 982 /* 983 * In Win32, display the file name in the window title. 984 * {{ Seems like this should be done in edit_ifile, not on every prompt. }} 985 */ 986 if (!(ch_getflags() & CH_HELPFILE)) 987 { 988 WCHAR w[MAX_PATH+16]; 989 p = pr_expand("Less?f - %f."); 990 MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w)); 991 SetConsoleTitleW(w); 992 } 993 #endif 994 995 /* 996 * Select the proper prompt and display it. 997 */ 998 /* 999 * If the previous action was a forward movement, 1000 * don't clear the bottom line of the display; 1001 * just print the prompt since the forward movement guarantees 1002 * that we're in the right position to display the prompt. 1003 * Clearing the line could cause a problem: for example, if the last 1004 * line displayed ended at the right screen edge without a newline, 1005 * then clearing would clear the last displayed line rather than 1006 * the prompt line. 1007 */ 1008 if (!forw_prompt) 1009 clear_bot(); 1010 clear_cmd(); 1011 forw_prompt = FALSE; 1012 prompt_message(); 1013 /* We called make_display above, but if prompt_message displayed 1014 * a message longer than the screen width, we may have trashed 1015 * the screen and need to call make_display again. */ 1016 if (is_screen_trashed()) 1017 make_display(); 1018 p = pr_string(); 1019 if (p == NULL || *p == '\0') 1020 { 1021 p = ":"; 1022 attr = AT_NORMAL|AT_COLOR_PROMPT; 1023 } else 1024 { 1025 attr = AT_STANDOUT|AT_COLOR_PROMPT; 1026 #if MSDOS_COMPILER==WIN32C 1027 MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w)); 1028 WideCharToMultiByte(utf_mode ? CP_UTF8 : GetConsoleOutputCP(), 1029 0, w, -1, a, sizeof(a), NULL, NULL); 1030 p = a; 1031 #endif 1032 } 1033 #if HILITE_SEARCH 1034 if (is_filtering()) 1035 { 1036 constant char *amp = "& "; 1037 load_line(p, attr, strlen(amp)+1); 1038 putstr(amp); 1039 } else 1040 #endif 1041 { 1042 load_line(p, attr, 1); 1043 } 1044 put_line(FALSE); 1045 clear_eol(); 1046 resume_screen(); 1047 prompting = TRUE; 1048 } 1049 1050 /* 1051 * Display the less version message. 1052 */ 1053 public void dispversion(void) 1054 { 1055 PARG parg; 1056 1057 parg.p_string = version; 1058 error("less %s", &parg); 1059 } 1060 1061 /* 1062 * Return a character to complete a partial command, if possible. 1063 */ 1064 static char getcc_end_command(void) 1065 { 1066 int ch; 1067 switch (mca) 1068 { 1069 case A_DIGIT: 1070 /* We have a number but no command. Treat as #g. */ 1071 return ('g'); 1072 case A_F_SEARCH: 1073 case A_B_SEARCH: 1074 case A_FILTER: 1075 /* We have "/string" but no newline. Add the \n. */ 1076 return ('\n'); 1077 default: 1078 /* Some other incomplete command. Let user complete it. */ 1079 if (ungot != NULL) 1080 return ('\0'); 1081 ch = getchr(); 1082 if (ch < 0) ch = '\0'; 1083 return (char) ch; 1084 } 1085 } 1086 1087 /* 1088 * Get a command character from the ungotten stack. 1089 */ 1090 static char get_ungot(lbool *p_end_command) 1091 { 1092 struct ungot *ug = ungot; 1093 char c = ug->ug_char; 1094 if (p_end_command != NULL) 1095 *p_end_command = ug->ug_end_command; 1096 ungot = ug->ug_next; 1097 free(ug); 1098 return c; 1099 } 1100 1101 /* 1102 * Delete all ungotten characters. 1103 */ 1104 public void getcc_clear(void) 1105 { 1106 while (ungot != NULL) 1107 (void) get_ungot(NULL); 1108 } 1109 1110 /* 1111 * Get command character. 1112 * The character normally comes from the keyboard, 1113 * but may come from ungotten characters 1114 * (characters previously given to ungetcc or ungetsc). 1115 */ 1116 static char getccu(void) 1117 { 1118 int c = 0; 1119 while (c == 0 && sigs == 0) 1120 { 1121 if (ungot == NULL) 1122 { 1123 /* Normal case: no ungotten chars. 1124 * Get char from the user. */ 1125 c = getchr(); 1126 if (c < 0) c = '\0'; 1127 } else 1128 { 1129 /* Ungotten chars available: 1130 * Take the top of stack (most recent). */ 1131 lbool end_command; 1132 c = get_ungot(&end_command); 1133 if (end_command) 1134 c = getcc_end_command(); 1135 } 1136 } 1137 return ((char) c); 1138 } 1139 1140 /* 1141 * Get a command character, but if we receive the orig sequence, 1142 * convert it to the repl sequence. 1143 */ 1144 static char getcc_repl(char constant *orig, char constant *repl, char (*gr_getc)(void), void (*gr_ungetc)(char)) 1145 { 1146 char c; 1147 char keys[16]; 1148 size_t ki = 0; 1149 1150 c = (*gr_getc)(); 1151 if (orig == NULL || orig[0] == '\0') 1152 return c; 1153 for (;;) 1154 { 1155 keys[ki] = c; 1156 if (c != orig[ki] || ki >= sizeof(keys)-1) 1157 { 1158 /* This is not orig we have been receiving. 1159 * If we have stashed chars in keys[], 1160 * unget them and return the first one. */ 1161 while (ki > 0) 1162 (*gr_ungetc)(keys[ki--]); 1163 return keys[0]; 1164 } 1165 if (orig[++ki] == '\0') 1166 { 1167 /* We've received the full orig sequence. 1168 * Return the repl sequence. */ 1169 ki = strlen(repl)-1; 1170 while (ki > 0) 1171 (*gr_ungetc)(repl[ki--]); 1172 return repl[0]; 1173 } 1174 /* We've received a partial orig sequence (ki chars of it). 1175 * Get next char and see if it continues to match orig. */ 1176 c = (*gr_getc)(); 1177 } 1178 } 1179 1180 /* 1181 * Get command character. 1182 */ 1183 public char getcc(void) 1184 { 1185 /* Replace kent (keypad Enter) with a newline. 1186 * However don't do this if kent is mapped to a command via lesskey. */ 1187 return getcc_repl(kent_mapped ? NULL : kent, "\n", getccu, ungetcc); 1188 } 1189 1190 /* 1191 * "Unget" a command character. 1192 * The next getcc() will return this character. 1193 */ 1194 public void ungetcc(char c) 1195 { 1196 struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); 1197 1198 ug->ug_char = c; 1199 ug->ug_next = ungot; 1200 ungot = ug; 1201 } 1202 1203 /* 1204 * "Unget" a command character. 1205 * If any other chars are already ungotten, put this one after those. 1206 */ 1207 static void ungetcc_back1(char c, lbool end_command) 1208 { 1209 struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); 1210 ug->ug_char = c; 1211 ug->ug_end_command = end_command; 1212 ug->ug_next = NULL; 1213 if (ungot == NULL) 1214 ungot = ug; 1215 else 1216 { 1217 struct ungot *pu; 1218 for (pu = ungot; pu->ug_next != NULL; pu = pu->ug_next) 1219 continue; 1220 pu->ug_next = ug; 1221 } 1222 } 1223 1224 public void ungetcc_back(char c) 1225 { 1226 ungetcc_back1(c, FALSE); 1227 } 1228 1229 public void ungetcc_end_command(void) 1230 { 1231 ungetcc_back1('\0', TRUE); 1232 } 1233 1234 /* 1235 * Unget a whole string of command characters. 1236 * The next sequence of getcc()'s will return this string. 1237 */ 1238 public void ungetsc(constant char *s) 1239 { 1240 while (*s != '\0') 1241 ungetcc_back(*s++); 1242 } 1243 1244 /* 1245 * Peek the next command character, without consuming it. 1246 */ 1247 public char peekcc(void) 1248 { 1249 char c = getcc(); 1250 ungetcc(c); 1251 return c; 1252 } 1253 1254 /* 1255 * Search for a pattern, possibly in multiple files. 1256 * If SRCH_FIRST_FILE is set, begin searching at the first file. 1257 * If SRCH_PAST_EOF is set, continue the search thru multiple files. 1258 */ 1259 static void multi_search(constant char *pattern, int n, int silent) 1260 { 1261 int nomore; 1262 IFILE save_ifile; 1263 lbool changed_file; 1264 1265 changed_file = FALSE; 1266 save_ifile = save_curr_ifile(); 1267 1268 if ((search_type & (SRCH_FORW|SRCH_BACK)) == 0) 1269 search_type |= SRCH_FORW; 1270 if (search_type & SRCH_FIRST_FILE) 1271 { 1272 /* 1273 * Start at the first (or last) file 1274 * in the command line list. 1275 */ 1276 if (search_type & SRCH_FORW) 1277 nomore = edit_first(); 1278 else 1279 nomore = edit_last(); 1280 if (nomore) 1281 { 1282 unsave_ifile(save_ifile); 1283 return; 1284 } 1285 changed_file = TRUE; 1286 search_type &= ~SRCH_FIRST_FILE; 1287 } 1288 1289 for (;;) 1290 { 1291 n = search(search_type, pattern, n); 1292 /* 1293 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared 1294 * after being used once. This allows "n" to work after 1295 * using a /@@ search. 1296 */ 1297 search_type &= ~SRCH_NO_MOVE; 1298 last_search_type = search_type; 1299 if (n == 0) 1300 { 1301 /* 1302 * Found it. 1303 */ 1304 unsave_ifile(save_ifile); 1305 return; 1306 } 1307 1308 if (n < 0) 1309 /* 1310 * Some kind of error in the search. 1311 * Error message has been printed by search(). 1312 */ 1313 break; 1314 1315 if ((search_type & SRCH_PAST_EOF) == 0) 1316 /* 1317 * We didn't find a match, but we're 1318 * supposed to search only one file. 1319 */ 1320 break; 1321 /* 1322 * Move on to the next file. 1323 */ 1324 if (search_type & SRCH_FORW) 1325 nomore = edit_next(1); 1326 else 1327 nomore = edit_prev(1); 1328 if (nomore) 1329 break; 1330 changed_file = TRUE; 1331 } 1332 1333 /* 1334 * Didn't find it. 1335 * Print an error message if we haven't already. 1336 */ 1337 if (n > 0 && !silent) 1338 { 1339 PARG parg; 1340 parg.p_string = prev_pattern_text(); 1341 if (parg.p_string == NULL) /* {{ can this happen? }} */ 1342 error(LM(Pattern_not_found), NULL_PARG); 1343 else 1344 error(LM(Pattern_not_found_X), &parg); 1345 } 1346 1347 if (changed_file) 1348 { 1349 /* 1350 * Restore the file we were originally viewing. 1351 */ 1352 reedit_ifile(save_ifile); 1353 } else 1354 { 1355 unsave_ifile(save_ifile); 1356 } 1357 } 1358 1359 /* 1360 * Forward forever, or until a highlighted line appears. 1361 */ 1362 static int forw_loop(int action) 1363 { 1364 POSITION prev_hilite; 1365 1366 if (ch_getflags() & CH_HELPFILE) 1367 return (A_NOACTION); 1368 1369 cmd_exec(); 1370 jump_forw_buffered(); 1371 highest_hilite = prev_hilite = 0; 1372 ignore_eoi = TRUE; 1373 while (!sigs) 1374 { 1375 if (action != A_F_FOREVER && highest_hilite > prev_hilite) 1376 { 1377 lbell(); 1378 if (action == A_F_UNTIL_HILITE) 1379 break; 1380 prev_hilite = highest_hilite; 1381 } 1382 make_display(); 1383 forward(1, FALSE, FALSE, FALSE); 1384 } 1385 highest_hilite = NULL_POSITION; 1386 ignore_eoi = FALSE; 1387 ch_set_eof(); 1388 1389 /* 1390 * This gets us back in "F mode" after processing 1391 * a non-abort signal (e.g. window-change). 1392 */ 1393 if (sigs && !ABORT_SIGS()) 1394 return (action); 1395 1396 return (A_NOACTION); 1397 } 1398 1399 /* 1400 * Ignore subsequent (pasted) input chars. 1401 */ 1402 public void start_ignoring_input(void) 1403 { 1404 ignoring_input = TRUE; 1405 #if HAVE_TIME 1406 ignoring_input_time = get_time(); 1407 #endif 1408 } 1409 1410 /* 1411 * Stop ignoring input chars. 1412 */ 1413 public void stop_ignoring_input(void) 1414 { 1415 ignoring_input = FALSE; 1416 pasting = FALSE; 1417 } 1418 1419 /* 1420 * Are we ignoring input chars? 1421 */ 1422 public lbool is_ignoring_input(int action) 1423 { 1424 if (!ignoring_input) 1425 return FALSE; 1426 if (action == A_END_PASTE) 1427 stop_ignoring_input(); 1428 #if HAVE_TIME 1429 if (get_time() >= ignoring_input_time + MAX_PASTE_IGNORE_SEC) 1430 stop_ignoring_input(); 1431 #endif 1432 /* 1433 * Don't ignore prefix chars so we can parse a full command 1434 * (which might be A_END_PASTE). 1435 */ 1436 return (action != A_PREFIX); 1437 } 1438 1439 #if PIPEC 1440 /* 1441 * Read a mark char or line number from the user for use by the pipe command. 1442 * If mark_char != NULL, accept *mark_char as well as the normal mark chars. 1443 * Also return the entered mark char in *mark_char. 1444 */ 1445 static POSITION get_pipe_pos(constant char *mark_msg, constant char *line_msg, mutable char *mark_char) 1446 { 1447 lbool get_line = FALSE; 1448 char spec_char = '\0'; 1449 char c; 1450 1451 start_mca(A_PIPE, mark_msg, NULL, 0); 1452 if (mark_char != NULL) 1453 { 1454 spec_char = *mark_char; 1455 *mark_char = '\0'; 1456 } 1457 for (;;) 1458 { 1459 c = getcc(); 1460 if (spec_char != '\0' && c == spec_char) 1461 { 1462 *mark_char = c; 1463 return NULL_POSITION; 1464 } 1465 if (c == CONTROL('N')) /* toggle between reading mark and line number */ 1466 { 1467 get_line = !get_line; 1468 cmd_reset(); 1469 start_mca(A_PIPE, get_line ? line_msg : mark_msg, NULL, 0); 1470 } else if (get_line) /* reading line number */ 1471 { 1472 if ((c >= '0' && c <= '9') || is_erase_char(c)) 1473 { 1474 if (cmd_char(c) == CC_QUIT) 1475 return NULL_POSITION; 1476 } else if (is_newline_char(c)) 1477 { 1478 POSITION pos; 1479 LINENUM lnum = cmd_int(NULL); 1480 cmd_reset(); 1481 if (lnum == 0 || (pos = find_pos(lnum)) == NULL_POSITION) 1482 { 1483 error(LM(Invalid_line_number), NULL_PARG); 1484 return NULL_POSITION; 1485 } 1486 return pos; 1487 } else 1488 lbell(); 1489 } else /* reading mark char */ 1490 { 1491 if (is_erase_char(c)) 1492 return NULL_POSITION; 1493 if (is_newline_char(c)) 1494 c = '.'; 1495 if (badmark(c)) 1496 return NULL_POSITION; 1497 if (mark_char != NULL) 1498 *mark_char = c; 1499 return markpos(c); 1500 } 1501 } 1502 } 1503 #endif /* PIPEC */ 1504 1505 /* 1506 * Main command processor. 1507 * Accept and execute commands until a quit command. 1508 */ 1509 public void commands(void) 1510 { 1511 char c; 1512 int action; 1513 constant char *cbuf; 1514 constant char *msg; 1515 int newaction; 1516 int save_jump_sline; 1517 int save_search_type; 1518 constant char *extra; 1519 PARG parg; 1520 IFILE old_ifile; 1521 IFILE new_ifile; 1522 #if TAGS 1523 constant char *tagfile; 1524 #endif 1525 1526 search_type = SRCH_FORW; 1527 newaction = A_NOACTION; 1528 1529 for (;;) 1530 { 1531 clear_mca(); 1532 cmd_accept(); 1533 number = 0; 1534 curropt = NULL; 1535 1536 /* 1537 * See if any signals need processing. 1538 */ 1539 if (sigs) 1540 { 1541 psignals(); 1542 if (quitting) 1543 quit(QUIT_SAVED_STATUS); 1544 } 1545 1546 /* 1547 * See if window size changed, for systems that don't 1548 * generate SIGWINCH. 1549 */ 1550 check_winch(); 1551 1552 /* 1553 * Display prompt and accept a character. 1554 */ 1555 cmd_reset(); 1556 prompt(); 1557 if (sigs) 1558 continue; 1559 if (newaction == A_NOACTION) 1560 c = getcc(); 1561 1562 again: 1563 if (c == READ_AGAIN) 1564 continue; 1565 if (sigs) 1566 continue; 1567 1568 if (newaction != A_NOACTION) 1569 { 1570 action = newaction; 1571 newaction = A_NOACTION; 1572 } else 1573 { 1574 /* 1575 * If we are in a multicharacter command, call mca_char. 1576 * Otherwise we call fcmd_decode to determine the 1577 * action to be performed. 1578 */ 1579 if (mca) 1580 switch (mca_char(c)) 1581 { 1582 case MCA_MORE: 1583 /* 1584 * Need another character. 1585 */ 1586 c = getcc(); 1587 goto again; 1588 case MCA_DONE: 1589 /* 1590 * Command has been handled by mca_char. 1591 * Start clean with a prompt. 1592 */ 1593 continue; 1594 case NO_MCA: 1595 /* 1596 * Not a multi-char command 1597 * (at least, not anymore). 1598 */ 1599 break; 1600 } 1601 1602 /* 1603 * Decode the command character and decide what to do. 1604 */ 1605 extra = NULL; 1606 if (mca) 1607 { 1608 /* 1609 * We're in a multichar command. 1610 * Add the character to the command buffer 1611 * and display it on the screen. 1612 * If the user backspaces past the start 1613 * of the line, abort the command. 1614 */ 1615 if (cmd_char(c) == CC_QUIT || cmdbuf_empty()) 1616 continue; 1617 cbuf = get_cmdbuf(); 1618 if (cbuf == NULL) 1619 { 1620 c = getcc(); 1621 goto again; 1622 } 1623 action = fcmd_decode(cbuf, &extra); 1624 } else 1625 { 1626 /* 1627 * Don't use cmd_char if we're starting fresh 1628 * at the beginning of a command, because we 1629 * don't want to echo the command until we know 1630 * it is a multichar command. We also don't 1631 * want erase_char/kill_char to be treated 1632 * as line editing characters. 1633 */ 1634 char tbuf[2]; 1635 tbuf[0] = c; 1636 tbuf[1] = '\0'; 1637 action = fcmd_decode(tbuf, &extra); 1638 } 1639 /* 1640 * If an "extra" string was returned, 1641 * process it as a string of command characters. 1642 */ 1643 if (extra != NULL) 1644 ungetsc(extra); 1645 } 1646 /* 1647 * Clear the cmdbuf string. 1648 * (But not if we're in the prefix of a command, 1649 * because the partial command string is kept there.) 1650 */ 1651 if (action != A_PREFIX) 1652 cmd_reset(); 1653 1654 if (is_ignoring_input(action)) 1655 continue; 1656 1657 switch (action) 1658 { 1659 case A_START_PASTE: 1660 if (no_paste) 1661 start_ignoring_input(); 1662 break; 1663 1664 case A_DIGIT: 1665 /* 1666 * First digit of a number. 1667 */ 1668 start_mca(A_DIGIT, ":", NULL, CF_QUIT_ON_ERASE); 1669 goto again; 1670 1671 case A_F_WINDOW: 1672 /* 1673 * Forward one window (and set the window size). 1674 */ 1675 if (number > 0) 1676 swindow = (int) number; 1677 /* FALLTHRU */ 1678 case A_F_SCREEN: 1679 /* 1680 * Forward one screen. 1681 */ 1682 if (number <= 0) 1683 number = get_swindow(); 1684 cmd_exec(); 1685 if (show_attn) 1686 set_attnpos(bottompos); 1687 forward((int) number, FALSE, TRUE, FALSE); 1688 break; 1689 1690 case A_B_WINDOW: 1691 /* 1692 * Backward one window (and set the window size). 1693 */ 1694 if (number > 0) 1695 swindow = (int) number; 1696 /* FALLTHRU */ 1697 case A_B_SCREEN: 1698 /* 1699 * Backward one screen. 1700 */ 1701 if (number <= 0) 1702 number = get_swindow(); 1703 cmd_exec(); 1704 if (show_attn && toppos != NULL_POSITION && toppos > ch_zero()) 1705 set_attnpos(toppos-1); 1706 backward((int) number, FALSE, TRUE, FALSE); 1707 break; 1708 1709 case A_F_LINE: 1710 case A_F_NEWLINE: 1711 /* 1712 * Forward N (default 1) line. 1713 */ 1714 if (number <= 0) 1715 number = 1; 1716 cmd_exec(); 1717 if (show_attn == OPT_ONPLUS && number > 1) 1718 set_attnpos(bottompos); 1719 forward((int) number, FALSE, FALSE, action == A_F_NEWLINE && !chopline); 1720 break; 1721 1722 case A_B_LINE: 1723 case A_B_NEWLINE: 1724 /* 1725 * Backward N (default 1) line. 1726 */ 1727 if (number <= 0) 1728 number = 1; 1729 cmd_exec(); 1730 if (show_attn == OPT_ONPLUS && number > 1 && toppos != NULL_POSITION && toppos > ch_zero()) 1731 set_attnpos(toppos-1); 1732 backward((int) number, FALSE, FALSE, action == A_B_NEWLINE && !chopline); 1733 break; 1734 1735 case A_F_MOUSE: 1736 /* 1737 * Forward wheel_lines lines. 1738 */ 1739 cmd_exec(); 1740 forward(wheel_lines, FALSE, FALSE, FALSE); 1741 break; 1742 1743 case A_B_MOUSE: 1744 /* 1745 * Backward wheel_lines lines. 1746 */ 1747 cmd_exec(); 1748 backward(wheel_lines, FALSE, FALSE, FALSE); 1749 break; 1750 1751 case A_L_MOUSE: 1752 /* 1753 * Left wheel_lines columns. 1754 */ 1755 cmd_exec(); 1756 pos_rehead(FALSE); 1757 if (wheel_lines > hshift) 1758 hshift = 0; 1759 else 1760 hshift -= wheel_lines; 1761 screen_trashed(); 1762 break; 1763 1764 case A_R_MOUSE: 1765 /* 1766 * Right wheel_lines columns. 1767 */ 1768 cmd_exec(); 1769 pos_rehead(FALSE); 1770 hshift += wheel_lines; 1771 screen_trashed(); 1772 break; 1773 1774 case A_FF_LINE: 1775 /* 1776 * Force forward N (default 1) line. 1777 */ 1778 if (number <= 0) 1779 number = 1; 1780 cmd_exec(); 1781 if (show_attn == OPT_ONPLUS && number > 1) 1782 set_attnpos(bottompos); 1783 forward((int) number, TRUE, FALSE, FALSE); 1784 break; 1785 1786 case A_BF_LINE: 1787 /* 1788 * Force backward N (default 1) line. 1789 */ 1790 if (number <= 0) 1791 number = 1; 1792 cmd_exec(); 1793 if (show_attn == OPT_ONPLUS && number > 1 && toppos != NULL_POSITION && toppos > ch_zero()) 1794 set_attnpos(toppos-1); 1795 backward((int) number, TRUE, FALSE, FALSE); 1796 break; 1797 1798 case A_FF_SCREEN: 1799 /* 1800 * Force forward one screen. 1801 */ 1802 if (number <= 0) 1803 number = get_swindow(); 1804 cmd_exec(); 1805 if (show_attn == OPT_ONPLUS) 1806 set_attnpos(bottompos); 1807 forward((int) number, TRUE, FALSE, FALSE); 1808 break; 1809 1810 case A_BF_SCREEN: 1811 /* 1812 * Force backward one screen. 1813 */ 1814 if (number <= 0) 1815 number = get_swindow(); 1816 cmd_exec(); 1817 if (show_attn == OPT_ONPLUS && toppos != NULL_POSITION && toppos > ch_zero()) 1818 set_attnpos(toppos-1); 1819 backward((int) number, TRUE, FALSE, FALSE); 1820 break; 1821 1822 case A_F_FOREVER: 1823 case A_F_FOREVER_BELL: 1824 case A_F_UNTIL_HILITE: 1825 /* 1826 * Forward forever, ignoring EOF. 1827 */ 1828 if (get_altfilename(curr_ifile) != NULL) 1829 error(LM(command_may_not_work_correctly_with_LESSOPEN), NULL_PARG); 1830 if (show_attn) 1831 set_attnpos(bottompos); 1832 newaction = forw_loop(action); 1833 break; 1834 1835 case A_F_SCROLL: 1836 /* 1837 * Forward N lines 1838 * (default same as last 'd' or 'u' command). 1839 */ 1840 if (number > 0) 1841 wscroll = (int) number; 1842 cmd_exec(); 1843 if (show_attn == OPT_ONPLUS) 1844 set_attnpos(bottompos); 1845 forward(wscroll, FALSE, FALSE, FALSE); 1846 break; 1847 1848 case A_B_SCROLL: 1849 /* 1850 * Forward N lines 1851 * (default same as last 'd' or 'u' command). 1852 */ 1853 if (number > 0) 1854 wscroll = (int) number; 1855 cmd_exec(); 1856 if (show_attn == OPT_ONPLUS && toppos != NULL_POSITION && toppos > ch_zero()) 1857 set_attnpos(toppos-1); 1858 backward(wscroll, FALSE, FALSE, FALSE); 1859 break; 1860 1861 case A_FREPAINT: 1862 /* 1863 * Flush buffers, then repaint screen. 1864 * Don't flush the buffers on a pipe! 1865 */ 1866 clear_buffers(); 1867 /* FALLTHRU */ 1868 case A_REPAINT: 1869 /* 1870 * Repaint screen. 1871 */ 1872 cmd_exec(); 1873 repaint(); 1874 break; 1875 1876 case A_GOLINE: 1877 /* 1878 * Go to line N, default beginning of file. 1879 * If N <= 0, ignore jump_sline in order to avoid 1880 * empty lines before the beginning of the file. 1881 */ 1882 save_jump_sline = jump_sline; 1883 if (number <= 0) 1884 { 1885 number = 1; 1886 jump_sline = 0; 1887 } 1888 cmd_exec(); 1889 jump_back(number); 1890 jump_sline = save_jump_sline; 1891 break; 1892 1893 case A_PERCENT: 1894 /* 1895 * Go to a specified percentage into the file. 1896 */ 1897 if (number < 0) 1898 { 1899 number = 0; 1900 fraction = 0; 1901 } 1902 if (number > 100 || (number == 100 && fraction != 0)) 1903 { 1904 number = 100; 1905 fraction = 0; 1906 } 1907 cmd_exec(); 1908 jump_percent((int) number, fraction); 1909 break; 1910 1911 case A_GOEND: 1912 /* 1913 * Go to line N, default end of file. 1914 */ 1915 cmd_exec(); 1916 if (number <= 0) 1917 jump_forw(); 1918 else 1919 jump_back(number); 1920 break; 1921 1922 case A_GOEND_BUF: 1923 /* 1924 * Go to line N, default last buffered byte. 1925 */ 1926 cmd_exec(); 1927 if (number <= 0) 1928 jump_forw_buffered(); 1929 else 1930 jump_back(number); 1931 break; 1932 1933 case A_GOPOS: 1934 /* 1935 * Go to a specified byte position in the file. 1936 */ 1937 cmd_exec(); 1938 if (number < 0) 1939 number = 0; 1940 jump_line_loc((POSITION) number, jump_sline); 1941 break; 1942 1943 case A_STAT: 1944 /* 1945 * Print file name, etc. 1946 */ 1947 if (ch_getflags() & CH_HELPFILE) 1948 break; 1949 cmd_exec(); 1950 parg.p_string = eq_message(); 1951 error("%s", &parg); 1952 break; 1953 1954 case A_VERSION: 1955 /* 1956 * Print version number. 1957 */ 1958 cmd_exec(); 1959 dispversion(); 1960 break; 1961 1962 case A_QUIT: 1963 /* 1964 * Exit. 1965 */ 1966 cmd_exec(); 1967 if (curr_ifile != NULL_IFILE && 1968 ch_getflags() & CH_HELPFILE) 1969 { 1970 /* 1971 * Quit while viewing the help file 1972 * just means return to viewing the 1973 * previous file. 1974 */ 1975 hshift = save_hshift; 1976 bs_mode = save_bs_mode; 1977 proc_backspace = save_proc_backspace; 1978 if (edit_prev(1) == 0) 1979 break; 1980 } 1981 if (extra != NULL) 1982 quit(*extra); 1983 quit(QUIT_OK); 1984 break; 1985 1986 /* 1987 * Define abbreviation for a commonly used sequence below. 1988 */ 1989 #define DO_SEARCH() \ 1990 if (number <= 0) number = 1; \ 1991 mca_search(); \ 1992 cmd_exec(); \ 1993 multi_search(NULL, (int) number, 0); 1994 1995 case A_F_SEARCH: 1996 /* 1997 * Search forward for a pattern. 1998 * Get the first char of the pattern. 1999 */ 2000 search_type = SRCH_FORW | def_search_type; 2001 if (number <= 0) 2002 number = 1; 2003 literal_char = FALSE; 2004 mca_search(); 2005 c = getcc(); 2006 goto again; 2007 2008 case A_B_SEARCH: 2009 /* 2010 * Search backward for a pattern. 2011 * Get the first char of the pattern. 2012 */ 2013 search_type = SRCH_BACK | def_search_type; 2014 if (number <= 0) 2015 number = 1; 2016 literal_char = FALSE; 2017 mca_search(); 2018 c = getcc(); 2019 goto again; 2020 2021 case A_OSC8_F_SEARCH: 2022 #if OSC8_LINK 2023 cmd_exec(); 2024 if (number <= 0) 2025 number = 1; 2026 osc8_search(SRCH_FORW, NULL, number); 2027 #else 2028 error(LM(Command_not_available), NULL_PARG); 2029 #endif 2030 break; 2031 2032 case A_OSC8_B_SEARCH: 2033 #if OSC8_LINK 2034 cmd_exec(); 2035 if (number <= 0) 2036 number = 1; 2037 osc8_search(SRCH_BACK, NULL, number); 2038 #else 2039 error(LM(Command_not_available), NULL_PARG); 2040 #endif 2041 break; 2042 2043 case A_OSC8_OPEN: 2044 #if OSC8_LINK 2045 if (secure_allow(SF_OSC8_OPEN)) 2046 { 2047 cmd_exec(); 2048 osc8_open(); 2049 break; 2050 } 2051 #endif 2052 error(LM(Command_not_available), NULL_PARG); 2053 break; 2054 2055 case A_OSC8_JUMP: 2056 #if OSC8_LINK 2057 cmd_exec(); 2058 osc8_jump(); 2059 #else 2060 error(LM(Command_not_available), NULL_PARG); 2061 #endif 2062 break; 2063 2064 case A_FILTER: 2065 #if HILITE_SEARCH 2066 search_type = SRCH_FORW | SRCH_FILTER; 2067 literal_char = FALSE; 2068 mca_search(); 2069 c = getcc(); 2070 goto again; 2071 #else 2072 error(LM(Command_not_available), NULL_PARG); 2073 break; 2074 #endif 2075 2076 case A_AGAIN_SEARCH: 2077 /* 2078 * Repeat previous search. 2079 */ 2080 search_type = last_search_type; 2081 DO_SEARCH(); 2082 break; 2083 2084 case A_T_AGAIN_SEARCH: 2085 /* 2086 * Repeat previous search, multiple files. 2087 */ 2088 search_type = last_search_type | SRCH_PAST_EOF; 2089 DO_SEARCH(); 2090 break; 2091 2092 case A_REVERSE_SEARCH: 2093 /* 2094 * Repeat previous search, in reverse direction. 2095 */ 2096 save_search_type = search_type = last_search_type; 2097 search_type = SRCH_REVERSE(search_type); 2098 DO_SEARCH(); 2099 last_search_type = save_search_type; 2100 break; 2101 2102 case A_T_REVERSE_SEARCH: 2103 /* 2104 * Repeat previous search, 2105 * multiple files in reverse direction. 2106 */ 2107 save_search_type = search_type = last_search_type; 2108 search_type = SRCH_REVERSE(search_type) | SRCH_PAST_EOF; 2109 DO_SEARCH(); 2110 last_search_type = save_search_type; 2111 break; 2112 2113 case A_UNDO_SEARCH: 2114 case A_CLR_SEARCH: 2115 /* 2116 * Clear search string highlighting. 2117 */ 2118 cmd_exec(); 2119 undo_search(action == A_CLR_SEARCH); 2120 break; 2121 2122 case A_HELP: 2123 /* 2124 * Help. 2125 */ 2126 if (ch_getflags() & CH_HELPFILE) 2127 break; 2128 cmd_exec(); 2129 save_hshift = hshift; 2130 hshift = 0; 2131 save_bs_mode = bs_mode; 2132 bs_mode = BS_SPECIAL; 2133 save_proc_backspace = proc_backspace; 2134 proc_backspace = OPT_OFF; 2135 (void) edit(FAKE_HELPFILE); 2136 break; 2137 2138 case A_EXAMINE: 2139 /* 2140 * Edit a new file. Get the filename. 2141 */ 2142 #if EXAMINE 2143 if (secure_allow(SF_EXAMINE)) 2144 { 2145 start_mca(A_EXAMINE, LM(Examine), ml_examine, 0); 2146 c = getcc(); 2147 goto again; 2148 } 2149 #endif 2150 error(LM(Command_not_available), NULL_PARG); 2151 break; 2152 2153 case A_VISUAL: 2154 /* 2155 * Invoke an editor on the input file. 2156 */ 2157 #if EDITOR 2158 if (secure_allow(SF_EDIT)) 2159 { 2160 if (ch_getflags() & CH_HELPFILE) 2161 break; 2162 if (strcmp(get_filename(curr_ifile), "-") == 0) 2163 { 2164 error(LM(Cannot_edit_standard_input), NULL_PARG); 2165 break; 2166 } 2167 if (!no_edit_warn && get_altfilename(curr_ifile) != NULL) 2168 { 2169 error(LM(This_file_was_viewed_via_LESSOPEN), NULL_PARG); 2170 } 2171 start_mca(A_SHELL, "!", ml_shell, 0); 2172 /* 2173 * Expand the editor prototype string 2174 * and pass it to the system to execute. 2175 * (Make sure the screen is displayed so the 2176 * expansion of "+%lm" works.) 2177 */ 2178 make_display(); 2179 cmd_exec(); 2180 lsystem(pr_expand(editproto), NULL); 2181 break; 2182 } 2183 #endif 2184 error(LM(Command_not_available), NULL_PARG); 2185 break; 2186 2187 case A_NEXT_FILE: 2188 /* 2189 * Examine next file. 2190 */ 2191 #if TAGS 2192 if (ntags()) 2193 { 2194 error(LM(No_next_file), NULL_PARG); 2195 break; 2196 } 2197 #endif 2198 if (number <= 0) 2199 number = 1; 2200 cmd_exec(); 2201 if (edit_next((int) number)) 2202 { 2203 if (get_quit_at_eof() && eof_displayed(FALSE) && 2204 !(ch_getflags() & CH_HELPFILE)) 2205 quit(QUIT_OK); 2206 parg.p_string = (number > 1) ? "(N-th) " : ""; 2207 error(LM(No_X_next_file), &parg); 2208 } 2209 break; 2210 2211 case A_PREV_FILE: 2212 /* 2213 * Examine previous file. 2214 */ 2215 #if TAGS 2216 if (ntags()) 2217 { 2218 error(LM(No_previous_file), NULL_PARG); 2219 break; 2220 } 2221 #endif 2222 if (number <= 0) 2223 number = 1; 2224 cmd_exec(); 2225 if (edit_prev((int) number)) 2226 { 2227 parg.p_string = (number > 1) ? "(N-th) " : ""; 2228 error(LM(No_X_previous_file), &parg); 2229 } 2230 break; 2231 2232 case A_NEXT_TAG: 2233 /* 2234 * Jump to the next tag in the current tag list. 2235 */ 2236 #if TAGS 2237 if (number <= 0) 2238 number = 1; 2239 tagfile = nexttag((int) number); 2240 if (tagfile == NULL) 2241 { 2242 error(LM(No_next_tag), NULL_PARG); 2243 break; 2244 } 2245 cmd_exec(); 2246 if (edit(tagfile) == 0) 2247 { 2248 POSITION pos = tagsearch(); 2249 if (pos != NULL_POSITION) 2250 jump_loc(pos, jump_sline); 2251 } 2252 #else 2253 error(LM(Command_not_available), NULL_PARG); 2254 #endif 2255 break; 2256 2257 case A_PREV_TAG: 2258 /* 2259 * Jump to the previous tag in the current tag list. 2260 */ 2261 #if TAGS 2262 if (number <= 0) 2263 number = 1; 2264 tagfile = prevtag((int) number); 2265 if (tagfile == NULL) 2266 { 2267 error(LM(No_previous_tag), NULL_PARG); 2268 break; 2269 } 2270 cmd_exec(); 2271 if (edit(tagfile) == 0) 2272 { 2273 POSITION pos = tagsearch(); 2274 if (pos != NULL_POSITION) 2275 jump_loc(pos, jump_sline); 2276 } 2277 #else 2278 error(LM(Command_not_available), NULL_PARG); 2279 #endif 2280 break; 2281 2282 case A_INDEX_FILE: 2283 /* 2284 * Examine a particular file. 2285 */ 2286 if (number <= 0) 2287 number = 1; 2288 cmd_exec(); 2289 if (edit_index((int) number)) 2290 error(LM(No_such_file), NULL_PARG); 2291 break; 2292 2293 case A_REMOVE_FILE: 2294 /* 2295 * Remove a file from the input file list. 2296 */ 2297 if (ch_getflags() & CH_HELPFILE) 2298 break; 2299 old_ifile = curr_ifile; 2300 new_ifile = getoff_ifile(curr_ifile); 2301 cmd_exec(); 2302 if (new_ifile == NULL_IFILE) 2303 { 2304 lbell(); 2305 break; 2306 } 2307 if (edit_ifile(new_ifile) != 0) 2308 { 2309 reedit_ifile(old_ifile); 2310 break; 2311 } 2312 del_ifile(old_ifile); 2313 break; 2314 2315 case A_OPT_TOGGLE: 2316 /* 2317 * Change the setting of an option. 2318 */ 2319 optflag = OPT_TOGGLE; 2320 optgetname = FALSE; 2321 mca_opt_toggle(); 2322 c = getcc(); 2323 msg = opt_toggle_disallowed(c); 2324 if (msg != NULL) 2325 { 2326 error(msg, NULL_PARG); 2327 break; 2328 } 2329 goto again; 2330 2331 case A_DISP_OPTION: 2332 /* 2333 * Report the setting of an option. 2334 */ 2335 optflag = OPT_NO_TOGGLE; 2336 optgetname = FALSE; 2337 mca_opt_toggle(); 2338 c = getcc(); 2339 goto again; 2340 2341 case A_FIRSTCMD: 2342 /* 2343 * Set an initial command for new files. 2344 */ 2345 start_mca(A_FIRSTCMD, "+", NULL, 0); 2346 c = getcc(); 2347 goto again; 2348 2349 case A_SHELL: 2350 case A_PSHELL: 2351 /* 2352 * Shell escape. 2353 */ 2354 #if SHELL_ESCAPE 2355 if (secure_allow(SF_SHELL)) 2356 { 2357 start_mca(action, (action == A_SHELL) ? "!" : "#", ml_shell, 0); 2358 c = getcc(); 2359 goto again; 2360 } 2361 #endif 2362 error(LM(Command_not_available), NULL_PARG); 2363 break; 2364 2365 case A_SETMARK: 2366 case A_SETMARKBOT: 2367 /* 2368 * Set a mark. 2369 */ 2370 if (ch_getflags() & CH_HELPFILE) 2371 { 2372 if (ungot != NULL) 2373 { 2374 /* 2375 * Probably from a lesskey file, in which case there 2376 * is probably an ungotten letter from the "extra" string. 2377 * Eat it so it is not interpreted as a command. 2378 */ 2379 (void) getcc(); 2380 } 2381 break; 2382 } 2383 start_mca(A_SETMARK, LM(set_mark), NULL, 0); 2384 c = getcc(); 2385 make_display(); 2386 cmd_exec(); 2387 if (is_erase_char(c) || is_newline_char(c)) 2388 break; 2389 setmark(c, action == A_SETMARKBOT ? BOTTOM : TOP, number); 2390 repaint(); 2391 break; 2392 2393 case A_CLRMARK: 2394 /* 2395 * Clear a mark. 2396 */ 2397 start_mca(A_CLRMARK, LM(clear_mark), NULL, 0); 2398 c = getcc(); 2399 cmd_exec(); 2400 if (is_erase_char(c) || is_newline_char(c)) 2401 break; 2402 clrmark(c); 2403 repaint(); 2404 break; 2405 2406 case A_GOMARK: 2407 /* 2408 * Jump to a marked position. 2409 */ 2410 start_mca(A_GOMARK, LM(goto_mark), NULL, 0); 2411 c = getcc(); 2412 if (is_erase_char(c) || is_newline_char(c)) 2413 break; 2414 cmd_exec(); 2415 gomark(c, number); 2416 break; 2417 2418 case A_PIPE: 2419 /* 2420 * Write part of the input to a pipe to a shell command. 2421 */ 2422 #if PIPEC 2423 if (secure_allow(SF_PIPE)) 2424 { 2425 char mark_char = '|'; 2426 pipe_pos1 = get_pipe_pos(LM(pipe_mark), LM(pipe_line_number), &mark_char); 2427 if (mark_char == '|') /* double pipe: read two marks */ 2428 { 2429 if ((pipe_pos1 = get_pipe_pos(LM(pipe_first_mark), LM(pipe_first_line_number), NULL)) == NULL_POSITION || 2430 (pipe_pos2 = get_pipe_pos(LM(pipe_second_mark), LM(pipe_second_line_number), NULL)) == NULL_POSITION) 2431 { 2432 getcc_clear(); 2433 break; 2434 } 2435 } else 2436 { 2437 if (pipe_pos1 == NULL_POSITION) 2438 { 2439 getcc_clear(); 2440 break; 2441 } 2442 if (mark_char == '.') /* special case: pipe current screen */ 2443 { 2444 pipe_pos1 = markpos(':'); 2445 pipe_pos2 = markpos(';'); 2446 } else 2447 { 2448 pipe_pos2 = NULL_POSITION; 2449 } 2450 } 2451 start_mca(A_PIPE, "!", ml_shell, 0); 2452 c = getcc(); 2453 goto again; 2454 } 2455 #endif 2456 error(LM(Command_not_available), NULL_PARG); 2457 break; 2458 2459 case A_B_BRACKET: 2460 case A_F_BRACKET: 2461 start_mca(action, LM(Brackets), NULL, 0); 2462 c = getcc(); 2463 goto again; 2464 2465 case A_LSHIFT: 2466 /* 2467 * Shift view left. 2468 */ 2469 if (number > 0) 2470 shift_count = (int) number; 2471 else 2472 number = (shift_count > 0) ? shift_count : sc_width / 2; 2473 if (number > hshift) 2474 number = hshift; 2475 pos_rehead(FALSE); 2476 hshift -= (int) number; 2477 screen_trashed(); 2478 cmd_exec(); 2479 break; 2480 2481 case A_RSHIFT: 2482 case A_RSHIFT_LIMIT: 2483 /* 2484 * Shift view right. 2485 */ 2486 if (number > 0) 2487 shift_count = (int) number; 2488 else 2489 number = (shift_count > 0) ? shift_count : sc_width / 2; 2490 pos_rehead(FALSE); 2491 if (action == A_RSHIFT_LIMIT) 2492 { 2493 int ll = longest_line_width(); 2494 if (hshift + sc_width + (int) number > ll) 2495 number = ll - hshift - sc_width; 2496 } 2497 hshift += (int) number; 2498 screen_trashed(); 2499 cmd_exec(); 2500 break; 2501 2502 case A_LLSHIFT: 2503 /* 2504 * Shift view left to margin. 2505 */ 2506 pos_rehead(FALSE); 2507 hshift = 0; 2508 screen_trashed(); 2509 cmd_exec(); 2510 break; 2511 2512 case A_RRSHIFT: 2513 /* 2514 * Shift view right to view rightmost char on screen. 2515 */ 2516 pos_rehead(FALSE); 2517 hshift = rrshift(); 2518 screen_trashed(); 2519 cmd_exec(); 2520 break; 2521 2522 case A_PREFIX: 2523 /* 2524 * The command is incomplete (more chars are needed). 2525 * Display the current char, so the user knows 2526 * what's going on, and get another character. 2527 */ 2528 if (mca != A_PREFIX) 2529 { 2530 cmd_reset(); 2531 start_mca(A_PREFIX, " ", NULL, CF_QUIT_ON_ERASE); 2532 (void) cmd_char(c); 2533 } 2534 c = getcc(); 2535 goto again; 2536 2537 case A_NOACTION: 2538 break; 2539 2540 default: 2541 lbell(); 2542 break; 2543 } 2544 } 2545 } 2546