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