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