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