1 /* $NetBSD: cmdbuf.c,v 1.4 2013/09/04 19:44:21 tron Exp $ */ 2 3 /* 4 * Copyright (C) 1984-2012 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 * Functions which manipulate the command buffer. 15 * Used only by command() and related functions. 16 */ 17 18 #include "less.h" 19 #include "cmd.h" 20 #include "charset.h" 21 #if HAVE_STAT 22 #include <sys/stat.h> 23 #endif 24 #if HAVE_ERRNO_H 25 #include <errno.h> 26 #endif 27 28 extern int sc_width; 29 extern int utf_mode; 30 31 static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ 32 static int cmd_col; /* Current column of the cursor */ 33 static int prompt_col; /* Column of cursor just after prompt */ 34 static char *cp; /* Pointer into cmdbuf */ 35 static int cmd_offset; /* Index into cmdbuf of first displayed char */ 36 static int literal; /* Next input char should not be interpreted */ 37 static int updown_match = -1; /* Prefix length in up/down movement */ 38 39 #if TAB_COMPLETE_FILENAME 40 static int cmd_complete __P((int)); 41 /* 42 * These variables are statics used by cmd_complete. 43 */ 44 static int in_completion = 0; 45 static char *tk_text; 46 static char *tk_original; 47 static char *tk_ipoint; 48 static char *tk_trial; 49 static struct textlist tk_tlist; 50 #endif 51 52 static void cmd_repaint __P((char *)); 53 static void cmd_home __P((void)); 54 static void cmd_lshift __P((void)); 55 static void cmd_rshift __P((void)); 56 static int cmd_right __P((void)); 57 static int cmd_left __P((void)); 58 static int cmd_ichar __P((char *, int)); 59 static int cmd_erase __P((void)); 60 static int cmd_delete __P((void)); 61 static int cmd_werase __P((void)); 62 static int cmd_wdelete __P((void)); 63 static int cmd_kill __P((void)); 64 static int cmd_updown __P((int)); 65 static int cmd_edit __P((int)); 66 static int cmd_istr __P((char *)); 67 static char *delimit_word __P((void)); 68 static void init_compl __P((void)); 69 static char *next_compl __P((int, char *)); 70 71 #if SPACES_IN_FILENAMES 72 public char openquote = '"'; 73 public char closequote = '"'; 74 #endif 75 76 #if CMD_HISTORY 77 78 /* History file */ 79 #define HISTFILE_FIRST_LINE ".less-history-file:" 80 #define HISTFILE_SEARCH_SECTION ".search" 81 #define HISTFILE_SHELL_SECTION ".shell" 82 83 /* 84 * A mlist structure represents a command history. 85 */ 86 struct mlist 87 { 88 struct mlist *next; 89 struct mlist *prev; 90 struct mlist *curr_mp; 91 char *string; 92 int modified; 93 }; 94 95 /* 96 * These are the various command histories that exist. 97 */ 98 struct mlist mlist_search = 99 { &mlist_search, &mlist_search, &mlist_search, NULL, 0 }; 100 public void * constant ml_search = (void *) &mlist_search; 101 102 struct mlist mlist_examine = 103 { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; 104 public void * constant ml_examine = (void *) &mlist_examine; 105 106 #if SHELL_ESCAPE || PIPEC 107 struct mlist mlist_shell = 108 { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; 109 public void * constant ml_shell = (void *) &mlist_shell; 110 #endif 111 112 #else /* CMD_HISTORY */ 113 114 /* If CMD_HISTORY is off, these are just flags. */ 115 public void * constant ml_search = (void *)1; 116 public void * constant ml_examine = (void *)2; 117 #if SHELL_ESCAPE || PIPEC 118 public void * constant ml_shell = (void *)3; 119 #endif 120 121 #endif /* CMD_HISTORY */ 122 123 /* 124 * History for the current command. 125 */ 126 static struct mlist *curr_mlist = NULL; 127 static int curr_cmdflags; 128 129 static char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; 130 static int cmd_mbc_buf_len; 131 static int cmd_mbc_buf_index; 132 133 134 /* 135 * Reset command buffer (to empty). 136 */ 137 public void 138 cmd_reset() 139 { 140 cp = cmdbuf; 141 *cp = '\0'; 142 cmd_col = 0; 143 cmd_offset = 0; 144 literal = 0; 145 cmd_mbc_buf_len = 0; 146 updown_match = -1; 147 } 148 149 /* 150 * Clear command line. 151 */ 152 public void 153 clear_cmd() 154 { 155 cmd_col = prompt_col = 0; 156 cmd_mbc_buf_len = 0; 157 updown_match = -1; 158 } 159 160 /* 161 * Display a string, usually as a prompt for input into the command buffer. 162 */ 163 public void 164 cmd_putstr(s) 165 char *s; 166 { 167 LWCHAR prev_ch = 0; 168 LWCHAR ch; 169 char *endline = s + strlen(s); 170 while (*s != '\0') 171 { 172 char *ns = s; 173 ch = step_char(&ns, +1, endline); 174 while (s < ns) 175 putchr(*s++); 176 if (!utf_mode) 177 { 178 cmd_col++; 179 prompt_col++; 180 } else if (!is_composing_char(ch) && 181 !is_combining_char(prev_ch, ch)) 182 { 183 int width = is_wide_char(ch) ? 2 : 1; 184 cmd_col += width; 185 prompt_col += width; 186 } 187 prev_ch = ch; 188 } 189 } 190 191 /* 192 * How many characters are in the command buffer? 193 */ 194 public int 195 len_cmdbuf() 196 { 197 char *s = cmdbuf; 198 char *endline = s + strlen(s); 199 int len = 0; 200 201 while (*s != '\0') 202 { 203 step_char(&s, +1, endline); 204 len++; 205 } 206 return (len); 207 } 208 209 /* 210 * Common part of cmd_step_right() and cmd_step_left(). 211 */ 212 static char * 213 cmd_step_common(p, ch, len, pwidth, bswidth) 214 char *p; 215 LWCHAR ch; 216 int len; 217 int *pwidth; 218 int *bswidth; 219 { 220 char *pr; 221 222 if (len == 1) 223 { 224 pr = prchar((int) ch); 225 if (pwidth != NULL || bswidth != NULL) 226 { 227 int len = strlen(pr); 228 if (pwidth != NULL) 229 *pwidth = len; 230 if (bswidth != NULL) 231 *bswidth = len; 232 } 233 } else 234 { 235 pr = prutfchar(ch); 236 if (pwidth != NULL || bswidth != NULL) 237 { 238 if (is_composing_char(ch)) 239 { 240 if (pwidth != NULL) 241 *pwidth = 0; 242 if (bswidth != NULL) 243 *bswidth = 0; 244 } else if (is_ubin_char(ch)) 245 { 246 int len = strlen(pr); 247 if (pwidth != NULL) 248 *pwidth = len; 249 if (bswidth != NULL) 250 *bswidth = len; 251 } else 252 { 253 LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 254 if (is_combining_char(prev_ch, ch)) 255 { 256 if (pwidth != NULL) 257 *pwidth = 0; 258 if (bswidth != NULL) 259 *bswidth = 0; 260 } else 261 { 262 if (pwidth != NULL) 263 *pwidth = is_wide_char(ch) 264 ? 2 265 : 1; 266 if (bswidth != NULL) 267 *bswidth = 1; 268 } 269 } 270 } 271 } 272 273 return (pr); 274 } 275 276 /* 277 * Step a pointer one character right in the command buffer. 278 */ 279 static char * 280 cmd_step_right(pp, pwidth, bswidth) 281 char **pp; 282 int *pwidth; 283 int *bswidth; 284 { 285 char *p = *pp; 286 LWCHAR ch = step_char(pp, +1, p + strlen(p)); 287 288 return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); 289 } 290 291 /* 292 * Step a pointer one character left in the command buffer. 293 */ 294 static char * 295 cmd_step_left(pp, pwidth, bswidth) 296 char **pp; 297 int *pwidth; 298 int *bswidth; 299 { 300 char *p = *pp; 301 LWCHAR ch = step_char(pp, -1, cmdbuf); 302 303 return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); 304 } 305 306 /* 307 * Repaint the line from cp onwards. 308 * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 309 */ 310 static void 311 cmd_repaint(old_cp) 312 char *old_cp; 313 { 314 /* 315 * Repaint the line from the current position. 316 */ 317 clear_eol(); 318 while (*cp != '\0') 319 { 320 char *np = cp; 321 int width; 322 char *pr = cmd_step_right(&np, &width, NULL); 323 if (cmd_col + width >= sc_width) 324 break; 325 cp = np; 326 putstr(pr); 327 cmd_col += width; 328 } 329 while (*cp != '\0') 330 { 331 char *np = cp; 332 int width; 333 char *pr = cmd_step_right(&np, &width, NULL); 334 if (width > 0) 335 break; 336 cp = np; 337 putstr(pr); 338 } 339 340 /* 341 * Back up the cursor to the correct position. 342 */ 343 while (cp > old_cp) 344 cmd_left(); 345 } 346 347 /* 348 * Put the cursor at "home" (just after the prompt), 349 * and set cp to the corresponding char in cmdbuf. 350 */ 351 static void 352 cmd_home() 353 { 354 while (cmd_col > prompt_col) 355 { 356 int width, bswidth; 357 358 cmd_step_left(&cp, &width, &bswidth); 359 while (bswidth-- > 0) 360 putbs(); 361 cmd_col -= width; 362 } 363 364 cp = &cmdbuf[cmd_offset]; 365 } 366 367 /* 368 * Shift the cmdbuf display left a half-screen. 369 */ 370 static void 371 cmd_lshift() 372 { 373 char *s; 374 char *save_cp; 375 int cols; 376 377 /* 378 * Start at the first displayed char, count how far to the 379 * right we'd have to move to reach the center of the screen. 380 */ 381 s = cmdbuf + cmd_offset; 382 cols = 0; 383 while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 384 { 385 int width; 386 cmd_step_right(&s, &width, NULL); 387 cols += width; 388 } 389 while (*s != '\0') 390 { 391 int width; 392 char *ns = s; 393 cmd_step_right(&ns, &width, NULL); 394 if (width > 0) 395 break; 396 s = ns; 397 } 398 399 cmd_offset = s - cmdbuf; 400 save_cp = cp; 401 cmd_home(); 402 cmd_repaint(save_cp); 403 } 404 405 /* 406 * Shift the cmdbuf display right a half-screen. 407 */ 408 static void 409 cmd_rshift() 410 { 411 char *s; 412 char *save_cp; 413 int cols; 414 415 /* 416 * Start at the first displayed char, count how far to the 417 * left we'd have to move to traverse a half-screen width 418 * of displayed characters. 419 */ 420 s = cmdbuf + cmd_offset; 421 cols = 0; 422 while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 423 { 424 int width; 425 cmd_step_left(&s, &width, NULL); 426 cols += width; 427 } 428 429 cmd_offset = s - cmdbuf; 430 save_cp = cp; 431 cmd_home(); 432 cmd_repaint(save_cp); 433 } 434 435 /* 436 * Move cursor right one character. 437 */ 438 static int 439 cmd_right() 440 { 441 char *pr; 442 char *ncp; 443 int width; 444 445 if (*cp == '\0') 446 { 447 /* Already at the end of the line. */ 448 return (CC_OK); 449 } 450 ncp = cp; 451 pr = cmd_step_right(&ncp, &width, NULL); 452 if (cmd_col + width >= sc_width) 453 cmd_lshift(); 454 else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 455 cmd_lshift(); 456 cp = ncp; 457 cmd_col += width; 458 putstr(pr); 459 while (*cp != '\0') 460 { 461 pr = cmd_step_right(&ncp, &width, NULL); 462 if (width > 0) 463 break; 464 putstr(pr); 465 cp = ncp; 466 } 467 return (CC_OK); 468 } 469 470 /* 471 * Move cursor left one character. 472 */ 473 static int 474 cmd_left() 475 { 476 char *ncp; 477 int width, bswidth; 478 479 if (cp <= cmdbuf) 480 { 481 /* Already at the beginning of the line */ 482 return (CC_OK); 483 } 484 ncp = cp; 485 while (ncp > cmdbuf) 486 { 487 cmd_step_left(&ncp, &width, &bswidth); 488 if (width > 0) 489 break; 490 } 491 if (cmd_col < prompt_col + width) 492 cmd_rshift(); 493 cp = ncp; 494 cmd_col -= width; 495 while (bswidth-- > 0) 496 putbs(); 497 return (CC_OK); 498 } 499 500 /* 501 * Insert a char into the command buffer, at the current position. 502 */ 503 static int 504 cmd_ichar(cs, clen) 505 char *cs; 506 int clen; 507 { 508 char *s; 509 510 if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 511 { 512 /* No room in the command buffer for another char. */ 513 bell(); 514 return (CC_ERROR); 515 } 516 517 /* 518 * Make room for the new character (shift the tail of the buffer right). 519 */ 520 for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 521 s[clen] = s[0]; 522 /* 523 * Insert the character into the buffer. 524 */ 525 for (s = cp; s < cp + clen; s++) 526 *s = *cs++; 527 /* 528 * Reprint the tail of the line from the inserted char. 529 */ 530 updown_match = -1; 531 cmd_repaint(cp); 532 cmd_right(); 533 return (CC_OK); 534 } 535 536 /* 537 * Backspace in the command buffer. 538 * Delete the char to the left of the cursor. 539 */ 540 static int 541 cmd_erase() 542 { 543 register char *s; 544 int clen; 545 546 if (cp == cmdbuf) 547 { 548 /* 549 * Backspace past beginning of the buffer: 550 * this usually means abort the command. 551 */ 552 return (CC_QUIT); 553 } 554 /* 555 * Move cursor left (to the char being erased). 556 */ 557 s = cp; 558 cmd_left(); 559 clen = s - cp; 560 561 /* 562 * Remove the char from the buffer (shift the buffer left). 563 */ 564 for (s = cp; ; s++) 565 { 566 s[0] = s[clen]; 567 if (s[0] == '\0') 568 break; 569 } 570 571 /* 572 * Repaint the buffer after the erased char. 573 */ 574 updown_match = -1; 575 cmd_repaint(cp); 576 577 /* 578 * We say that erasing the entire command string causes us 579 * to abort the current command, if CF_QUIT_ON_ERASE is set. 580 */ 581 if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 582 return (CC_QUIT); 583 return (CC_OK); 584 } 585 586 /* 587 * Delete the char under the cursor. 588 */ 589 static int 590 cmd_delete() 591 { 592 if (*cp == '\0') 593 { 594 /* At end of string; there is no char under the cursor. */ 595 return (CC_OK); 596 } 597 /* 598 * Move right, then use cmd_erase. 599 */ 600 cmd_right(); 601 cmd_erase(); 602 return (CC_OK); 603 } 604 605 /* 606 * Delete the "word" to the left of the cursor. 607 */ 608 static int 609 cmd_werase() 610 { 611 if (cp > cmdbuf && cp[-1] == ' ') 612 { 613 /* 614 * If the char left of cursor is a space, 615 * erase all the spaces left of cursor (to the first non-space). 616 */ 617 while (cp > cmdbuf && cp[-1] == ' ') 618 (void) cmd_erase(); 619 } else 620 { 621 /* 622 * If the char left of cursor is not a space, 623 * erase all the nonspaces left of cursor (the whole "word"). 624 */ 625 while (cp > cmdbuf && cp[-1] != ' ') 626 (void) cmd_erase(); 627 } 628 return (CC_OK); 629 } 630 631 /* 632 * Delete the "word" under the cursor. 633 */ 634 static int 635 cmd_wdelete() 636 { 637 if (*cp == ' ') 638 { 639 /* 640 * If the char under the cursor is a space, 641 * delete it and all the spaces right of cursor. 642 */ 643 while (*cp == ' ') 644 (void) cmd_delete(); 645 } else 646 { 647 /* 648 * If the char under the cursor is not a space, 649 * delete it and all nonspaces right of cursor (the whole word). 650 */ 651 while (*cp != ' ' && *cp != '\0') 652 (void) cmd_delete(); 653 } 654 return (CC_OK); 655 } 656 657 /* 658 * Delete all chars in the command buffer. 659 */ 660 static int 661 cmd_kill() 662 { 663 if (cmdbuf[0] == '\0') 664 { 665 /* Buffer is already empty; abort the current command. */ 666 return (CC_QUIT); 667 } 668 cmd_offset = 0; 669 cmd_home(); 670 *cp = '\0'; 671 updown_match = -1; 672 cmd_repaint(cp); 673 674 /* 675 * We say that erasing the entire command string causes us 676 * to abort the current command, if CF_QUIT_ON_ERASE is set. 677 */ 678 if (curr_cmdflags & CF_QUIT_ON_ERASE) 679 return (CC_QUIT); 680 return (CC_OK); 681 } 682 683 /* 684 * Select an mlist structure to be the current command history. 685 */ 686 public void 687 set_mlist(mlist, cmdflags) 688 void *mlist; 689 int cmdflags; 690 { 691 #if CMD_HISTORY 692 curr_mlist = (struct mlist *) mlist; 693 curr_cmdflags = cmdflags; 694 695 /* Make sure the next up-arrow moves to the last string in the mlist. */ 696 if (curr_mlist != NULL) 697 curr_mlist->curr_mp = curr_mlist; 698 #endif 699 } 700 701 #if CMD_HISTORY 702 /* 703 * Move up or down in the currently selected command history list. 704 * Only consider entries whose first updown_match chars are equal to 705 * cmdbuf's corresponding chars. 706 */ 707 static int 708 cmd_updown(action) 709 int action; 710 { 711 char *s; 712 struct mlist *ml; 713 714 if (curr_mlist == NULL) 715 { 716 /* 717 * The current command has no history list. 718 */ 719 bell(); 720 return (CC_OK); 721 } 722 723 if (updown_match < 0) 724 { 725 updown_match = cp - cmdbuf; 726 } 727 728 /* 729 * Find the next history entry which matches. 730 */ 731 for (ml = curr_mlist->curr_mp;;) 732 { 733 ml = (action == EC_UP) ? ml->prev : ml->next; 734 if (ml == curr_mlist) 735 { 736 /* 737 * We reached the end (or beginning) of the list. 738 */ 739 break; 740 } 741 if (strncmp(cmdbuf, ml->string, updown_match) == 0) 742 { 743 /* 744 * This entry matches; stop here. 745 * Copy the entry into cmdbuf and echo it on the screen. 746 */ 747 curr_mlist->curr_mp = ml; 748 s = ml->string; 749 if (s == NULL) 750 s = ""; 751 cmd_home(); 752 clear_eol(); 753 strcpy(cmdbuf, s); 754 for (cp = cmdbuf; *cp != '\0'; ) 755 cmd_right(); 756 return (CC_OK); 757 } 758 } 759 /* 760 * We didn't find a history entry that matches. 761 */ 762 bell(); 763 return (CC_OK); 764 } 765 #endif 766 767 /* 768 * Add a string to a history list. 769 */ 770 public void 771 cmd_addhist(mlist, cmd) 772 struct mlist *mlist; 773 char *cmd; 774 { 775 #if CMD_HISTORY 776 struct mlist *ml; 777 778 /* 779 * Don't save a trivial command. 780 */ 781 if (strlen(cmd) == 0) 782 return; 783 784 /* 785 * Save the command unless it's a duplicate of the 786 * last command in the history. 787 */ 788 ml = mlist->prev; 789 if (ml == mlist || strcmp(ml->string, cmd) != 0) 790 { 791 /* 792 * Did not find command in history. 793 * Save the command and put it at the end of the history list. 794 */ 795 ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 796 ml->string = save(cmd); 797 ml->next = mlist; 798 ml->prev = mlist->prev; 799 mlist->prev->next = ml; 800 mlist->prev = ml; 801 } 802 /* 803 * Point to the cmd just after the just-accepted command. 804 * Thus, an UPARROW will always retrieve the previous command. 805 */ 806 mlist->curr_mp = ml->next; 807 #endif 808 } 809 810 /* 811 * Accept the command in the command buffer. 812 * Add it to the currently selected history list. 813 */ 814 public void 815 cmd_accept() 816 { 817 #if CMD_HISTORY 818 /* 819 * Nothing to do if there is no currently selected history list. 820 */ 821 if (curr_mlist == NULL) 822 return; 823 cmd_addhist(curr_mlist, cmdbuf); 824 curr_mlist->modified = 1; 825 #endif 826 } 827 828 /* 829 * Try to perform a line-edit function on the command buffer, 830 * using a specified char as a line-editing command. 831 * Returns: 832 * CC_PASS The char does not invoke a line edit function. 833 * CC_OK Line edit function done. 834 * CC_QUIT The char requests the current command to be aborted. 835 */ 836 static int 837 cmd_edit(c) 838 int c; 839 { 840 int action; 841 int flags; 842 843 #if TAB_COMPLETE_FILENAME 844 #define not_in_completion() in_completion = 0 845 #else 846 #define not_in_completion() 847 #endif 848 849 /* 850 * See if the char is indeed a line-editing command. 851 */ 852 flags = 0; 853 #if CMD_HISTORY 854 if (curr_mlist == NULL) 855 /* 856 * No current history; don't accept history manipulation cmds. 857 */ 858 flags |= EC_NOHISTORY; 859 #endif 860 #if TAB_COMPLETE_FILENAME 861 if (curr_mlist == ml_search) 862 /* 863 * In a search command; don't accept file-completion cmds. 864 */ 865 flags |= EC_NOCOMPLETE; 866 #endif 867 868 action = editchar(c, flags); 869 870 switch (action) 871 { 872 case EC_RIGHT: 873 not_in_completion(); 874 return (cmd_right()); 875 case EC_LEFT: 876 not_in_completion(); 877 return (cmd_left()); 878 case EC_W_RIGHT: 879 not_in_completion(); 880 while (*cp != '\0' && *cp != ' ') 881 cmd_right(); 882 while (*cp == ' ') 883 cmd_right(); 884 return (CC_OK); 885 case EC_W_LEFT: 886 not_in_completion(); 887 while (cp > cmdbuf && cp[-1] == ' ') 888 cmd_left(); 889 while (cp > cmdbuf && cp[-1] != ' ') 890 cmd_left(); 891 return (CC_OK); 892 case EC_HOME: 893 not_in_completion(); 894 cmd_offset = 0; 895 cmd_home(); 896 cmd_repaint(cp); 897 return (CC_OK); 898 case EC_END: 899 not_in_completion(); 900 while (*cp != '\0') 901 cmd_right(); 902 return (CC_OK); 903 case EC_INSERT: 904 not_in_completion(); 905 return (CC_OK); 906 case EC_BACKSPACE: 907 not_in_completion(); 908 return (cmd_erase()); 909 case EC_LINEKILL: 910 not_in_completion(); 911 return (cmd_kill()); 912 case EC_ABORT: 913 not_in_completion(); 914 (void) cmd_kill(); 915 return (CC_QUIT); 916 case EC_W_BACKSPACE: 917 not_in_completion(); 918 return (cmd_werase()); 919 case EC_DELETE: 920 not_in_completion(); 921 return (cmd_delete()); 922 case EC_W_DELETE: 923 not_in_completion(); 924 return (cmd_wdelete()); 925 case EC_LITERAL: 926 literal = 1; 927 return (CC_OK); 928 #if CMD_HISTORY 929 case EC_UP: 930 case EC_DOWN: 931 not_in_completion(); 932 return (cmd_updown(action)); 933 #endif 934 #if TAB_COMPLETE_FILENAME 935 case EC_F_COMPLETE: 936 case EC_B_COMPLETE: 937 case EC_EXPAND: 938 return (cmd_complete(action)); 939 #endif 940 case EC_NOACTION: 941 return (CC_OK); 942 default: 943 not_in_completion(); 944 return (CC_PASS); 945 } 946 } 947 948 #if TAB_COMPLETE_FILENAME 949 /* 950 * Insert a string into the command buffer, at the current position. 951 */ 952 static int 953 cmd_istr(str) 954 char *str; 955 { 956 char *s; 957 int action; 958 char *endline = str + strlen(str); 959 960 for (s = str; *s != '\0'; ) 961 { 962 char *os = s; 963 step_char(&s, +1, endline); 964 action = cmd_ichar(os, s - os); 965 if (action != CC_OK) 966 { 967 bell(); 968 return (action); 969 } 970 } 971 return (CC_OK); 972 } 973 974 /* 975 * Find the beginning and end of the "current" word. 976 * This is the word which the cursor (cp) is inside or at the end of. 977 * Return pointer to the beginning of the word and put the 978 * cursor at the end of the word. 979 */ 980 static char * 981 delimit_word() 982 { 983 char *word = NULL; 984 #if SPACES_IN_FILENAMES 985 char *p; 986 int delim_quoted = 0; 987 int meta_quoted = 0; 988 char *esc = get_meta_escape(); 989 int esclen = strlen(esc); 990 #endif 991 992 /* 993 * Move cursor to end of word. 994 */ 995 if (*cp != ' ' && *cp != '\0') 996 { 997 /* 998 * Cursor is on a nonspace. 999 * Move cursor right to the next space. 1000 */ 1001 while (*cp != ' ' && *cp != '\0') 1002 cmd_right(); 1003 } else if (cp > cmdbuf && cp[-1] != ' ') 1004 { 1005 /* 1006 * Cursor is on a space, and char to the left is a nonspace. 1007 * We're already at the end of the word. 1008 */ 1009 ; 1010 #if 0 1011 } else 1012 { 1013 /* 1014 * Cursor is on a space and char to the left is a space. 1015 * Huh? There's no word here. 1016 */ 1017 return (NULL); 1018 #endif 1019 } 1020 /* 1021 * Find the beginning of the word which the cursor is in. 1022 */ 1023 if (cp == cmdbuf) 1024 return (NULL); 1025 #if SPACES_IN_FILENAMES 1026 /* 1027 * If we have an unbalanced quote (that is, an open quote 1028 * without a corresponding close quote), we return everything 1029 * from the open quote, including spaces. 1030 */ 1031 for (word = cmdbuf; word < cp; word++) 1032 if (*word != ' ') 1033 break; 1034 if (word >= cp) 1035 return (cp); 1036 for (p = cmdbuf; p < cp; p++) 1037 { 1038 if (meta_quoted) 1039 { 1040 meta_quoted = 0; 1041 } else if (esclen > 0 && p + esclen < cp && 1042 strncmp(p, esc, esclen) == 0) 1043 { 1044 meta_quoted = 1; 1045 p += esclen - 1; 1046 } else if (delim_quoted) 1047 { 1048 if (*p == closequote) 1049 delim_quoted = 0; 1050 } else /* (!delim_quoted) */ 1051 { 1052 if (*p == openquote) 1053 delim_quoted = 1; 1054 else if (*p == ' ') 1055 word = p+1; 1056 } 1057 } 1058 #endif 1059 return (word); 1060 } 1061 1062 /* 1063 * Set things up to enter completion mode. 1064 * Expand the word under the cursor into a list of filenames 1065 * which start with that word, and set tk_text to that list. 1066 */ 1067 static void 1068 init_compl() 1069 { 1070 char *word; 1071 char c; 1072 1073 /* 1074 * Get rid of any previous tk_text. 1075 */ 1076 if (tk_text != NULL) 1077 { 1078 free(tk_text); 1079 tk_text = NULL; 1080 } 1081 /* 1082 * Find the original (uncompleted) word in the command buffer. 1083 */ 1084 word = delimit_word(); 1085 if (word == NULL) 1086 return; 1087 /* 1088 * Set the insertion point to the point in the command buffer 1089 * where the original (uncompleted) word now sits. 1090 */ 1091 tk_ipoint = word; 1092 /* 1093 * Save the original (uncompleted) word 1094 */ 1095 if (tk_original != NULL) 1096 free(tk_original); 1097 tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 1098 strncpy(tk_original, word, cp-word); 1099 /* 1100 * Get the expanded filename. 1101 * This may result in a single filename, or 1102 * a blank-separated list of filenames. 1103 */ 1104 c = *cp; 1105 *cp = '\0'; 1106 if (*word != openquote) 1107 { 1108 tk_text = fcomplete(word); 1109 } else 1110 { 1111 #if MSDOS_COMPILER 1112 char *qword = NULL; 1113 #else 1114 char *qword = shell_quote(word+1); 1115 #endif 1116 if (qword == NULL) 1117 tk_text = fcomplete(word+1); 1118 else 1119 { 1120 tk_text = fcomplete(qword); 1121 free(qword); 1122 } 1123 } 1124 *cp = c; 1125 } 1126 1127 /* 1128 * Return the next word in the current completion list. 1129 */ 1130 static char * 1131 next_compl(action, prev) 1132 int action; 1133 char *prev; 1134 { 1135 switch (action) 1136 { 1137 case EC_F_COMPLETE: 1138 return (forw_textlist(&tk_tlist, prev)); 1139 case EC_B_COMPLETE: 1140 return (back_textlist(&tk_tlist, prev)); 1141 } 1142 /* Cannot happen */ 1143 return ("?"); 1144 } 1145 1146 /* 1147 * Complete the filename before (or under) the cursor. 1148 * cmd_complete may be called multiple times. The global in_completion 1149 * remembers whether this call is the first time (create the list), 1150 * or a subsequent time (step thru the list). 1151 */ 1152 static int 1153 cmd_complete(action) 1154 int action; 1155 { 1156 char *s; 1157 1158 if (!in_completion || action == EC_EXPAND) 1159 { 1160 /* 1161 * Expand the word under the cursor and 1162 * use the first word in the expansion 1163 * (or the entire expansion if we're doing EC_EXPAND). 1164 */ 1165 init_compl(); 1166 if (tk_text == NULL) 1167 { 1168 bell(); 1169 return (CC_OK); 1170 } 1171 if (action == EC_EXPAND) 1172 { 1173 /* 1174 * Use the whole list. 1175 */ 1176 tk_trial = tk_text; 1177 } else 1178 { 1179 /* 1180 * Use the first filename in the list. 1181 */ 1182 in_completion = 1; 1183 init_textlist(&tk_tlist, tk_text); 1184 tk_trial = next_compl(action, (char*)NULL); 1185 } 1186 } else 1187 { 1188 /* 1189 * We already have a completion list. 1190 * Use the next/previous filename from the list. 1191 */ 1192 tk_trial = next_compl(action, tk_trial); 1193 } 1194 1195 /* 1196 * Remove the original word, or the previous trial completion. 1197 */ 1198 while (cp > tk_ipoint) 1199 (void) cmd_erase(); 1200 1201 if (tk_trial == NULL) 1202 { 1203 /* 1204 * There are no more trial completions. 1205 * Insert the original (uncompleted) filename. 1206 */ 1207 in_completion = 0; 1208 if (cmd_istr(tk_original) != CC_OK) 1209 goto fail; 1210 } else 1211 { 1212 /* 1213 * Insert trial completion. 1214 */ 1215 if (cmd_istr(tk_trial) != CC_OK) 1216 goto fail; 1217 /* 1218 * If it is a directory, append a slash. 1219 */ 1220 if (is_dir(tk_trial)) 1221 { 1222 if (cp > cmdbuf && cp[-1] == closequote) 1223 (void) cmd_erase(); 1224 s = lgetenv("LESSSEPARATOR"); 1225 if (s == NULL) 1226 s = PATHNAME_SEP; 1227 if (cmd_istr(s) != CC_OK) 1228 goto fail; 1229 } 1230 } 1231 1232 return (CC_OK); 1233 1234 fail: 1235 in_completion = 0; 1236 bell(); 1237 return (CC_OK); 1238 } 1239 1240 #endif /* TAB_COMPLETE_FILENAME */ 1241 1242 /* 1243 * Process a single character of a multi-character command, such as 1244 * a number, or the pattern of a search command. 1245 * Returns: 1246 * CC_OK The char was accepted. 1247 * CC_QUIT The char requests the command to be aborted. 1248 * CC_ERROR The char could not be accepted due to an error. 1249 */ 1250 public int 1251 cmd_char(c) 1252 int c; 1253 { 1254 int action; 1255 int len; 1256 1257 if (!utf_mode) 1258 { 1259 cmd_mbc_buf[0] = c; 1260 len = 1; 1261 } else 1262 { 1263 /* Perform strict validation in all possible cases. */ 1264 if (cmd_mbc_buf_len == 0) 1265 { 1266 retry: 1267 cmd_mbc_buf_index = 1; 1268 *cmd_mbc_buf = c; 1269 if (IS_ASCII_OCTET(c)) 1270 cmd_mbc_buf_len = 1; 1271 else if (IS_UTF8_LEAD(c)) 1272 { 1273 cmd_mbc_buf_len = utf_len(c); 1274 return (CC_OK); 1275 } else 1276 { 1277 /* UTF8_INVALID or stray UTF8_TRAIL */ 1278 bell(); 1279 return (CC_ERROR); 1280 } 1281 } else if (IS_UTF8_TRAIL(c)) 1282 { 1283 cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1284 if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1285 return (CC_OK); 1286 if (!is_utf8_well_formed(cmd_mbc_buf)) 1287 { 1288 /* complete, but not well formed (non-shortest form), sequence */ 1289 cmd_mbc_buf_len = 0; 1290 bell(); 1291 return (CC_ERROR); 1292 } 1293 } else 1294 { 1295 /* Flush incomplete (truncated) sequence. */ 1296 cmd_mbc_buf_len = 0; 1297 bell(); 1298 /* Handle new char. */ 1299 goto retry; 1300 } 1301 1302 len = cmd_mbc_buf_len; 1303 cmd_mbc_buf_len = 0; 1304 } 1305 1306 if (literal) 1307 { 1308 /* 1309 * Insert the char, even if it is a line-editing char. 1310 */ 1311 literal = 0; 1312 return (cmd_ichar(cmd_mbc_buf, len)); 1313 } 1314 1315 /* 1316 * See if it is a line-editing character. 1317 */ 1318 if (in_mca() && len == 1) 1319 { 1320 action = cmd_edit(c); 1321 switch (action) 1322 { 1323 case CC_OK: 1324 case CC_QUIT: 1325 return (action); 1326 case CC_PASS: 1327 break; 1328 } 1329 } 1330 1331 /* 1332 * Insert the char into the command buffer. 1333 */ 1334 return (cmd_ichar(cmd_mbc_buf, len)); 1335 } 1336 1337 /* 1338 * Return the number currently in the command buffer. 1339 */ 1340 public LINENUM 1341 cmd_int(frac) 1342 long *frac; 1343 { 1344 char *p; 1345 LINENUM n = 0; 1346 int err; 1347 1348 for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1349 n = (n * 10) + (*p - '0'); 1350 *frac = 0; 1351 if (*p++ == '.') 1352 { 1353 *frac = getfraction(&p, NULL, &err); 1354 /* {{ do something if err is set? }} */ 1355 } 1356 return (n); 1357 } 1358 1359 /* 1360 * Return a pointer to the command buffer. 1361 */ 1362 public char * 1363 get_cmdbuf() 1364 { 1365 return (cmdbuf); 1366 } 1367 1368 #if CMD_HISTORY 1369 /* 1370 * Return the last (most recent) string in the current command history. 1371 */ 1372 public char * 1373 cmd_lastpattern() 1374 { 1375 if (curr_mlist == NULL) 1376 return (NULL); 1377 return (curr_mlist->curr_mp->prev->string); 1378 } 1379 #endif 1380 1381 #if CMD_HISTORY 1382 /* 1383 * Get the name of the history file. 1384 */ 1385 static char * 1386 histfile_name() 1387 { 1388 char *home; 1389 char *name; 1390 int len; 1391 1392 /* See if filename is explicitly specified by $LESSHISTFILE. */ 1393 name = lgetenv("LESSHISTFILE"); 1394 if (name != NULL && *name != '\0') 1395 { 1396 if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1397 /* $LESSHISTFILE == "-" means don't use a history file. */ 1398 return (NULL); 1399 return (save(name)); 1400 } 1401 1402 /* Otherwise, file is in $HOME. */ 1403 home = lgetenv("HOME"); 1404 if (home == NULL || *home == '\0') 1405 { 1406 #if OS2 1407 home = lgetenv("INIT"); 1408 if (home == NULL || *home == '\0') 1409 #endif 1410 return (NULL); 1411 } 1412 len = strlen(home) + strlen(LESSHISTFILE) + 2; 1413 name = (char *) ecalloc(len, sizeof(char)); 1414 SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1415 return (name); 1416 } 1417 #endif /* CMD_HISTORY */ 1418 1419 /* 1420 * Initialize history from a .lesshist file. 1421 */ 1422 public void 1423 init_cmdhist() 1424 { 1425 #if CMD_HISTORY 1426 struct mlist *ml = NULL; 1427 char line[CMDBUF_SIZE]; 1428 char *filename; 1429 FILE *f; 1430 char *p; 1431 #ifdef HAVE_STAT 1432 struct stat st; 1433 #endif 1434 1435 filename = histfile_name(); 1436 if (filename == NULL) 1437 return; 1438 #ifdef HAVE_STAT 1439 /* ignore devices/fifos; allow symlinks */ 1440 if (stat(filename, &st) < 0) 1441 return; 1442 if (!S_ISREG(st.st_mode)) 1443 return; 1444 #endif 1445 f = fopen(filename, "r"); 1446 free(filename); 1447 if (f == NULL) 1448 return; 1449 if (fgets(line, sizeof(line), f) == NULL || 1450 strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1451 { 1452 fclose(f); 1453 return; 1454 } 1455 while (fgets(line, sizeof(line), f) != NULL) 1456 { 1457 for (p = line; *p != '\0'; p++) 1458 { 1459 if (*p == '\n' || *p == '\r') 1460 { 1461 *p = '\0'; 1462 break; 1463 } 1464 } 1465 if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1466 ml = &mlist_search; 1467 else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1468 { 1469 #if SHELL_ESCAPE || PIPEC 1470 ml = &mlist_shell; 1471 #else 1472 ml = NULL; 1473 #endif 1474 } else if (*line == '"') 1475 { 1476 if (ml != NULL) 1477 cmd_addhist(ml, line+1); 1478 } 1479 } 1480 fclose(f); 1481 #endif /* CMD_HISTORY */ 1482 } 1483 1484 /* 1485 * 1486 */ 1487 #if CMD_HISTORY 1488 static void 1489 save_mlist(ml, f) 1490 struct mlist *ml; 1491 FILE *f; 1492 { 1493 int histsize = 0; 1494 int n; 1495 char *s; 1496 1497 s = lgetenv("LESSHISTSIZE"); 1498 if (s != NULL) 1499 histsize = atoi(s); 1500 if (histsize == 0) 1501 histsize = 100; 1502 1503 ml = ml->prev; 1504 for (n = 0; n < histsize; n++) 1505 { 1506 if (ml->string == NULL) 1507 break; 1508 ml = ml->prev; 1509 } 1510 for (ml = ml->next; ml->string != NULL; ml = ml->next) 1511 fprintf(f, "\"%s\n", ml->string); 1512 } 1513 #endif /* CMD_HISTORY */ 1514 1515 /* 1516 * 1517 */ 1518 public void 1519 save_cmdhist() 1520 { 1521 #if CMD_HISTORY 1522 char *filename; 1523 FILE *f; 1524 int modified = 0; 1525 1526 if (mlist_search.modified) 1527 modified = 1; 1528 #if SHELL_ESCAPE || PIPEC 1529 if (mlist_shell.modified) 1530 modified = 1; 1531 #endif 1532 if (!modified) 1533 return; 1534 filename = histfile_name(); 1535 if (filename == NULL) 1536 return; 1537 f = fopen(filename, "w"); 1538 free(filename); 1539 if (f == NULL) 1540 return; 1541 #if HAVE_FCHMOD 1542 { 1543 /* Make history file readable only by owner. */ 1544 int do_chmod = 1; 1545 #if HAVE_STAT 1546 struct stat statbuf; 1547 int r = fstat(fileno(f), &statbuf); 1548 if (r < 0 || !S_ISREG(statbuf.st_mode)) 1549 /* Don't chmod if not a regular file. */ 1550 do_chmod = 0; 1551 #endif 1552 if (do_chmod) 1553 fchmod(fileno(f), 0600); 1554 } 1555 #endif 1556 1557 fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1558 1559 fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1560 save_mlist(&mlist_search, f); 1561 1562 #if SHELL_ESCAPE || PIPEC 1563 fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1564 save_mlist(&mlist_shell, f); 1565 #endif 1566 1567 fclose(f); 1568 #endif /* CMD_HISTORY */ 1569 } 1570