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