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