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 801 #if MSDOS_COMPILER==WIN32C 802 /* 803 * In Win32, display the file name in the window title. 804 */ 805 if (!(ch_getflags() & CH_HELPFILE)) 806 { 807 WCHAR w[MAX_PATH+16]; 808 p = pr_expand("Less?f - %f.", 0); 809 MultiByteToWideChar(CP_ACP, 0, p, -1, w, sizeof(w)/sizeof(*w)); 810 SetConsoleTitleW(w); 811 } 812 #endif 813 814 /* 815 * Select the proper prompt and display it. 816 */ 817 /* 818 * If the previous action was a forward movement, 819 * don't clear the bottom line of the display; 820 * just print the prompt since the forward movement guarantees 821 * that we're in the right position to display the prompt. 822 * Clearing the line could cause a problem: for example, if the last 823 * line displayed ended at the right screen edge without a newline, 824 * then clearing would clear the last displayed line rather than 825 * the prompt line. 826 */ 827 if (!forw_prompt) 828 clear_bot(); 829 clear_cmd(); 830 forw_prompt = 0; 831 p = pr_string(); 832 #if HILITE_SEARCH 833 if (is_filtering()) 834 putstr("& "); 835 #endif 836 if (p == NULL || *p == '\0') 837 { 838 at_enter(AT_NORMAL|AT_COLOR_PROMPT); 839 putchr(':'); 840 at_exit(); 841 } else 842 { 843 #if MSDOS_COMPILER==WIN32C 844 WCHAR w[MAX_PATH*2]; 845 char a[MAX_PATH*2]; 846 MultiByteToWideChar(CP_ACP, 0, p, -1, w, sizeof(w)/sizeof(*w)); 847 WideCharToMultiByte(utf_mode ? CP_UTF8 : GetConsoleOutputCP(), 848 0, w, -1, a, sizeof(a), NULL, NULL); 849 p = a; 850 #endif 851 at_enter(AT_STANDOUT|AT_COLOR_PROMPT); 852 putstr(p); 853 at_exit(); 854 } 855 clear_eol(); 856 } 857 858 /* 859 * Display the less version message. 860 */ 861 public void 862 dispversion(VOID_PARAM) 863 { 864 PARG parg; 865 866 parg.p_string = version; 867 error("less %s", &parg); 868 } 869 870 /* 871 * Return a character to complete a partial command, if possible. 872 */ 873 static LWCHAR 874 getcc_end_command(VOID_PARAM) 875 { 876 switch (mca) 877 { 878 case A_DIGIT: 879 /* We have a number but no command. Treat as #g. */ 880 return ('g'); 881 case A_F_SEARCH: 882 case A_B_SEARCH: 883 /* We have "/string" but no newline. Add the \n. */ 884 return ('\n'); 885 default: 886 /* Some other incomplete command. Let user complete it. */ 887 return ((ungot == NULL) ? getchr() : 0); 888 } 889 } 890 891 /* 892 * Get command character. 893 * The character normally comes from the keyboard, 894 * but may come from ungotten characters 895 * (characters previously given to ungetcc or ungetsc). 896 */ 897 static LWCHAR 898 getccu(VOID_PARAM) 899 { 900 LWCHAR c = 0; 901 while (c == 0) 902 { 903 if (ungot == NULL) 904 { 905 /* Normal case: no ungotten chars. 906 * Get char from the user. */ 907 c = getchr(); 908 } else 909 { 910 /* Ungotten chars available: 911 * Take the top of stack (most recent). */ 912 struct ungot *ug = ungot; 913 c = ug->ug_char; 914 ungot = ug->ug_next; 915 free(ug); 916 917 if (c == CHAR_END_COMMAND) 918 c = getcc_end_command(); 919 } 920 } 921 return (c); 922 } 923 924 /* 925 * Get a command character, but if we receive the orig sequence, 926 * convert it to the repl sequence. 927 */ 928 static LWCHAR 929 getcc_repl(orig, repl, gr_getc, gr_ungetc) 930 char const* orig; 931 char const* repl; 932 LWCHAR (*gr_getc)(VOID_PARAM); 933 void (*gr_ungetc)(LWCHAR); 934 { 935 LWCHAR c; 936 LWCHAR keys[16]; 937 int ki = 0; 938 939 c = (*gr_getc)(); 940 if (orig == NULL || orig[0] == '\0') 941 return c; 942 for (;;) 943 { 944 keys[ki] = c; 945 if (c != orig[ki] || ki >= sizeof(keys)-1) 946 { 947 /* This is not orig we have been receiving. 948 * If we have stashed chars in keys[], 949 * unget them and return the first one. */ 950 while (ki > 0) 951 (*gr_ungetc)(keys[ki--]); 952 return keys[0]; 953 } 954 if (orig[++ki] == '\0') 955 { 956 /* We've received the full orig sequence. 957 * Return the repl sequence. */ 958 ki = strlen(repl)-1; 959 while (ki > 0) 960 (*gr_ungetc)(repl[ki--]); 961 return repl[0]; 962 } 963 /* We've received a partial orig sequence (ki chars of it). 964 * Get next char and see if it continues to match orig. */ 965 c = (*gr_getc)(); 966 } 967 } 968 969 /* 970 * Get command character. 971 */ 972 public int 973 getcc(VOID_PARAM) 974 { 975 /* Replace kent (keypad Enter) with a newline. */ 976 return getcc_repl(kent, "\n", getccu, ungetcc); 977 } 978 979 /* 980 * "Unget" a command character. 981 * The next getcc() will return this character. 982 */ 983 public void 984 ungetcc(c) 985 LWCHAR c; 986 { 987 struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); 988 989 ug->ug_char = c; 990 ug->ug_next = ungot; 991 ungot = ug; 992 } 993 994 /* 995 * "Unget" a command character. 996 * If any other chars are already ungotten, put this one after those. 997 */ 998 public void 999 ungetcc_back(c) 1000 LWCHAR c; 1001 { 1002 struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); 1003 ug->ug_char = c; 1004 ug->ug_next = NULL; 1005 if (ungot == NULL) 1006 ungot = ug; 1007 else 1008 { 1009 struct ungot *pu; 1010 for (pu = ungot; pu->ug_next != NULL; pu = pu->ug_next) 1011 continue; 1012 pu->ug_next = ug; 1013 } 1014 } 1015 1016 /* 1017 * Unget a whole string of command characters. 1018 * The next sequence of getcc()'s will return this string. 1019 */ 1020 public void 1021 ungetsc(s) 1022 char *s; 1023 { 1024 while (*s != '\0') 1025 ungetcc_back(*s++); 1026 } 1027 1028 /* 1029 * Peek the next command character, without consuming it. 1030 */ 1031 public LWCHAR 1032 peekcc(VOID_PARAM) 1033 { 1034 LWCHAR c = getcc(); 1035 ungetcc(c); 1036 return c; 1037 } 1038 1039 /* 1040 * Search for a pattern, possibly in multiple files. 1041 * If SRCH_FIRST_FILE is set, begin searching at the first file. 1042 * If SRCH_PAST_EOF is set, continue the search thru multiple files. 1043 */ 1044 static void 1045 multi_search(pattern, n, silent) 1046 char *pattern; 1047 int n; 1048 int silent; 1049 { 1050 int nomore; 1051 IFILE save_ifile; 1052 int changed_file; 1053 1054 changed_file = 0; 1055 save_ifile = save_curr_ifile(); 1056 1057 if (search_type & SRCH_FIRST_FILE) 1058 { 1059 /* 1060 * Start at the first (or last) file 1061 * in the command line list. 1062 */ 1063 if (search_type & SRCH_FORW) 1064 nomore = edit_first(); 1065 else 1066 nomore = edit_last(); 1067 if (nomore) 1068 { 1069 unsave_ifile(save_ifile); 1070 return; 1071 } 1072 changed_file = 1; 1073 search_type &= ~SRCH_FIRST_FILE; 1074 } 1075 1076 for (;;) 1077 { 1078 n = search(search_type, pattern, n); 1079 /* 1080 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared 1081 * after being used once. This allows "n" to work after 1082 * using a /@@ search. 1083 */ 1084 search_type &= ~SRCH_NO_MOVE; 1085 if (n == 0) 1086 { 1087 /* 1088 * Found it. 1089 */ 1090 unsave_ifile(save_ifile); 1091 return; 1092 } 1093 1094 if (n < 0) 1095 /* 1096 * Some kind of error in the search. 1097 * Error message has been printed by search(). 1098 */ 1099 break; 1100 1101 if ((search_type & SRCH_PAST_EOF) == 0) 1102 /* 1103 * We didn't find a match, but we're 1104 * supposed to search only one file. 1105 */ 1106 break; 1107 /* 1108 * Move on to the next file. 1109 */ 1110 if (search_type & SRCH_FORW) 1111 nomore = edit_next(1); 1112 else 1113 nomore = edit_prev(1); 1114 if (nomore) 1115 break; 1116 changed_file = 1; 1117 } 1118 1119 /* 1120 * Didn't find it. 1121 * Print an error message if we haven't already. 1122 */ 1123 if (n > 0 && !silent) 1124 error("Pattern not found", NULL_PARG); 1125 1126 if (changed_file) 1127 { 1128 /* 1129 * Restore the file we were originally viewing. 1130 */ 1131 reedit_ifile(save_ifile); 1132 } else 1133 { 1134 unsave_ifile(save_ifile); 1135 } 1136 } 1137 1138 /* 1139 * Forward forever, or until a highlighted line appears. 1140 */ 1141 static int 1142 forw_loop(until_hilite) 1143 int until_hilite; 1144 { 1145 POSITION curr_len; 1146 1147 if (ch_getflags() & CH_HELPFILE) 1148 return (A_NOACTION); 1149 1150 cmd_exec(); 1151 jump_forw_buffered(); 1152 curr_len = ch_length(); 1153 highest_hilite = until_hilite ? curr_len : NULL_POSITION; 1154 ignore_eoi = 1; 1155 while (!sigs) 1156 { 1157 if (until_hilite && highest_hilite > curr_len) 1158 { 1159 bell(); 1160 break; 1161 } 1162 make_display(); 1163 forward(1, 0, 0); 1164 } 1165 ignore_eoi = 0; 1166 ch_set_eof(); 1167 1168 /* 1169 * This gets us back in "F mode" after processing 1170 * a non-abort signal (e.g. window-change). 1171 */ 1172 if (sigs && !ABORT_SIGS()) 1173 return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER); 1174 1175 return (A_NOACTION); 1176 } 1177 1178 /* 1179 * Main command processor. 1180 * Accept and execute commands until a quit command. 1181 */ 1182 public void 1183 commands(VOID_PARAM) 1184 { 1185 int c; 1186 int action; 1187 char *cbuf; 1188 int newaction; 1189 int save_jump_sline; 1190 int save_search_type; 1191 char *extra; 1192 char tbuf[2]; 1193 PARG parg; 1194 IFILE old_ifile; 1195 IFILE new_ifile; 1196 char *tagfile; 1197 1198 search_type = SRCH_FORW; 1199 wscroll = (sc_height + 1) / 2; 1200 newaction = A_NOACTION; 1201 1202 for (;;) 1203 { 1204 clear_mca(); 1205 cmd_accept(); 1206 number = 0; 1207 curropt = NULL; 1208 1209 /* 1210 * See if any signals need processing. 1211 */ 1212 if (sigs) 1213 { 1214 psignals(); 1215 if (quitting) 1216 quit(QUIT_SAVED_STATUS); 1217 } 1218 1219 /* 1220 * See if window size changed, for systems that don't 1221 * generate SIGWINCH. 1222 */ 1223 check_winch(); 1224 1225 /* 1226 * Display prompt and accept a character. 1227 */ 1228 cmd_reset(); 1229 prompt(); 1230 if (sigs) 1231 continue; 1232 if (newaction == A_NOACTION) 1233 c = getcc(); 1234 1235 again: 1236 if (sigs) 1237 continue; 1238 1239 if (newaction != A_NOACTION) 1240 { 1241 action = newaction; 1242 newaction = A_NOACTION; 1243 } else 1244 { 1245 /* 1246 * If we are in a multicharacter command, call mca_char. 1247 * Otherwise we call fcmd_decode to determine the 1248 * action to be performed. 1249 */ 1250 if (mca) 1251 switch (mca_char(c)) 1252 { 1253 case MCA_MORE: 1254 /* 1255 * Need another character. 1256 */ 1257 c = getcc(); 1258 goto again; 1259 case MCA_DONE: 1260 /* 1261 * Command has been handled by mca_char. 1262 * Start clean with a prompt. 1263 */ 1264 continue; 1265 case NO_MCA: 1266 /* 1267 * Not a multi-char command 1268 * (at least, not anymore). 1269 */ 1270 break; 1271 } 1272 1273 /* 1274 * Decode the command character and decide what to do. 1275 */ 1276 if (mca) 1277 { 1278 /* 1279 * We're in a multichar command. 1280 * Add the character to the command buffer 1281 * and display it on the screen. 1282 * If the user backspaces past the start 1283 * of the line, abort the command. 1284 */ 1285 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) 1286 continue; 1287 cbuf = get_cmdbuf(); 1288 } else 1289 { 1290 /* 1291 * Don't use cmd_char if we're starting fresh 1292 * at the beginning of a command, because we 1293 * don't want to echo the command until we know 1294 * it is a multichar command. We also don't 1295 * want erase_char/kill_char to be treated 1296 * as line editing characters. 1297 */ 1298 tbuf[0] = c; 1299 tbuf[1] = '\0'; 1300 cbuf = tbuf; 1301 } 1302 extra = NULL; 1303 action = fcmd_decode(cbuf, &extra); 1304 /* 1305 * If an "extra" string was returned, 1306 * process it as a string of command characters. 1307 */ 1308 if (extra != NULL) 1309 ungetsc(extra); 1310 } 1311 /* 1312 * Clear the cmdbuf string. 1313 * (But not if we're in the prefix of a command, 1314 * because the partial command string is kept there.) 1315 */ 1316 if (action != A_PREFIX) 1317 cmd_reset(); 1318 1319 switch (action) 1320 { 1321 case A_DIGIT: 1322 /* 1323 * First digit of a number. 1324 */ 1325 start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE); 1326 goto again; 1327 1328 case A_F_WINDOW: 1329 /* 1330 * Forward one window (and set the window size). 1331 */ 1332 if (number > 0) 1333 swindow = (int) number; 1334 /* FALLTHRU */ 1335 case A_F_SCREEN: 1336 /* 1337 * Forward one screen. 1338 */ 1339 if (number <= 0) 1340 number = get_swindow(); 1341 cmd_exec(); 1342 if (show_attn) 1343 set_attnpos(bottompos); 1344 forward((int) number, 0, 1); 1345 break; 1346 1347 case A_B_WINDOW: 1348 /* 1349 * Backward one window (and set the window size). 1350 */ 1351 if (number > 0) 1352 swindow = (int) number; 1353 /* FALLTHRU */ 1354 case A_B_SCREEN: 1355 /* 1356 * Backward one screen. 1357 */ 1358 if (number <= 0) 1359 number = get_swindow(); 1360 cmd_exec(); 1361 backward((int) number, 0, 1); 1362 break; 1363 1364 case A_F_LINE: 1365 /* 1366 * Forward N (default 1) line. 1367 */ 1368 if (number <= 0) 1369 number = 1; 1370 cmd_exec(); 1371 if (show_attn == OPT_ONPLUS && number > 1) 1372 set_attnpos(bottompos); 1373 forward((int) number, 0, 0); 1374 break; 1375 1376 case A_B_LINE: 1377 /* 1378 * Backward N (default 1) line. 1379 */ 1380 if (number <= 0) 1381 number = 1; 1382 cmd_exec(); 1383 backward((int) number, 0, 0); 1384 break; 1385 1386 case A_F_MOUSE: 1387 /* 1388 * Forward wheel_lines lines. 1389 */ 1390 cmd_exec(); 1391 forward(wheel_lines, 0, 0); 1392 break; 1393 1394 case A_B_MOUSE: 1395 /* 1396 * Backward wheel_lines lines. 1397 */ 1398 cmd_exec(); 1399 backward(wheel_lines, 0, 0); 1400 break; 1401 1402 case A_FF_LINE: 1403 /* 1404 * Force forward N (default 1) line. 1405 */ 1406 if (number <= 0) 1407 number = 1; 1408 cmd_exec(); 1409 if (show_attn == OPT_ONPLUS && number > 1) 1410 set_attnpos(bottompos); 1411 forward((int) number, 1, 0); 1412 break; 1413 1414 case A_BF_LINE: 1415 /* 1416 * Force backward N (default 1) line. 1417 */ 1418 if (number <= 0) 1419 number = 1; 1420 cmd_exec(); 1421 backward((int) number, 1, 0); 1422 break; 1423 1424 case A_FF_SCREEN: 1425 /* 1426 * Force forward one screen. 1427 */ 1428 if (number <= 0) 1429 number = get_swindow(); 1430 cmd_exec(); 1431 if (show_attn == OPT_ONPLUS) 1432 set_attnpos(bottompos); 1433 forward((int) number, 1, 0); 1434 break; 1435 1436 case A_F_FOREVER: 1437 /* 1438 * Forward forever, ignoring EOF. 1439 */ 1440 if (show_attn) 1441 set_attnpos(bottompos); 1442 newaction = forw_loop(0); 1443 break; 1444 1445 case A_F_UNTIL_HILITE: 1446 newaction = forw_loop(1); 1447 break; 1448 1449 case A_F_SCROLL: 1450 /* 1451 * Forward N lines 1452 * (default same as last 'd' or 'u' command). 1453 */ 1454 if (number > 0) 1455 wscroll = (int) number; 1456 cmd_exec(); 1457 if (show_attn == OPT_ONPLUS) 1458 set_attnpos(bottompos); 1459 forward(wscroll, 0, 0); 1460 break; 1461 1462 case A_B_SCROLL: 1463 /* 1464 * Forward N lines 1465 * (default same as last 'd' or 'u' command). 1466 */ 1467 if (number > 0) 1468 wscroll = (int) number; 1469 cmd_exec(); 1470 backward(wscroll, 0, 0); 1471 break; 1472 1473 case A_FREPAINT: 1474 /* 1475 * Flush buffers, then repaint screen. 1476 * Don't flush the buffers on a pipe! 1477 */ 1478 clear_buffers(); 1479 /* FALLTHRU */ 1480 case A_REPAINT: 1481 /* 1482 * Repaint screen. 1483 */ 1484 cmd_exec(); 1485 repaint(); 1486 break; 1487 1488 case A_GOLINE: 1489 /* 1490 * Go to line N, default beginning of file. 1491 * If N <= 0, ignore jump_sline in order to avoid 1492 * empty lines before the beginning of the file. 1493 */ 1494 save_jump_sline = jump_sline; 1495 if (number <= 0) 1496 { 1497 number = 1; 1498 jump_sline = 0; 1499 } 1500 cmd_exec(); 1501 jump_back(number); 1502 jump_sline = save_jump_sline; 1503 break; 1504 1505 case A_PERCENT: 1506 /* 1507 * Go to a specified percentage into the file. 1508 */ 1509 if (number < 0) 1510 { 1511 number = 0; 1512 fraction = 0; 1513 } 1514 if (number > 100 || (number == 100 && fraction != 0)) 1515 { 1516 number = 100; 1517 fraction = 0; 1518 } 1519 cmd_exec(); 1520 jump_percent((int) number, fraction); 1521 break; 1522 1523 case A_GOEND: 1524 /* 1525 * Go to line N, default end of file. 1526 */ 1527 cmd_exec(); 1528 if (number <= 0) 1529 jump_forw(); 1530 else 1531 jump_back(number); 1532 break; 1533 1534 case A_GOEND_BUF: 1535 /* 1536 * Go to line N, default last buffered byte. 1537 */ 1538 cmd_exec(); 1539 if (number <= 0) 1540 jump_forw_buffered(); 1541 else 1542 jump_back(number); 1543 break; 1544 1545 case A_GOPOS: 1546 /* 1547 * Go to a specified byte position in the file. 1548 */ 1549 cmd_exec(); 1550 if (number < 0) 1551 number = 0; 1552 jump_line_loc((POSITION) number, jump_sline); 1553 break; 1554 1555 case A_STAT: 1556 /* 1557 * Print file name, etc. 1558 */ 1559 if (ch_getflags() & CH_HELPFILE) 1560 break; 1561 cmd_exec(); 1562 parg.p_string = eq_message(); 1563 error("%s", &parg); 1564 break; 1565 1566 case A_VERSION: 1567 /* 1568 * Print version number. 1569 */ 1570 cmd_exec(); 1571 dispversion(); 1572 break; 1573 1574 case A_QUIT: 1575 /* 1576 * Exit. 1577 */ 1578 if (curr_ifile != NULL_IFILE && 1579 ch_getflags() & CH_HELPFILE) 1580 { 1581 /* 1582 * Quit while viewing the help file 1583 * just means return to viewing the 1584 * previous file. 1585 */ 1586 hshift = save_hshift; 1587 bs_mode = save_bs_mode; 1588 if (edit_prev(1) == 0) 1589 break; 1590 } 1591 if (extra != NULL) 1592 quit(*extra); 1593 quit(QUIT_OK); 1594 break; 1595 1596 /* 1597 * Define abbreviation for a commonly used sequence below. 1598 */ 1599 #define DO_SEARCH() \ 1600 if (number <= 0) number = 1; \ 1601 mca_search(); \ 1602 cmd_exec(); \ 1603 multi_search((char *)NULL, (int) number, 0); 1604 1605 1606 case A_F_SEARCH: 1607 /* 1608 * Search forward for a pattern. 1609 * Get the first char of the pattern. 1610 */ 1611 search_type = SRCH_FORW; 1612 if (number <= 0) 1613 number = 1; 1614 mca_search(); 1615 c = getcc(); 1616 goto again; 1617 1618 case A_B_SEARCH: 1619 /* 1620 * Search backward for a pattern. 1621 * Get the first char of the pattern. 1622 */ 1623 search_type = SRCH_BACK; 1624 if (number <= 0) 1625 number = 1; 1626 mca_search(); 1627 c = getcc(); 1628 goto again; 1629 1630 case A_FILTER: 1631 #if HILITE_SEARCH 1632 search_type = SRCH_FORW | SRCH_FILTER; 1633 mca_search(); 1634 c = getcc(); 1635 goto again; 1636 #else 1637 error("Command not available", NULL_PARG); 1638 break; 1639 #endif 1640 1641 case A_AGAIN_SEARCH: 1642 /* 1643 * Repeat previous search. 1644 */ 1645 DO_SEARCH(); 1646 break; 1647 1648 case A_T_AGAIN_SEARCH: 1649 /* 1650 * Repeat previous search, multiple files. 1651 */ 1652 search_type |= SRCH_PAST_EOF; 1653 DO_SEARCH(); 1654 break; 1655 1656 case A_REVERSE_SEARCH: 1657 /* 1658 * Repeat previous search, in reverse direction. 1659 */ 1660 save_search_type = search_type; 1661 search_type = SRCH_REVERSE(search_type); 1662 DO_SEARCH(); 1663 search_type = save_search_type; 1664 break; 1665 1666 case A_T_REVERSE_SEARCH: 1667 /* 1668 * Repeat previous search, 1669 * multiple files in reverse direction. 1670 */ 1671 save_search_type = search_type; 1672 search_type = SRCH_REVERSE(search_type); 1673 search_type |= SRCH_PAST_EOF; 1674 DO_SEARCH(); 1675 search_type = save_search_type; 1676 break; 1677 1678 case A_UNDO_SEARCH: 1679 case A_CLR_SEARCH: 1680 /* 1681 * Clear search string highlighting. 1682 */ 1683 undo_search(action == A_CLR_SEARCH); 1684 break; 1685 1686 case A_HELP: 1687 /* 1688 * Help. 1689 */ 1690 if (ch_getflags() & CH_HELPFILE) 1691 break; 1692 cmd_exec(); 1693 save_hshift = hshift; 1694 hshift = 0; 1695 save_bs_mode = bs_mode; 1696 bs_mode = BS_SPECIAL; 1697 (void) edit(FAKE_HELPFILE); 1698 break; 1699 1700 case A_EXAMINE: 1701 /* 1702 * Edit a new file. Get the filename. 1703 */ 1704 #if EXAMINE 1705 if (!secure) 1706 { 1707 start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); 1708 c = getcc(); 1709 goto again; 1710 } 1711 #endif 1712 error("Command not available", NULL_PARG); 1713 break; 1714 1715 case A_VISUAL: 1716 /* 1717 * Invoke an editor on the input file. 1718 */ 1719 #if EDITOR 1720 if (!secure) 1721 { 1722 if (ch_getflags() & CH_HELPFILE) 1723 break; 1724 if (strcmp(get_filename(curr_ifile), "-") == 0) 1725 { 1726 error("Cannot edit standard input", NULL_PARG); 1727 break; 1728 } 1729 if (get_altfilename(curr_ifile) != NULL) 1730 { 1731 error("WARNING: This file was viewed via LESSOPEN", 1732 NULL_PARG); 1733 } 1734 start_mca(A_SHELL, "!", ml_shell, 0); 1735 /* 1736 * Expand the editor prototype string 1737 * and pass it to the system to execute. 1738 * (Make sure the screen is displayed so the 1739 * expansion of "+%lm" works.) 1740 */ 1741 make_display(); 1742 cmd_exec(); 1743 lsystem(pr_expand(editproto, 0), (char*)NULL); 1744 break; 1745 } 1746 #endif 1747 error("Command not available", NULL_PARG); 1748 break; 1749 1750 case A_NEXT_FILE: 1751 /* 1752 * Examine next file. 1753 */ 1754 #if TAGS 1755 if (ntags()) 1756 { 1757 error("No next file", NULL_PARG); 1758 break; 1759 } 1760 #endif 1761 if (number <= 0) 1762 number = 1; 1763 if (edit_next((int) number)) 1764 { 1765 if (get_quit_at_eof() && eof_displayed() && 1766 !(ch_getflags() & CH_HELPFILE)) 1767 quit(QUIT_OK); 1768 parg.p_string = (number > 1) ? "(N-th) " : ""; 1769 error("No %snext file", &parg); 1770 } 1771 break; 1772 1773 case A_PREV_FILE: 1774 /* 1775 * Examine previous file. 1776 */ 1777 #if TAGS 1778 if (ntags()) 1779 { 1780 error("No previous file", NULL_PARG); 1781 break; 1782 } 1783 #endif 1784 if (number <= 0) 1785 number = 1; 1786 if (edit_prev((int) number)) 1787 { 1788 parg.p_string = (number > 1) ? "(N-th) " : ""; 1789 error("No %sprevious file", &parg); 1790 } 1791 break; 1792 1793 case A_NEXT_TAG: 1794 /* 1795 * Jump to the next tag in the current tag list. 1796 */ 1797 #if TAGS 1798 if (number <= 0) 1799 number = 1; 1800 tagfile = nexttag((int) number); 1801 if (tagfile == NULL) 1802 { 1803 error("No next tag", NULL_PARG); 1804 break; 1805 } 1806 cmd_exec(); 1807 if (edit(tagfile) == 0) 1808 { 1809 POSITION pos = tagsearch(); 1810 if (pos != NULL_POSITION) 1811 jump_loc(pos, jump_sline); 1812 } 1813 #else 1814 error("Command not available", NULL_PARG); 1815 #endif 1816 break; 1817 1818 case A_PREV_TAG: 1819 /* 1820 * Jump to the previous tag in the current tag list. 1821 */ 1822 #if TAGS 1823 if (number <= 0) 1824 number = 1; 1825 tagfile = prevtag((int) number); 1826 if (tagfile == NULL) 1827 { 1828 error("No previous tag", NULL_PARG); 1829 break; 1830 } 1831 cmd_exec(); 1832 if (edit(tagfile) == 0) 1833 { 1834 POSITION pos = tagsearch(); 1835 if (pos != NULL_POSITION) 1836 jump_loc(pos, jump_sline); 1837 } 1838 #else 1839 error("Command not available", NULL_PARG); 1840 #endif 1841 break; 1842 1843 case A_INDEX_FILE: 1844 /* 1845 * Examine a particular file. 1846 */ 1847 if (number <= 0) 1848 number = 1; 1849 if (edit_index((int) number)) 1850 error("No such file", NULL_PARG); 1851 break; 1852 1853 case A_REMOVE_FILE: 1854 /* 1855 * Remove a file from the input file list. 1856 */ 1857 if (ch_getflags() & CH_HELPFILE) 1858 break; 1859 old_ifile = curr_ifile; 1860 new_ifile = getoff_ifile(curr_ifile); 1861 if (new_ifile == NULL_IFILE) 1862 { 1863 bell(); 1864 break; 1865 } 1866 if (edit_ifile(new_ifile) != 0) 1867 { 1868 reedit_ifile(old_ifile); 1869 break; 1870 } 1871 del_ifile(old_ifile); 1872 break; 1873 1874 case A_OPT_TOGGLE: 1875 /* 1876 * Change the setting of an option. 1877 */ 1878 optflag = OPT_TOGGLE; 1879 optgetname = FALSE; 1880 mca_opt_toggle(); 1881 c = getcc(); 1882 cbuf = opt_toggle_disallowed(c); 1883 if (cbuf != NULL) 1884 { 1885 error(cbuf, NULL_PARG); 1886 break; 1887 } 1888 goto again; 1889 1890 case A_DISP_OPTION: 1891 /* 1892 * Report the setting of an option. 1893 */ 1894 optflag = OPT_NO_TOGGLE; 1895 optgetname = FALSE; 1896 mca_opt_toggle(); 1897 c = getcc(); 1898 goto again; 1899 1900 case A_FIRSTCMD: 1901 /* 1902 * Set an initial command for new files. 1903 */ 1904 start_mca(A_FIRSTCMD, "+", (void*)NULL, 0); 1905 c = getcc(); 1906 goto again; 1907 1908 case A_SHELL: 1909 /* 1910 * Shell escape. 1911 */ 1912 #if SHELL_ESCAPE 1913 if (!secure) 1914 { 1915 start_mca(A_SHELL, "!", ml_shell, 0); 1916 c = getcc(); 1917 goto again; 1918 } 1919 #endif 1920 error("Command not available", NULL_PARG); 1921 break; 1922 1923 case A_SETMARK: 1924 case A_SETMARKBOT: 1925 /* 1926 * Set a mark. 1927 */ 1928 if (ch_getflags() & CH_HELPFILE) 1929 break; 1930 start_mca(A_SETMARK, "set mark: ", (void*)NULL, 0); 1931 c = getcc(); 1932 if (is_erase_char(c) || is_newline_char(c)) 1933 break; 1934 setmark(c, action == A_SETMARKBOT ? BOTTOM : TOP); 1935 repaint(); 1936 break; 1937 1938 case A_CLRMARK: 1939 /* 1940 * Clear a mark. 1941 */ 1942 start_mca(A_CLRMARK, "clear mark: ", (void*)NULL, 0); 1943 c = getcc(); 1944 if (is_erase_char(c) || is_newline_char(c)) 1945 break; 1946 clrmark(c); 1947 repaint(); 1948 break; 1949 1950 case A_GOMARK: 1951 /* 1952 * Jump to a marked position. 1953 */ 1954 start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); 1955 c = getcc(); 1956 if (is_erase_char(c) || is_newline_char(c)) 1957 break; 1958 cmd_exec(); 1959 gomark(c); 1960 break; 1961 1962 case A_PIPE: 1963 /* 1964 * Write part of the input to a pipe to a shell command. 1965 */ 1966 #if PIPEC 1967 if (!secure) 1968 { 1969 start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); 1970 c = getcc(); 1971 if (is_erase_char(c)) 1972 break; 1973 if (is_newline_char(c)) 1974 c = '.'; 1975 if (badmark(c)) 1976 break; 1977 pipec = c; 1978 start_mca(A_PIPE, "!", ml_shell, 0); 1979 c = getcc(); 1980 goto again; 1981 } 1982 #endif 1983 error("Command not available", NULL_PARG); 1984 break; 1985 1986 case A_B_BRACKET: 1987 case A_F_BRACKET: 1988 start_mca(action, "Brackets: ", (void*)NULL, 0); 1989 c = getcc(); 1990 goto again; 1991 1992 case A_LSHIFT: 1993 /* 1994 * Shift view left. 1995 */ 1996 if (number > 0) 1997 shift_count = number; 1998 else 1999 number = (shift_count > 0) ? 2000 shift_count : sc_width / 2; 2001 if (number > hshift) 2002 number = hshift; 2003 hshift -= number; 2004 screen_trashed = 1; 2005 break; 2006 2007 case A_RSHIFT: 2008 /* 2009 * Shift view right. 2010 */ 2011 if (number > 0) 2012 shift_count = number; 2013 else 2014 number = (shift_count > 0) ? 2015 shift_count : sc_width / 2; 2016 hshift += number; 2017 screen_trashed = 1; 2018 break; 2019 2020 case A_LLSHIFT: 2021 /* 2022 * Shift view left to margin. 2023 */ 2024 hshift = 0; 2025 screen_trashed = 1; 2026 break; 2027 2028 case A_RRSHIFT: 2029 /* 2030 * Shift view right to view rightmost char on screen. 2031 */ 2032 hshift = rrshift(); 2033 screen_trashed = 1; 2034 break; 2035 2036 case A_PREFIX: 2037 /* 2038 * The command is incomplete (more chars are needed). 2039 * Display the current char, so the user knows 2040 * what's going on, and get another character. 2041 */ 2042 if (mca != A_PREFIX) 2043 { 2044 cmd_reset(); 2045 start_mca(A_PREFIX, " ", (void*)NULL, 2046 CF_QUIT_ON_ERASE); 2047 (void) cmd_char(c); 2048 } 2049 c = getcc(); 2050 goto again; 2051 2052 case A_NOACTION: 2053 break; 2054 2055 default: 2056 bell(); 2057 break; 2058 } 2059 } 2060 } 2061