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