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