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