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