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