1 /* 2 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice in the documentation and/or other materials provided with 12 * the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 28 /* 29 * Functions which manipulate the command buffer. 30 * Used only by command() and related functions. 31 */ 32 33 #include "less.h" 34 #include "cmd.h" 35 36 extern int sc_width; 37 38 static char cmdbuf[120]; /* Buffer for holding a multi-char command */ 39 static int cmd_col; /* Current column of the multi-char command */ 40 static char *cp; /* Pointer into cmdbuf */ 41 static int literal; 42 43 #if TAB_COMPLETE_FILENAME 44 static int cmd_complete(); 45 /* 46 * These variables are statics used by cmd_complete. 47 */ 48 static int in_completion = 0; 49 static char *tk_text; 50 static char *tk_original; 51 static char *tk_ipoint; 52 static char *tk_trial; 53 static struct textlist tk_tlist; 54 #endif 55 56 #if CMD_HISTORY 57 /* 58 * A mlist structure represents a command history. 59 */ 60 struct mlist 61 { 62 struct mlist *next; 63 struct mlist *prev; 64 struct mlist *curr_mp; 65 char *string; 66 }; 67 68 /* 69 * These are the various command histories that exist. 70 */ 71 struct mlist mlist_search = 72 { &mlist_search, &mlist_search, &mlist_search, NULL }; 73 public void *ml_search = (void *) &mlist_search; 74 struct mlist mlist_examine = 75 { &mlist_examine, &mlist_examine, &mlist_examine, NULL }; 76 public void *ml_examine = (void *) &mlist_examine; 77 #if SHELL_ESCAPE || PIPEC 78 struct mlist mlist_shell = 79 { &mlist_shell, &mlist_shell, &mlist_shell, NULL }; 80 public void *ml_shell = (void *) &mlist_shell; 81 #endif /* SHELL_ESCAPE || PIPEC */ 82 83 /* 84 * History for the current command. 85 */ 86 static struct mlist *curr_mlist = NULL; 87 88 #endif /* CMD_HISTORY */ 89 90 /* 91 * Reset command buffer (to empty). 92 */ 93 public void 94 cmd_reset() 95 { 96 cp = cmdbuf; 97 *cp = '\0'; 98 cmd_col = 0; 99 literal = 0; 100 } 101 102 /* 103 * How many characters are in the command buffer? 104 */ 105 public int 106 len_cmdbuf() 107 { 108 return (strlen(cmdbuf)); 109 } 110 111 /* 112 * Backspace in the command buffer. 113 * Delete the char to the left of the cursor. 114 */ 115 static int 116 cmd_erase() 117 { 118 register char *s; 119 char *p; 120 int col; 121 122 if (cp == cmdbuf) 123 { 124 /* 125 * Backspace past beginning of the buffer: 126 * this usually means abort the command. 127 */ 128 return (CC_QUIT); 129 } 130 /* 131 * Back up the pointer. 132 */ 133 --cp; 134 /* 135 * Remember the current cursor column and 136 * set it back the width of the char being erased. 137 */ 138 col = cmd_col; 139 p = prchar(*cp); 140 cmd_col -= strlen(p); 141 /* 142 * Shift left the buffer after the erased char. 143 */ 144 for (s = cp; *s != '\0'; s++) 145 s[0] = s[1]; 146 /* 147 * Back up the cursor to the position of the erased char, 148 * clear the tail of the line, 149 * and reprint the line after the erased char. 150 */ 151 while (col > cmd_col) 152 { 153 putbs(); 154 col--; 155 } 156 clear_eol(); 157 for (s = cp; *s != '\0'; s++) 158 { 159 p = prchar(*s); 160 putstr(p); 161 col += strlen(p); 162 } 163 /* 164 * Back up the cursor again. 165 */ 166 while (col > cmd_col) 167 { 168 putbs(); 169 col--; 170 } 171 172 /* 173 * This is rather weird. 174 * We say that erasing the entire command string causes us 175 * to abort the current command, BUT ONLY IF there is no history 176 * for this type of command. This causes commands like search (/) 177 * and edit (:e) to stay active even if we erase the entire string, 178 * but commands like <digit> and - go away when we erase the string. 179 * (See same thing in cmd_kill.) 180 */ 181 if (curr_mlist == NULL && cp == cmdbuf && *cp == '\0') 182 return (CC_QUIT); 183 return (CC_OK); 184 } 185 186 /* 187 * Delete the char under the cursor. 188 */ 189 static int 190 cmd_delete() 191 { 192 char *p; 193 194 if (*cp == '\0') 195 { 196 /* 197 * At end of string; there is no char under the cursor. 198 */ 199 return (CC_OK); 200 } 201 /* 202 * Move right, then use cmd_erase. 203 */ 204 p = prchar(*cp); 205 cp++; 206 putstr(p); 207 cmd_col += strlen(p); 208 cmd_erase(); 209 return (CC_OK); 210 } 211 212 /* 213 * Delete the "word" to the left of the cursor. 214 */ 215 static int 216 cmd_werase() 217 { 218 if (cp > cmdbuf && cp[-1] == ' ') 219 { 220 /* 221 * If the char left of cursor is a space, 222 * erase all the spaces left of cursor (to the first non-space). 223 */ 224 while (cp > cmdbuf && cp[-1] == ' ') 225 (void) cmd_erase(); 226 } else 227 { 228 /* 229 * If the char left of cursor is not a space, 230 * erase all the nonspaces left of cursor (the whole "word"). 231 */ 232 while (cp > cmdbuf && cp[-1] != ' ') 233 (void) cmd_erase(); 234 } 235 return (CC_OK); 236 } 237 238 /* 239 * Delete the "word" under the cursor. 240 */ 241 static int 242 cmd_wdelete() 243 { 244 if (*cp == ' ') 245 { 246 /* 247 * If the char under the cursor is a space, 248 * delete it and all the spaces right of cursor. 249 */ 250 while (*cp == ' ') 251 (void) cmd_delete(); 252 } else 253 { 254 /* 255 * If the char under the cursor is not a space, 256 * delete it and all nonspaces right of cursor (the whole word). 257 */ 258 while (*cp != ' ' && *cp != '\0') 259 (void) cmd_delete(); 260 } 261 return (CC_OK); 262 } 263 264 /* 265 * Move cursor to start of command buffer. 266 */ 267 static int 268 cmd_home() 269 { 270 char *p; 271 272 /* 273 * Back up until we hit start of buffer. 274 */ 275 while (cp > cmdbuf) 276 { 277 cp--; 278 p = prchar(*cp); 279 cmd_col -= strlen(p); 280 while (*p++ != '\0') 281 putbs(); 282 } 283 return (CC_OK); 284 } 285 286 /* 287 * Delete all chars in the command buffer. 288 */ 289 static int 290 cmd_kill() 291 { 292 if (cmdbuf[0] == '\0') 293 { 294 /* 295 * Buffer is already empty; abort the current command. 296 */ 297 return (CC_QUIT); 298 } 299 (void) cmd_home(); 300 *cp = '\0'; 301 clear_eol(); 302 /* 303 * Same weirdness as in cmd_erase. 304 * If the current command has no history, abort the current command. 305 */ 306 if (curr_mlist == NULL) 307 return (CC_QUIT); 308 return (CC_OK); 309 } 310 311 /* 312 * Move cursor right one character. 313 */ 314 static int 315 cmd_right() 316 { 317 char *p; 318 319 if (*cp == '\0') 320 { 321 /* 322 * Already at the end of the line. 323 */ 324 return (CC_OK); 325 } 326 p = prchar(*cp); 327 cp++; 328 putstr(p); 329 cmd_col += strlen(p); 330 return (CC_OK); 331 } 332 333 /* 334 * Move cursor left one character. 335 */ 336 static int 337 cmd_left() 338 { 339 char *p; 340 341 if (cp <= cmdbuf) 342 { 343 /* Already at the beginning of the line */ 344 return (CC_OK); 345 } 346 cp--; 347 p = prchar(*cp); 348 cmd_col -= strlen(p); 349 while (*p++ != '\0') 350 putbs(); 351 return (CC_OK); 352 } 353 354 #if CMD_HISTORY 355 /* 356 * Select an mlist structure to be the current command history. 357 */ 358 public void 359 set_mlist(mlist) 360 void *mlist; 361 { 362 curr_mlist = (struct mlist *) mlist; 363 } 364 365 /* 366 * Move up or down in the currently selected command history list. 367 */ 368 static int 369 cmd_updown(action) 370 int action; 371 { 372 char *p; 373 char *s; 374 375 if (curr_mlist == NULL) 376 { 377 /* 378 * The current command has no history list. 379 */ 380 bell(); 381 return (CC_OK); 382 } 383 cmd_home(); 384 clear_eol(); 385 /* 386 * Move curr_mp to the next/prev entry. 387 */ 388 if (action == EC_UP) 389 curr_mlist->curr_mp = curr_mlist->curr_mp->prev; 390 else 391 curr_mlist->curr_mp = curr_mlist->curr_mp->next; 392 /* 393 * Copy the entry into cmdbuf and echo it on the screen. 394 */ 395 s = curr_mlist->curr_mp->string; 396 if (s == NULL) 397 s = ""; 398 for (cp = cmdbuf; *s != '\0'; s++, cp++) 399 { 400 *cp = *s; 401 p = prchar(*cp); 402 cmd_col += strlen(p); 403 putstr(p); 404 } 405 *cp = '\0'; 406 return (CC_OK); 407 } 408 409 /* 410 * Accept the command in the command buffer. 411 * Add it to the currently selected history list. 412 */ 413 public void 414 cmd_accept() 415 { 416 struct mlist *ml; 417 418 /* 419 * Nothing to do if there is no currently selected history list. 420 */ 421 if (curr_mlist == NULL) 422 return; 423 /* 424 * Don't save a trivial command. 425 */ 426 if (strlen(cmdbuf) == 0) 427 return; 428 /* 429 * Don't save if a duplicate of a command which is already in the history. 430 * But select the one already in the history to be current. 431 */ 432 for (ml = curr_mlist->next; ml != curr_mlist; ml = ml->next) 433 { 434 if (strcmp(ml->string, cmdbuf) == 0) 435 break; 436 } 437 if (ml == curr_mlist) 438 { 439 /* 440 * Did not find command in history. 441 * Save the command and put it at the end of the history list. 442 */ 443 ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 444 ml->string = save(cmdbuf); 445 ml->next = curr_mlist; 446 ml->prev = curr_mlist->prev; 447 curr_mlist->prev->next = ml; 448 curr_mlist->prev = ml; 449 } 450 /* 451 * Point to the cmd just after the just-accepted command. 452 * Thus, an UPARROW will always retrieve the previous command. 453 */ 454 curr_mlist->curr_mp = ml->next; 455 } 456 #endif 457 458 /* 459 * Try to perform a line-edit function on the command buffer, 460 * using a specified char as a line-editing command. 461 * Returns: 462 * CC_PASS The char does not invoke a line edit function. 463 * CC_OK Line edit function done. 464 * CC_QUIT The char requests the current command to be aborted. 465 */ 466 static int 467 cmd_edit(c) 468 int c; 469 { 470 int action; 471 int flags; 472 473 #if TAB_COMPLETE_FILENAME 474 #define not_in_completion() in_completion = 0 475 #else 476 #define not_in_completion() 477 #endif 478 479 /* 480 * See if the char is indeed a line-editing command. 481 */ 482 flags = 0; 483 if (curr_mlist == NULL) 484 /* 485 * No current history; don't accept history manipulation cmds. 486 */ 487 flags |= EC_NOHISTORY; 488 if (curr_mlist == &mlist_search) 489 /* 490 * In a search command; don't accept file-completion cmds. 491 */ 492 flags |= EC_NOCOMPLETE; 493 494 action = editchar(c, flags); 495 496 switch (action) 497 { 498 case EC_RIGHT: 499 not_in_completion(); 500 return (cmd_right()); 501 case EC_LEFT: 502 not_in_completion(); 503 return (cmd_left()); 504 case EC_W_RIGHT: 505 not_in_completion(); 506 while (*cp != '\0' && *cp != ' ') 507 cmd_right(); 508 while (*cp == ' ') 509 cmd_right(); 510 return (CC_OK); 511 case EC_W_LEFT: 512 not_in_completion(); 513 while (cp > cmdbuf && cp[-1] == ' ') 514 cmd_left(); 515 while (cp > cmdbuf && cp[-1] != ' ') 516 cmd_left(); 517 return (CC_OK); 518 case EC_HOME: 519 not_in_completion(); 520 return (cmd_home()); 521 case EC_END: 522 not_in_completion(); 523 while (*cp != '\0') 524 cmd_right(); 525 return (CC_OK); 526 case EC_INSERT: 527 not_in_completion(); 528 return (CC_OK); 529 case EC_BACKSPACE: 530 not_in_completion(); 531 return (cmd_erase()); 532 case EC_LINEKILL: 533 not_in_completion(); 534 return (cmd_kill()); 535 case EC_W_BACKSPACE: 536 not_in_completion(); 537 return (cmd_werase()); 538 case EC_DELETE: 539 not_in_completion(); 540 return (cmd_delete()); 541 case EC_W_DELETE: 542 not_in_completion(); 543 return (cmd_wdelete()); 544 case EC_LITERAL: 545 literal = 1; 546 return (CC_OK); 547 #if CMD_HISTORY 548 case EC_UP: 549 case EC_DOWN: 550 not_in_completion(); 551 return (cmd_updown(action)); 552 #endif 553 #if TAB_COMPLETE_FILENAME 554 case EC_F_COMPLETE: 555 case EC_B_COMPLETE: 556 case EC_EXPAND: 557 return (cmd_complete(action)); 558 #endif 559 default: 560 not_in_completion(); 561 return (CC_PASS); 562 } 563 } 564 565 /* 566 * Insert a char into the command buffer, at the current position. 567 */ 568 static int 569 cmd_ichar(c) 570 int c; 571 { 572 int col; 573 char *p; 574 char *s; 575 576 if (strlen(cmdbuf) >= sizeof(cmdbuf)-2) 577 { 578 /* 579 * No room in the command buffer for another char. 580 */ 581 bell(); 582 return (CC_ERROR); 583 } 584 585 /* 586 * Remember the current cursor column and 587 * move it forward the width of the char being inserted. 588 */ 589 col = cmd_col; 590 p = prchar(c); 591 cmd_col += strlen(p); 592 if (cmd_col >= sc_width-1) 593 { 594 cmd_col -= strlen(p); 595 bell(); 596 return (CC_ERROR); 597 } 598 /* 599 * Insert the character in the string. 600 * First, make room for the new char. 601 */ 602 for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 603 s[1] = s[0]; 604 *cp++ = c; 605 /* 606 * Reprint the tail of the line after the inserted char. 607 */ 608 clear_eol(); 609 for (s = cp-1; *s != '\0'; s++) 610 { 611 p = prchar(*s); 612 col += strlen(p); 613 if (col >= sc_width-1) 614 { 615 /* 616 * Oops. There is no room on the screen 617 * for the new char. Back up the cursor to 618 * just after the inserted char and erase it. 619 */ 620 col -= strlen(p); 621 while (col > cmd_col) 622 { 623 putbs(); 624 col--; 625 } 626 (void) cmd_erase(); 627 bell(); 628 return (CC_ERROR); 629 } 630 putstr(p); 631 } 632 /* 633 * Back up the cursor to just after the inserted char. 634 */ 635 while (col > cmd_col) 636 { 637 putbs(); 638 col--; 639 } 640 return (CC_OK); 641 } 642 643 #if TAB_COMPLETE_FILENAME 644 /* 645 * Insert a string into the command buffer, at the current position. 646 */ 647 static int 648 cmd_istr(str) 649 char *str; 650 { 651 char *s; 652 int action; 653 654 for (s = str; *s != '\0'; s++) 655 { 656 action = cmd_ichar(*s); 657 if (action != CC_OK) 658 { 659 bell(); 660 return (action); 661 } 662 } 663 return (CC_OK); 664 } 665 666 /* 667 * Find the beginning and end of the "current" word. 668 * This is the word which the cursor (cp) is inside or at the end of. 669 * Return pointer to the beginning of the word and put the 670 * cursor at the end of the word. 671 */ 672 static char * 673 delimit_word() 674 { 675 char *word; 676 677 /* 678 * Move cursor to end of word. 679 */ 680 if (*cp != ' ' && *cp != '\0') 681 { 682 /* 683 * Cursor is on a nonspace. 684 * Move cursor right to the next space. 685 */ 686 while (*cp != ' ' && *cp != '\0') 687 cmd_right(); 688 } else if (cp > cmdbuf && cp[-1] != ' ') 689 { 690 /* 691 * Cursor is on a space, and char to the left is a nonspace. 692 * We're already at the end of the word. 693 */ 694 ; 695 } else 696 { 697 /* 698 * Cursor is on a space and char to the left is a space. 699 * Huh? There's no word here. 700 */ 701 return (NULL); 702 } 703 /* 704 * Search backwards for beginning of the word. 705 */ 706 if (cp == cmdbuf) 707 return (NULL); 708 for (word = cp-1; word > cmdbuf; word--) 709 if (word[-1] == ' ') 710 break; 711 return (word); 712 } 713 714 /* 715 * Set things up to enter completion mode. 716 * Expand the word under the cursor into a list of filenames 717 * which start with that word, and set tk_text to that list. 718 */ 719 static void 720 init_compl() 721 { 722 char *word; 723 char c; 724 725 /* 726 * Get rid of any previous tk_text. 727 */ 728 if (tk_text != NULL) 729 { 730 free(tk_text); 731 tk_text = NULL; 732 } 733 /* 734 * Find the original (uncompleted) word in the command buffer. 735 */ 736 word = delimit_word(); 737 if (word == NULL) 738 return; 739 /* 740 * Set the insertion point to the point in the command buffer 741 * where the original (uncompleted) word now sits. 742 */ 743 tk_ipoint = word; 744 /* 745 * Save the original (uncompleted) word 746 */ 747 if (tk_original != NULL) 748 free(tk_original); 749 tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 750 strncpy(tk_original, word, cp-word); 751 /* 752 * Get the expanded filename. 753 * This may result in a single filename, or 754 * a blank-separated list of filenames. 755 */ 756 c = *cp; 757 *cp = '\0'; 758 tk_text = fcomplete(word); 759 *cp = c; 760 } 761 762 /* 763 * Return the next word in the current completion list. 764 */ 765 static char * 766 next_compl(action, prev) 767 int action; 768 char *prev; 769 { 770 switch (action) 771 { 772 case EC_F_COMPLETE: 773 return (forw_textlist(&tk_tlist, prev)); 774 case EC_B_COMPLETE: 775 return (back_textlist(&tk_tlist, prev)); 776 default: 777 /* Cannot happen */ 778 return ("?"); 779 } 780 } 781 782 /* 783 * Complete the filename before (or under) the cursor. 784 * cmd_complete may be called multiple times. The global in_completion 785 * remembers whether this call is the first time (create the list), 786 * or a subsequent time (step thru the list). 787 */ 788 static int 789 cmd_complete(action) 790 int action; 791 { 792 793 if (!in_completion || action == EC_EXPAND) 794 { 795 /* 796 * Expand the word under the cursor and 797 * use the first word in the expansion 798 * (or the entire expansion if we're doing EC_EXPAND). 799 */ 800 init_compl(); 801 if (tk_text == NULL) 802 { 803 bell(); 804 return (CC_OK); 805 } 806 if (action == EC_EXPAND) 807 { 808 /* 809 * Use the whole list. 810 */ 811 tk_trial = tk_text; 812 } else 813 { 814 /* 815 * Use the first filename in the list. 816 */ 817 in_completion = 1; 818 init_textlist(&tk_tlist, tk_text); 819 tk_trial = next_compl(action, (char*)NULL); 820 } 821 } else 822 { 823 /* 824 * We already have a completion list. 825 * Use the next/previous filename from the list. 826 */ 827 tk_trial = next_compl(action, tk_trial); 828 } 829 830 /* 831 * Remove the original word, or the previous trial completion. 832 */ 833 while (cp > tk_ipoint) 834 (void) cmd_erase(); 835 836 if (tk_trial == NULL) 837 { 838 /* 839 * There are no more trial completions. 840 * Insert the original (uncompleted) filename. 841 */ 842 in_completion = 0; 843 if (cmd_istr(tk_original) != CC_OK) 844 goto fail; 845 } else 846 { 847 /* 848 * Insert trial completion. 849 */ 850 if (cmd_istr(tk_trial) != CC_OK) 851 goto fail; 852 } 853 854 return (CC_OK); 855 856 fail: 857 in_completion = 0; 858 bell(); 859 return (CC_OK); 860 } 861 862 #endif /* TAB_COMPLETE_FILENAME */ 863 864 /* 865 * Process a single character of a multi-character command, such as 866 * a number, or the pattern of a search command. 867 * Returns: 868 * CC_OK The char was accepted. 869 * CC_QUIT The char requests the command to be aborted. 870 * CC_ERROR The char could not be accepted due to an error. 871 */ 872 public int 873 cmd_char(c) 874 int c; 875 { 876 int action; 877 878 if (literal) 879 { 880 /* 881 * Insert the char, even if it is a line-editing char. 882 */ 883 literal = 0; 884 return (cmd_ichar(c)); 885 } 886 887 /* 888 * See if it is a special line-editing character. 889 */ 890 if (in_mca()) 891 { 892 action = cmd_edit(c); 893 switch (action) 894 { 895 case CC_OK: 896 case CC_QUIT: 897 return (action); 898 case CC_PASS: 899 break; 900 } 901 } 902 903 /* 904 * Insert the char into the command buffer. 905 */ 906 action = cmd_ichar(c); 907 if (action != CC_OK) 908 return (action); 909 return (CC_OK); 910 } 911 912 /* 913 * Return the number currently in the command buffer. 914 */ 915 public int 916 cmd_int() 917 { 918 return (atoi(cmdbuf)); 919 } 920 921 /* 922 * Display a string, usually as a prompt for input into the command buffer. 923 */ 924 public void 925 cmd_putstr(s) 926 char *s; 927 { 928 putstr(s); 929 cmd_col += strlen(s); 930 } 931 932 /* 933 * Return a pointer to the command buffer. 934 */ 935 public char * 936 get_cmdbuf() 937 { 938 return (cmdbuf); 939 } 940