1 /* 2 * Copyright (C) 1984-2007 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 } 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 curr_mlist->modified = 1; 771 #endif 772 } 773 774 /* 775 * Try to perform a line-edit function on the command buffer, 776 * using a specified char as a line-editing command. 777 * Returns: 778 * CC_PASS The char does not invoke a line edit function. 779 * CC_OK Line edit function done. 780 * CC_QUIT The char requests the current command to be aborted. 781 */ 782 static int 783 cmd_edit(c) 784 int c; 785 { 786 int action; 787 int flags; 788 789 #if TAB_COMPLETE_FILENAME 790 #define not_in_completion() in_completion = 0 791 #else 792 #define not_in_completion() 793 #endif 794 795 /* 796 * See if the char is indeed a line-editing command. 797 */ 798 flags = 0; 799 #if CMD_HISTORY 800 if (curr_mlist == NULL) 801 /* 802 * No current history; don't accept history manipulation cmds. 803 */ 804 flags |= EC_NOHISTORY; 805 #endif 806 #if TAB_COMPLETE_FILENAME 807 if (curr_mlist == ml_search) 808 /* 809 * In a search command; don't accept file-completion cmds. 810 */ 811 flags |= EC_NOCOMPLETE; 812 #endif 813 814 action = editchar(c, flags); 815 816 switch (action) 817 { 818 case EC_RIGHT: 819 not_in_completion(); 820 return (cmd_right()); 821 case EC_LEFT: 822 not_in_completion(); 823 return (cmd_left()); 824 case EC_W_RIGHT: 825 not_in_completion(); 826 while (*cp != '\0' && *cp != ' ') 827 cmd_right(); 828 while (*cp == ' ') 829 cmd_right(); 830 return (CC_OK); 831 case EC_W_LEFT: 832 not_in_completion(); 833 while (cp > cmdbuf && cp[-1] == ' ') 834 cmd_left(); 835 while (cp > cmdbuf && cp[-1] != ' ') 836 cmd_left(); 837 return (CC_OK); 838 case EC_HOME: 839 not_in_completion(); 840 cmd_offset = 0; 841 cmd_home(); 842 cmd_repaint(cp); 843 return (CC_OK); 844 case EC_END: 845 not_in_completion(); 846 while (*cp != '\0') 847 cmd_right(); 848 return (CC_OK); 849 case EC_INSERT: 850 not_in_completion(); 851 return (CC_OK); 852 case EC_BACKSPACE: 853 not_in_completion(); 854 return (cmd_erase()); 855 case EC_LINEKILL: 856 not_in_completion(); 857 return (cmd_kill()); 858 case EC_W_BACKSPACE: 859 not_in_completion(); 860 return (cmd_werase()); 861 case EC_DELETE: 862 not_in_completion(); 863 return (cmd_delete()); 864 case EC_W_DELETE: 865 not_in_completion(); 866 return (cmd_wdelete()); 867 case EC_LITERAL: 868 literal = 1; 869 return (CC_OK); 870 #if CMD_HISTORY 871 case EC_UP: 872 case EC_DOWN: 873 not_in_completion(); 874 return (cmd_updown(action)); 875 #endif 876 #if TAB_COMPLETE_FILENAME 877 case EC_F_COMPLETE: 878 case EC_B_COMPLETE: 879 case EC_EXPAND: 880 return (cmd_complete(action)); 881 #endif 882 case EC_NOACTION: 883 return (CC_OK); 884 default: 885 not_in_completion(); 886 return (CC_PASS); 887 } 888 } 889 890 #if TAB_COMPLETE_FILENAME 891 /* 892 * Insert a string into the command buffer, at the current position. 893 */ 894 static int 895 cmd_istr(str) 896 char *str; 897 { 898 char *s; 899 int action; 900 char *endline = str + strlen(str); 901 902 for (s = str; *s != '\0'; ) 903 { 904 char *os = s; 905 step_char(&s, +1, endline); 906 action = cmd_ichar(os, s - os); 907 if (action != CC_OK) 908 { 909 bell(); 910 return (action); 911 } 912 } 913 return (CC_OK); 914 } 915 916 /* 917 * Find the beginning and end of the "current" word. 918 * This is the word which the cursor (cp) is inside or at the end of. 919 * Return pointer to the beginning of the word and put the 920 * cursor at the end of the word. 921 */ 922 static char * 923 delimit_word() 924 { 925 char *word; 926 #if SPACES_IN_FILENAMES 927 char *p; 928 int delim_quoted = 0; 929 int meta_quoted = 0; 930 char *esc = get_meta_escape(); 931 int esclen = strlen(esc); 932 #endif 933 934 /* 935 * Move cursor to end of word. 936 */ 937 if (*cp != ' ' && *cp != '\0') 938 { 939 /* 940 * Cursor is on a nonspace. 941 * Move cursor right to the next space. 942 */ 943 while (*cp != ' ' && *cp != '\0') 944 cmd_right(); 945 } else if (cp > cmdbuf && cp[-1] != ' ') 946 { 947 /* 948 * Cursor is on a space, and char to the left is a nonspace. 949 * We're already at the end of the word. 950 */ 951 ; 952 #if 0 953 } else 954 { 955 /* 956 * Cursor is on a space and char to the left is a space. 957 * Huh? There's no word here. 958 */ 959 return (NULL); 960 #endif 961 } 962 /* 963 * Find the beginning of the word which the cursor is in. 964 */ 965 if (cp == cmdbuf) 966 return (NULL); 967 #if SPACES_IN_FILENAMES 968 /* 969 * If we have an unbalanced quote (that is, an open quote 970 * without a corresponding close quote), we return everything 971 * from the open quote, including spaces. 972 */ 973 for (word = cmdbuf; word < cp; word++) 974 if (*word != ' ') 975 break; 976 if (word >= cp) 977 return (cp); 978 for (p = cmdbuf; p < cp; p++) 979 { 980 if (meta_quoted) 981 { 982 meta_quoted = 0; 983 } else if (esclen > 0 && p + esclen < cp && 984 strncmp(p, esc, esclen) == 0) 985 { 986 meta_quoted = 1; 987 p += esclen - 1; 988 } else if (delim_quoted) 989 { 990 if (*p == closequote) 991 delim_quoted = 0; 992 } else /* (!delim_quoted) */ 993 { 994 if (*p == openquote) 995 delim_quoted = 1; 996 else if (*p == ' ') 997 word = p+1; 998 } 999 } 1000 #endif 1001 return (word); 1002 } 1003 1004 /* 1005 * Set things up to enter completion mode. 1006 * Expand the word under the cursor into a list of filenames 1007 * which start with that word, and set tk_text to that list. 1008 */ 1009 static void 1010 init_compl() 1011 { 1012 char *word; 1013 char c; 1014 1015 /* 1016 * Get rid of any previous tk_text. 1017 */ 1018 if (tk_text != NULL) 1019 { 1020 free(tk_text); 1021 tk_text = NULL; 1022 } 1023 /* 1024 * Find the original (uncompleted) word in the command buffer. 1025 */ 1026 word = delimit_word(); 1027 if (word == NULL) 1028 return; 1029 /* 1030 * Set the insertion point to the point in the command buffer 1031 * where the original (uncompleted) word now sits. 1032 */ 1033 tk_ipoint = word; 1034 /* 1035 * Save the original (uncompleted) word 1036 */ 1037 if (tk_original != NULL) 1038 free(tk_original); 1039 tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 1040 strncpy(tk_original, word, cp-word); 1041 /* 1042 * Get the expanded filename. 1043 * This may result in a single filename, or 1044 * a blank-separated list of filenames. 1045 */ 1046 c = *cp; 1047 *cp = '\0'; 1048 if (*word != openquote) 1049 { 1050 tk_text = fcomplete(word); 1051 } else 1052 { 1053 char *qword = shell_quote(word+1); 1054 if (qword == NULL) 1055 tk_text = fcomplete(word+1); 1056 else 1057 { 1058 tk_text = fcomplete(qword); 1059 free(qword); 1060 } 1061 } 1062 *cp = c; 1063 } 1064 1065 /* 1066 * Return the next word in the current completion list. 1067 */ 1068 static char * 1069 next_compl(action, prev) 1070 int action; 1071 char *prev; 1072 { 1073 switch (action) 1074 { 1075 case EC_F_COMPLETE: 1076 return (forw_textlist(&tk_tlist, prev)); 1077 case EC_B_COMPLETE: 1078 return (back_textlist(&tk_tlist, prev)); 1079 } 1080 /* Cannot happen */ 1081 return ("?"); 1082 } 1083 1084 /* 1085 * Complete the filename before (or under) the cursor. 1086 * cmd_complete may be called multiple times. The global in_completion 1087 * remembers whether this call is the first time (create the list), 1088 * or a subsequent time (step thru the list). 1089 */ 1090 static int 1091 cmd_complete(action) 1092 int action; 1093 { 1094 char *s; 1095 1096 if (!in_completion || action == EC_EXPAND) 1097 { 1098 /* 1099 * Expand the word under the cursor and 1100 * use the first word in the expansion 1101 * (or the entire expansion if we're doing EC_EXPAND). 1102 */ 1103 init_compl(); 1104 if (tk_text == NULL) 1105 { 1106 bell(); 1107 return (CC_OK); 1108 } 1109 if (action == EC_EXPAND) 1110 { 1111 /* 1112 * Use the whole list. 1113 */ 1114 tk_trial = tk_text; 1115 } else 1116 { 1117 /* 1118 * Use the first filename in the list. 1119 */ 1120 in_completion = 1; 1121 init_textlist(&tk_tlist, tk_text); 1122 tk_trial = next_compl(action, (char*)NULL); 1123 } 1124 } else 1125 { 1126 /* 1127 * We already have a completion list. 1128 * Use the next/previous filename from the list. 1129 */ 1130 tk_trial = next_compl(action, tk_trial); 1131 } 1132 1133 /* 1134 * Remove the original word, or the previous trial completion. 1135 */ 1136 while (cp > tk_ipoint) 1137 (void) cmd_erase(); 1138 1139 if (tk_trial == NULL) 1140 { 1141 /* 1142 * There are no more trial completions. 1143 * Insert the original (uncompleted) filename. 1144 */ 1145 in_completion = 0; 1146 if (cmd_istr(tk_original) != CC_OK) 1147 goto fail; 1148 } else 1149 { 1150 /* 1151 * Insert trial completion. 1152 */ 1153 if (cmd_istr(tk_trial) != CC_OK) 1154 goto fail; 1155 /* 1156 * If it is a directory, append a slash. 1157 */ 1158 if (is_dir(tk_trial)) 1159 { 1160 if (cp > cmdbuf && cp[-1] == closequote) 1161 (void) cmd_erase(); 1162 s = lgetenv("LESSSEPARATOR"); 1163 if (s == NULL) 1164 s = PATHNAME_SEP; 1165 if (cmd_istr(s) != CC_OK) 1166 goto fail; 1167 } 1168 } 1169 1170 return (CC_OK); 1171 1172 fail: 1173 in_completion = 0; 1174 bell(); 1175 return (CC_OK); 1176 } 1177 1178 #endif /* TAB_COMPLETE_FILENAME */ 1179 1180 /* 1181 * Process a single character of a multi-character command, such as 1182 * a number, or the pattern of a search command. 1183 * Returns: 1184 * CC_OK The char was accepted. 1185 * CC_QUIT The char requests the command to be aborted. 1186 * CC_ERROR The char could not be accepted due to an error. 1187 */ 1188 public int 1189 cmd_char(c) 1190 int c; 1191 { 1192 int action; 1193 int len; 1194 1195 if (!utf_mode) 1196 { 1197 cmd_mbc_buf[0] = c; 1198 len = 1; 1199 } else 1200 { 1201 /* Perform strict validation in all possible cases. */ 1202 if (cmd_mbc_buf_len == 0) 1203 { 1204 retry: 1205 cmd_mbc_buf_index = 1; 1206 *cmd_mbc_buf = c; 1207 if (IS_ASCII_OCTET(c)) 1208 cmd_mbc_buf_len = 1; 1209 else if (IS_UTF8_LEAD(c)) 1210 { 1211 cmd_mbc_buf_len = utf_len(c); 1212 return (CC_OK); 1213 } else 1214 { 1215 /* UTF8_INVALID or stray UTF8_TRAIL */ 1216 bell(); 1217 return (CC_ERROR); 1218 } 1219 } else if (IS_UTF8_TRAIL(c)) 1220 { 1221 cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1222 if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1223 return (CC_OK); 1224 if (!is_utf8_well_formed(cmd_mbc_buf)) 1225 { 1226 /* complete, but not well formed (non-shortest form), sequence */ 1227 cmd_mbc_buf_len = 0; 1228 bell(); 1229 return (CC_ERROR); 1230 } 1231 } else 1232 { 1233 /* Flush incomplete (truncated) sequence. */ 1234 cmd_mbc_buf_len = 0; 1235 bell(); 1236 /* Handle new char. */ 1237 goto retry; 1238 } 1239 1240 len = cmd_mbc_buf_len; 1241 cmd_mbc_buf_len = 0; 1242 } 1243 1244 if (literal) 1245 { 1246 /* 1247 * Insert the char, even if it is a line-editing char. 1248 */ 1249 literal = 0; 1250 return (cmd_ichar(cmd_mbc_buf, len)); 1251 } 1252 1253 /* 1254 * See if it is a line-editing character. 1255 */ 1256 if (in_mca() && len == 1) 1257 { 1258 action = cmd_edit(c); 1259 switch (action) 1260 { 1261 case CC_OK: 1262 case CC_QUIT: 1263 return (action); 1264 case CC_PASS: 1265 break; 1266 } 1267 } 1268 1269 /* 1270 * Insert the char into the command buffer. 1271 */ 1272 return (cmd_ichar(cmd_mbc_buf, len)); 1273 } 1274 1275 /* 1276 * Return the number currently in the command buffer. 1277 */ 1278 public LINENUM 1279 cmd_int(frac) 1280 long *frac; 1281 { 1282 char *p; 1283 LINENUM n = 0; 1284 int err; 1285 1286 for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1287 n = (n * 10) + (*p - '0'); 1288 *frac = 0; 1289 if (*p++ == '.') 1290 { 1291 *frac = getfraction(&p, NULL, &err); 1292 /* {{ do something if err is set? }} */ 1293 } 1294 return (n); 1295 } 1296 1297 /* 1298 * Return a pointer to the command buffer. 1299 */ 1300 public char * 1301 get_cmdbuf() 1302 { 1303 return (cmdbuf); 1304 } 1305 1306 /* 1307 * Return the last (most recent) string in the current command history. 1308 */ 1309 public char * 1310 cmd_lastpattern() 1311 { 1312 if (curr_mlist == NULL) 1313 return (NULL); 1314 return (curr_mlist->curr_mp->prev->string); 1315 } 1316 1317 #if CMD_HISTORY 1318 /* 1319 * Get the name of the history file. 1320 */ 1321 static char * 1322 histfile_name() 1323 { 1324 char *home; 1325 char *name; 1326 int len; 1327 1328 /* See if filename is explicitly specified by $LESSHISTFILE. */ 1329 name = lgetenv("LESSHISTFILE"); 1330 if (name != NULL && *name != '\0') 1331 { 1332 if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1333 /* $LESSHISTFILE == "-" means don't use a history file. */ 1334 return (NULL); 1335 return (save(name)); 1336 } 1337 1338 /* Otherwise, file is in $HOME. */ 1339 home = lgetenv("HOME"); 1340 if (home == NULL || *home == '\0') 1341 { 1342 #if OS2 1343 home = lgetenv("INIT"); 1344 if (home == NULL || *home == '\0') 1345 #endif 1346 return (NULL); 1347 } 1348 len = strlen(home) + strlen(LESSHISTFILE) + 2; 1349 name = (char *) ecalloc(len, sizeof(char)); 1350 SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1351 return (name); 1352 } 1353 #endif /* CMD_HISTORY */ 1354 1355 /* 1356 * Initialize history from a .lesshist file. 1357 */ 1358 public void 1359 init_cmdhist() 1360 { 1361 #if CMD_HISTORY 1362 struct mlist *ml = NULL; 1363 char line[CMDBUF_SIZE]; 1364 char *filename; 1365 FILE *f; 1366 char *p; 1367 1368 filename = histfile_name(); 1369 if (filename == NULL) 1370 return; 1371 f = fopen(filename, "r"); 1372 free(filename); 1373 if (f == NULL) 1374 return; 1375 if (fgets(line, sizeof(line), f) == NULL || 1376 strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1377 { 1378 fclose(f); 1379 return; 1380 } 1381 while (fgets(line, sizeof(line), f) != NULL) 1382 { 1383 for (p = line; *p != '\0'; p++) 1384 { 1385 if (*p == '\n' || *p == '\r') 1386 { 1387 *p = '\0'; 1388 break; 1389 } 1390 } 1391 if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1392 ml = &mlist_search; 1393 else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1394 { 1395 #if SHELL_ESCAPE || PIPEC 1396 ml = &mlist_shell; 1397 #else 1398 ml = NULL; 1399 #endif 1400 } else if (*line == '"') 1401 { 1402 if (ml != NULL) 1403 cmd_addhist(ml, line+1); 1404 } 1405 } 1406 fclose(f); 1407 #endif /* CMD_HISTORY */ 1408 } 1409 1410 /* 1411 * 1412 */ 1413 #if CMD_HISTORY 1414 static void 1415 save_mlist(ml, f) 1416 struct mlist *ml; 1417 FILE *f; 1418 { 1419 int histsize = 0; 1420 int n; 1421 char *s; 1422 1423 s = lgetenv("LESSHISTSIZE"); 1424 if (s != NULL) 1425 histsize = atoi(s); 1426 if (histsize == 0) 1427 histsize = 100; 1428 1429 ml = ml->prev; 1430 for (n = 0; n < histsize; n++) 1431 { 1432 if (ml->string == NULL) 1433 break; 1434 ml = ml->prev; 1435 } 1436 for (ml = ml->next; ml->string != NULL; ml = ml->next) 1437 fprintf(f, "\"%s\n", ml->string); 1438 } 1439 #endif /* CMD_HISTORY */ 1440 1441 /* 1442 * 1443 */ 1444 public void 1445 save_cmdhist() 1446 { 1447 #if CMD_HISTORY 1448 char *filename; 1449 FILE *f; 1450 int modified = 0; 1451 1452 filename = histfile_name(); 1453 if (filename == NULL) 1454 return; 1455 if (mlist_search.modified) 1456 modified = 1; 1457 #if SHELL_ESCAPE || PIPEC 1458 if (mlist_shell.modified) 1459 modified = 1; 1460 #endif 1461 if (!modified) 1462 return; 1463 f = fopen(filename, "w"); 1464 free(filename); 1465 if (f == NULL) 1466 return; 1467 #if HAVE_FCHMOD 1468 /* Make history file readable only by owner. */ 1469 fchmod(fileno(f), 0600); 1470 #endif 1471 1472 fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1473 1474 fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1475 save_mlist(&mlist_search, f); 1476 1477 #if SHELL_ESCAPE || PIPEC 1478 fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1479 save_mlist(&mlist_shell, f); 1480 #endif 1481 1482 fclose(f); 1483 #endif /* CMD_HISTORY */ 1484 } 1485