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