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