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