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