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