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