cmdbuf.c revision 161475
11590Srgrimes/* 21590Srgrimes * Copyright (C) 1984-2005 Mark Nudelman 31590Srgrimes * 41590Srgrimes * You may distribute under the terms of either the GNU General Public 51590Srgrimes * License or the Less License, as specified in the README file. 61590Srgrimes * 71590Srgrimes * For more information about less, or for information on how to 81590Srgrimes * contact the author, see the README file. 91590Srgrimes */ 101590Srgrimes 111590Srgrimes 121590Srgrimes/* 131590Srgrimes * Functions which manipulate the command buffer. 141590Srgrimes * Used only by command() and related functions. 151590Srgrimes */ 161590Srgrimes 171590Srgrimes#include "less.h" 181590Srgrimes#include "cmd.h" 191590Srgrimes#include "charset.h" 201590Srgrimes#if HAVE_STAT 211590Srgrimes#include <sys/stat.h> 221590Srgrimes#endif 231590Srgrimes 241590Srgrimesextern int sc_width; 251590Srgrimesextern int utf_mode; 261590Srgrimes 271590Srgrimesstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ 281590Srgrimesstatic int cmd_col; /* Current column of the cursor */ 291590Srgrimesstatic int prompt_col; /* Column of cursor just after prompt */ 301590Srgrimesstatic char *cp; /* Pointer into cmdbuf */ 3174769Smikehstatic int cmd_offset; /* Index into cmdbuf of first displayed char */ 3288150Smikehstatic int literal; /* Next input char should not be interpreted */ 3374769Smikeh 341590Srgrimes#if TAB_COMPLETE_FILENAME 3599112Sobrienstatic int cmd_complete(); 3699112Sobrien/* 371590Srgrimes * These variables are statics used by cmd_complete. 381590Srgrimes */ 391590Srgrimesstatic int in_completion = 0; 401590Srgrimesstatic char *tk_text; 411590Srgrimesstatic char *tk_original; 421590Srgrimesstatic char *tk_ipoint; 431590Srgrimesstatic char *tk_trial; 441590Srgrimesstatic struct textlist tk_tlist; 451590Srgrimes#endif 461590Srgrimes 471590Srgrimesstatic int cmd_left(); 481590Srgrimesstatic int cmd_right(); 491590Srgrimes 501590Srgrimes#if SPACES_IN_FILENAMES 511590Srgrimespublic char openquote = '"'; 521590Srgrimespublic char closequote = '"'; 5377274Smikeh#endif 5477274Smikeh 551590Srgrimes#if CMD_HISTORY 561590Srgrimes 571590Srgrimes/* History file */ 581590Srgrimes#define HISTFILE_FIRST_LINE ".less-history-file:" 59216564Scharnier#define HISTFILE_SEARCH_SECTION ".search" 601590Srgrimes#define HISTFILE_SHELL_SECTION ".shell" 6177274Smikeh 6277274Smikeh/* 631590Srgrimes * A mlist structure represents a command history. 641590Srgrimes */ 651590Srgrimesstruct mlist 6674769Smikeh{ 6788150Smikeh struct mlist *next; 681590Srgrimes struct mlist *prev; 691590Srgrimes struct mlist *curr_mp; 7074769Smikeh char *string; 7174769Smikeh}; 7274769Smikeh 7377274Smikeh/* 741590Srgrimes * These are the various command histories that exist. 7588150Smikeh */ 7688150Smikehstruct mlist mlist_search = 7788150Smikeh { &mlist_search, &mlist_search, &mlist_search, NULL }; 7888150Smikehpublic void * constant ml_search = (void *) &mlist_search; 7988227Sache 8088150Smikehstruct mlist mlist_examine = 8188150Smikeh { &mlist_examine, &mlist_examine, &mlist_examine, NULL }; 8288150Smikehpublic void * constant ml_examine = (void *) &mlist_examine; 8388150Smikeh 8488150Smikeh#if SHELL_ESCAPE || PIPEC 8588150Smikehstruct mlist mlist_shell = 8688227Sache { &mlist_shell, &mlist_shell, &mlist_shell, NULL }; 8788227Sachepublic void * constant ml_shell = (void *) &mlist_shell; 8888150Smikeh#endif 8988150Smikeh 901590Srgrimes#else /* CMD_HISTORY */ 911590Srgrimes 921590Srgrimes/* If CMD_HISTORY is off, these are just flags. */ 931590Srgrimespublic void * constant ml_search = (void *)1; 941590Srgrimespublic void * constant ml_examine = (void *)2; 951590Srgrimes#if SHELL_ESCAPE || PIPEC 961590Srgrimespublic void * constant ml_shell = (void *)3; 971590Srgrimes#endif 9874769Smikeh 9974769Smikeh#endif /* CMD_HISTORY */ 10074769Smikeh 10188150Smikeh/* 1021590Srgrimes * History for the current command. 1031590Srgrimes */ 1041590Srgrimesstatic struct mlist *curr_mlist = NULL; 10576455Smikehstatic int curr_cmdflags; 10676455Smikeh 10776455Smikehstatic char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; 10876455Smikehstatic int cmd_mbc_buf_len; 10976455Smikehstatic int cmd_mbc_buf_index; 11076455Smikeh 11176455Smikeh 11276455Smikeh/* 11376455Smikeh * Reset command buffer (to empty). 11476455Smikeh */ 11577274Smikeh public void 11674769Smikehcmd_reset() 11774769Smikeh{ 11874769Smikeh cp = cmdbuf; 11974769Smikeh *cp = '\0'; 1201590Srgrimes cmd_col = 0; 1211590Srgrimes cmd_offset = 0; 12274769Smikeh literal = 0; 12374769Smikeh cmd_mbc_buf_len = 0; 1241590Srgrimes} 1251590Srgrimes 1261590Srgrimes/* 1271590Srgrimes * Clear command line on display. 12867496Sphk */ 1291590Srgrimes public void 1301590Srgrimesclear_cmd() 1311590Srgrimes{ 1321590Srgrimes clear_bot(); 1331590Srgrimes cmd_col = prompt_col = 0; 13477274Smikeh cmd_mbc_buf_len = 0; 13588227Sache} 1361590Srgrimes 1371590Srgrimes/* 1381590Srgrimes * Display a string, usually as a prompt for input into the command buffer. 13977274Smikeh */ 1401590Srgrimes public void 1411590Srgrimescmd_putstr(s) 1421590Srgrimes char *s; 1431590Srgrimes{ 1441590Srgrimes LWCHAR prev_ch = 0; 1451590Srgrimes LWCHAR ch; 1461590Srgrimes char *endline = s + strlen(s); 14788227Sache while (*s != '\0') 1481590Srgrimes { 1491590Srgrimes char *ns = s; 1501590Srgrimes ch = step_char(&ns, +1, endline); 1511590Srgrimes while (s < ns) 1521590Srgrimes putchr(*s++); 1531590Srgrimes if (!utf_mode) 1541590Srgrimes { 1551590Srgrimes cmd_col++; 1561590Srgrimes prompt_col++; 1571590Srgrimes } else if (!is_composing_char(ch) && 1581590Srgrimes !is_combining_char(prev_ch, ch)) 1591590Srgrimes { 1601590Srgrimes int width = is_wide_char(ch) ? 2 : 1; 16188150Smikeh cmd_col += width; 1621590Srgrimes prompt_col += width; 1631590Srgrimes } 164216564Scharnier prev_ch = ch; 1651590Srgrimes } 16677274Smikeh} 1671590Srgrimes 1681590Srgrimes/* 16977274Smikeh * How many characters are in the command buffer? 17088150Smikeh */ 17188150Smikeh public int 17288150Smikehlen_cmdbuf() 17388150Smikeh{ 1741590Srgrimes char *s = cmdbuf; 1751590Srgrimes char *endline = s + strlen(s); 17688150Smikeh int len = 0; 1771590Srgrimes 1781590Srgrimes while (*s != '\0') 1791590Srgrimes { 1801590Srgrimes step_char(&s, +1, endline); 1811590Srgrimes len++; 18276455Smikeh } 1831590Srgrimes return (len); 1841590Srgrimes} 185216564Scharnier 1861590Srgrimes/* 18777274Smikeh * Common part of cmd_step_right() and cmd_step_left(). 1881590Srgrimes */ 1891590Srgrimes static char * 1901590Srgrimescmd_step_common(p, ch, len, pwidth, bswidth) 19177274Smikeh char *p; 1921590Srgrimes LWCHAR ch; 1931590Srgrimes int len; 1941590Srgrimes int *pwidth; 19576455Smikeh int *bswidth; 19676455Smikeh{ 19777274Smikeh char *pr; 1981590Srgrimes 1991590Srgrimes if (len == 1) 2001590Srgrimes { 2011590Srgrimes pr = prchar((int) ch); 2021590Srgrimes if (pwidth != NULL || bswidth != NULL) 2031590Srgrimes { 2041590Srgrimes int len = strlen(pr); 205216564Scharnier if (pwidth != NULL) 2061590Srgrimes *pwidth = len; 2071590Srgrimes if (bswidth != NULL) 20877274Smikeh *bswidth = len; 20982793Sache } 21082793Sache } else 21188227Sache { 2121590Srgrimes pr = prutfchar(ch); 2131590Srgrimes if (pwidth != NULL || bswidth != NULL) 2141590Srgrimes { 2151590Srgrimes if (is_composing_char(ch)) 2161590Srgrimes { 2171590Srgrimes if (pwidth != NULL) 2181590Srgrimes *pwidth = 0; 2191590Srgrimes if (bswidth != NULL) 220216564Scharnier *bswidth = 0; 2211590Srgrimes } else if (is_ubin_char(ch)) 22288150Smikeh { 22388150Smikeh int len = strlen(pr); 2241590Srgrimes if (pwidth != NULL) 22588150Smikeh *pwidth = len; 22688150Smikeh if (bswidth != NULL) 22788150Smikeh *bswidth = len; 22888150Smikeh } else 22988150Smikeh { 23088150Smikeh LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 23188150Smikeh if (is_combining_char(prev_ch, ch)) 23288150Smikeh { 23388150Smikeh if (pwidth != NULL) 23488150Smikeh *pwidth = 0; 23588150Smikeh if (bswidth != NULL) 23677274Smikeh *bswidth = 0; 23777274Smikeh } else 238336953Smarkj { 23974769Smikeh if (pwidth != NULL) 2401590Srgrimes *pwidth = is_wide_char(ch) 2411590Srgrimes ? 2 24277274Smikeh : 1; 2431590Srgrimes if (bswidth != NULL) 2441590Srgrimes *bswidth = 1; 2451590Srgrimes } 2461590Srgrimes } 2471590Srgrimes } 2481590Srgrimes } 2491590Srgrimes 250216564Scharnier return (pr); 2511590Srgrimes} 25277274Smikeh 2531590Srgrimes/* 2541590Srgrimes * Step a pointer one character right in the command buffer. 2551590Srgrimes */ 2561590Srgrimes static char * 2571590Srgrimescmd_step_right(pp, pwidth, bswidth) 2581590Srgrimes char **pp; 259216564Scharnier int *pwidth; 2601590Srgrimes int *bswidth; 2611590Srgrimes{ 2621590Srgrimes char *p = *pp; 2631590Srgrimes LWCHAR ch = step_char(pp, +1, p + strlen(p)); 26477274Smikeh 2651590Srgrimes return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); 2661590Srgrimes} 26777274Smikeh 2681590Srgrimes/* 26977274Smikeh * Step a pointer one character left in the command buffer. 2701590Srgrimes */ 2711590Srgrimes static char * 2721590Srgrimescmd_step_left(pp, pwidth, bswidth) 27388150Smikeh char **pp; 2741590Srgrimes int *pwidth; 2751590Srgrimes int *bswidth; 2761590Srgrimes{ 2771590Srgrimes char *p = *pp; 278216564Scharnier LWCHAR ch = step_char(pp, -1, cmdbuf); 2791590Srgrimes 2801590Srgrimes return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); 28188150Smikeh} 28288150Smikeh 28388150Smikeh/* 28488150Smikeh * Repaint the line from cp onwards. 28588150Smikeh * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 28688150Smikeh */ 28788150Smikeh static void 2881590Srgrimescmd_repaint(old_cp) 2891590Srgrimes char *old_cp; 2901590Srgrimes{ 2911590Srgrimes /* 2921590Srgrimes * Repaint the line from the current position. 2931590Srgrimes */ 294216564Scharnier clear_eol(); 2951590Srgrimes while (*cp != '\0') 2961590Srgrimes { 2971590Srgrimes char *np = cp; 29888150Smikeh int width; 2991590Srgrimes char *pr = cmd_step_right(&np, &width, NULL); 3001590Srgrimes if (cmd_col + width >= sc_width) 3011590Srgrimes break; 3021590Srgrimes cp = np; 3031590Srgrimes putstr(pr); 3041590Srgrimes cmd_col += width; 3051590Srgrimes } 306216564Scharnier while (*cp != '\0') 3071590Srgrimes { 3081590Srgrimes char *np = cp; 3091590Srgrimes int width; 3101590Srgrimes char *pr = cmd_step_right(&np, &width, NULL); 31177274Smikeh if (width > 0) 31277274Smikeh break; 3131590Srgrimes cp = np; 3141590Srgrimes putstr(pr); 3151590Srgrimes } 3161590Srgrimes 3171590Srgrimes /* 3181590Srgrimes * Back up the cursor to the correct position. 3191590Srgrimes */ 3201590Srgrimes while (cp > old_cp) 3211590Srgrimes cmd_left(); 3221590Srgrimes} 3231590Srgrimes 3241590Srgrimes/* 3251590Srgrimes * Put the cursor at "home" (just after the prompt), 3261590Srgrimes * and set cp to the corresponding char in cmdbuf. 327216564Scharnier */ 3281590Srgrimes static void 3291590Srgrimescmd_home() 3301590Srgrimes{ 33177274Smikeh while (cmd_col > prompt_col) 33277274Smikeh { 3331590Srgrimes int width, bswidth; 3341590Srgrimes 3351590Srgrimes cmd_step_left(&cp, &width, &bswidth); 3361590Srgrimes while (bswidth-- > 0) 3371590Srgrimes putbs(); 3381590Srgrimes cmd_col -= width; 3391590Srgrimes } 3401590Srgrimes 3411590Srgrimes cp = &cmdbuf[cmd_offset]; 3421590Srgrimes} 3431590Srgrimes 34474769Smikeh/* 34577274Smikeh * Shift the cmdbuf display left a half-screen. 3461590Srgrimes */ 3471590Srgrimes static void 3481590Srgrimescmd_lshift() 3491590Srgrimes{ 3501590Srgrimes char *s; 35177274Smikeh char *save_cp; 3521590Srgrimes int cols; 35377274Smikeh 3541590Srgrimes /* 35577274Smikeh * Start at the first displayed char, count how far to the 3561590Srgrimes * right we'd have to move to reach the center of the screen. 3571590Srgrimes */ 3581590Srgrimes s = cmdbuf + cmd_offset; 35974769Smikeh cols = 0; 36077274Smikeh while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 3611590Srgrimes { 3621590Srgrimes int width; 3631590Srgrimes cmd_step_right(&s, &width, NULL); 36474769Smikeh cols += width; 36574769Smikeh } 36677274Smikeh while (*s != '\0') 3671590Srgrimes { 3681590Srgrimes int width; 36974769Smikeh char *ns = s; 370300274Struckman cmd_step_right(&ns, &width, NULL); 3711590Srgrimes if (width > 0) 37274769Smikeh break; 373300274Struckman s = ns; 3741590Srgrimes } 37577274Smikeh 37677274Smikeh cmd_offset = s - cmdbuf; 37777274Smikeh save_cp = cp; 37877274Smikeh cmd_home(); 3791590Srgrimes cmd_repaint(save_cp); 38077274Smikeh} 38177274Smikeh 38277274Smikeh/* 3831590Srgrimes * Shift the cmdbuf display right a half-screen. 38477274Smikeh */ 3851590Srgrimes static void 38677274Smikehcmd_rshift() 38777274Smikeh{ 38877274Smikeh char *s; 3891590Srgrimes char *save_cp; 39077274Smikeh int cols; 3911590Srgrimes 3921590Srgrimes /* 39374769Smikeh * Start at the first displayed char, count how far to the 39477274Smikeh * left we'd have to move to traverse a half-screen width 3951590Srgrimes * of displayed characters. 3961590Srgrimes */ 3971590Srgrimes s = cmdbuf + cmd_offset; 39877274Smikeh cols = 0; 3991590Srgrimes while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 4001590Srgrimes { 4011590Srgrimes int width; 40277274Smikeh cmd_step_left(&s, &width, NULL); 4031590Srgrimes cols += width; 40474769Smikeh } 4051590Srgrimes 4061590Srgrimes cmd_offset = s - cmdbuf; 4071590Srgrimes save_cp = cp; 40874769Smikeh cmd_home(); 4091590Srgrimes cmd_repaint(save_cp); 41077274Smikeh} 4111590Srgrimes 41277274Smikeh/* 4131590Srgrimes * Move cursor right one character. 4141590Srgrimes */ 4151590Srgrimes static int 4161590Srgrimescmd_right() 4171590Srgrimes{ 4181590Srgrimes char *pr; 419216564Scharnier char *ncp; 4201590Srgrimes int width; 4211590Srgrimes 42274769Smikeh if (*cp == '\0') 4231590Srgrimes { 42477274Smikeh /* Already at the end of the line. */ 4251590Srgrimes return (CC_OK); 4261590Srgrimes } 42774769Smikeh ncp = cp; 4281590Srgrimes pr = cmd_step_right(&ncp, &width, NULL); 42977274Smikeh if (cmd_col + width >= sc_width) 43077274Smikeh cmd_lshift(); 43181979Sbrian else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 4321590Srgrimes cmd_lshift(); 4331590Srgrimes cp = ncp; 4341590Srgrimes cmd_col += width; 4351590Srgrimes putstr(pr); 4361590Srgrimes while (*cp != '\0') 4371590Srgrimes { 438216564Scharnier pr = cmd_step_right(&ncp, &width, NULL); 4391590Srgrimes if (width > 0) 44077274Smikeh break; 4411590Srgrimes putstr(pr); 44277274Smikeh cp = ncp; 4431590Srgrimes } 4441590Srgrimes return (CC_OK); 4451590Srgrimes} 4461590Srgrimes 44777274Smikeh/* 4481590Srgrimes * Move cursor left one character. 4491590Srgrimes */ 45077274Smikeh static int 4511590Srgrimescmd_left() 452{ 453 char *ncp; 454 int width, bswidth; 455 456 if (cp <= cmdbuf) 457 { 458 /* Already at the beginning of the line */ 459 return (CC_OK); 460 } 461 ncp = cp; 462 while (ncp > cmdbuf) 463 { 464 cmd_step_left(&ncp, &width, &bswidth); 465 if (width > 0) 466 break; 467 } 468 if (cmd_col < prompt_col + width) 469 cmd_rshift(); 470 cp = ncp; 471 cmd_col -= width; 472 while (bswidth-- > 0) 473 putbs(); 474 return (CC_OK); 475} 476 477/* 478 * Insert a char into the command buffer, at the current position. 479 */ 480 static int 481cmd_ichar(cs, clen) 482 char *cs; 483 int clen; 484{ 485 char *s; 486 487 if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 488 { 489 /* No room in the command buffer for another char. */ 490 bell(); 491 return (CC_ERROR); 492 } 493 494 /* 495 * Make room for the new character (shift the tail of the buffer right). 496 */ 497 for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 498 s[clen] = s[0]; 499 /* 500 * Insert the character into the buffer. 501 */ 502 for (s = cp; s < cp + clen; s++) 503 *s = *cs++; 504 /* 505 * Reprint the tail of the line from the inserted char. 506 */ 507 cmd_repaint(cp); 508 cmd_right(); 509 return (CC_OK); 510} 511 512/* 513 * Backspace in the command buffer. 514 * Delete the char to the left of the cursor. 515 */ 516 static int 517cmd_erase() 518{ 519 register char *s; 520 int clen; 521 522 if (cp == cmdbuf) 523 { 524 /* 525 * Backspace past beginning of the buffer: 526 * this usually means abort the command. 527 */ 528 return (CC_QUIT); 529 } 530 /* 531 * Move cursor left (to the char being erased). 532 */ 533 s = cp; 534 cmd_left(); 535 clen = s - cp; 536 537 /* 538 * Remove the char from the buffer (shift the buffer left). 539 */ 540 for (s = cp; ; s++) 541 { 542 s[0] = s[clen]; 543 if (s[0] == '\0') 544 break; 545 } 546 547 /* 548 * Repaint the buffer after the erased char. 549 */ 550 cmd_repaint(cp); 551 552 /* 553 * We say that erasing the entire command string causes us 554 * to abort the current command, if CF_QUIT_ON_ERASE is set. 555 */ 556 if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 557 return (CC_QUIT); 558 return (CC_OK); 559} 560 561/* 562 * Delete the char under the cursor. 563 */ 564 static int 565cmd_delete() 566{ 567 if (*cp == '\0') 568 { 569 /* At end of string; there is no char under the cursor. */ 570 return (CC_OK); 571 } 572 /* 573 * Move right, then use cmd_erase. 574 */ 575 cmd_right(); 576 cmd_erase(); 577 return (CC_OK); 578} 579 580/* 581 * Delete the "word" to the left of the cursor. 582 */ 583 static int 584cmd_werase() 585{ 586 if (cp > cmdbuf && cp[-1] == ' ') 587 { 588 /* 589 * If the char left of cursor is a space, 590 * erase all the spaces left of cursor (to the first non-space). 591 */ 592 while (cp > cmdbuf && cp[-1] == ' ') 593 (void) cmd_erase(); 594 } else 595 { 596 /* 597 * If the char left of cursor is not a space, 598 * erase all the nonspaces left of cursor (the whole "word"). 599 */ 600 while (cp > cmdbuf && cp[-1] != ' ') 601 (void) cmd_erase(); 602 } 603 return (CC_OK); 604} 605 606/* 607 * Delete the "word" under the cursor. 608 */ 609 static int 610cmd_wdelete() 611{ 612 if (*cp == ' ') 613 { 614 /* 615 * If the char under the cursor is a space, 616 * delete it and all the spaces right of cursor. 617 */ 618 while (*cp == ' ') 619 (void) cmd_delete(); 620 } else 621 { 622 /* 623 * If the char under the cursor is not a space, 624 * delete it and all nonspaces right of cursor (the whole word). 625 */ 626 while (*cp != ' ' && *cp != '\0') 627 (void) cmd_delete(); 628 } 629 return (CC_OK); 630} 631 632/* 633 * Delete all chars in the command buffer. 634 */ 635 static int 636cmd_kill() 637{ 638 if (cmdbuf[0] == '\0') 639 { 640 /* Buffer is already empty; abort the current command. */ 641 return (CC_QUIT); 642 } 643 cmd_offset = 0; 644 cmd_home(); 645 *cp = '\0'; 646 cmd_repaint(cp); 647 648 /* 649 * We say that erasing the entire command string causes us 650 * to abort the current command, if CF_QUIT_ON_ERASE is set. 651 */ 652 if (curr_cmdflags & CF_QUIT_ON_ERASE) 653 return (CC_QUIT); 654 return (CC_OK); 655} 656 657/* 658 * Select an mlist structure to be the current command history. 659 */ 660 public void 661set_mlist(mlist, cmdflags) 662 void *mlist; 663 int cmdflags; 664{ 665 curr_mlist = (struct mlist *) mlist; 666 curr_cmdflags = cmdflags; 667 668 /* Make sure the next up-arrow moves to the last string in the mlist. */ 669 if (curr_mlist != NULL) 670 curr_mlist->curr_mp = curr_mlist; 671} 672 673#if CMD_HISTORY 674/* 675 * Move up or down in the currently selected command history list. 676 */ 677 static int 678cmd_updown(action) 679 int action; 680{ 681 char *s; 682 683 if (curr_mlist == NULL) 684 { 685 /* 686 * The current command has no history list. 687 */ 688 bell(); 689 return (CC_OK); 690 } 691 cmd_home(); 692 clear_eol(); 693 /* 694 * Move curr_mp to the next/prev entry. 695 */ 696 if (action == EC_UP) 697 curr_mlist->curr_mp = curr_mlist->curr_mp->prev; 698 else 699 curr_mlist->curr_mp = curr_mlist->curr_mp->next; 700 /* 701 * Copy the entry into cmdbuf and echo it on the screen. 702 */ 703 s = curr_mlist->curr_mp->string; 704 if (s == NULL) 705 s = ""; 706 strcpy(cmdbuf, s); 707 for (cp = cmdbuf; *cp != '\0'; ) 708 cmd_right(); 709 return (CC_OK); 710} 711#endif 712 713/* 714 * Add a string to a history list. 715 */ 716 public void 717cmd_addhist(mlist, cmd) 718 struct mlist *mlist; 719 char *cmd; 720{ 721#if CMD_HISTORY 722 struct mlist *ml; 723 724 /* 725 * Don't save a trivial command. 726 */ 727 if (strlen(cmd) == 0) 728 return; 729 730 /* 731 * Save the command unless it's a duplicate of the 732 * last command in the history. 733 */ 734 ml = mlist->prev; 735 if (ml == mlist || strcmp(ml->string, cmd) != 0) 736 { 737 /* 738 * Did not find command in history. 739 * Save the command and put it at the end of the history list. 740 */ 741 ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 742 ml->string = save(cmd); 743 ml->next = mlist; 744 ml->prev = mlist->prev; 745 mlist->prev->next = ml; 746 mlist->prev = ml; 747 } 748 /* 749 * Point to the cmd just after the just-accepted command. 750 * Thus, an UPARROW will always retrieve the previous command. 751 */ 752 mlist->curr_mp = ml->next; 753#endif 754} 755 756/* 757 * Accept the command in the command buffer. 758 * Add it to the currently selected history list. 759 */ 760 public void 761cmd_accept() 762{ 763#if CMD_HISTORY 764 /* 765 * Nothing to do if there is no currently selected history list. 766 */ 767 if (curr_mlist == NULL) 768 return; 769 cmd_addhist(curr_mlist, cmdbuf); 770#endif 771} 772 773/* 774 * Try to perform a line-edit function on the command buffer, 775 * using a specified char as a line-editing command. 776 * Returns: 777 * CC_PASS The char does not invoke a line edit function. 778 * CC_OK Line edit function done. 779 * CC_QUIT The char requests the current command to be aborted. 780 */ 781 static int 782cmd_edit(c) 783 int c; 784{ 785 int action; 786 int flags; 787 788#if TAB_COMPLETE_FILENAME 789#define not_in_completion() in_completion = 0 790#else 791#define not_in_completion() 792#endif 793 794 /* 795 * See if the char is indeed a line-editing command. 796 */ 797 flags = 0; 798#if CMD_HISTORY 799 if (curr_mlist == NULL) 800 /* 801 * No current history; don't accept history manipulation cmds. 802 */ 803 flags |= EC_NOHISTORY; 804#endif 805#if TAB_COMPLETE_FILENAME 806 if (curr_mlist == ml_search) 807 /* 808 * In a search command; don't accept file-completion cmds. 809 */ 810 flags |= EC_NOCOMPLETE; 811#endif 812 813 action = editchar(c, flags); 814 815 switch (action) 816 { 817 case EC_RIGHT: 818 not_in_completion(); 819 return (cmd_right()); 820 case EC_LEFT: 821 not_in_completion(); 822 return (cmd_left()); 823 case EC_W_RIGHT: 824 not_in_completion(); 825 while (*cp != '\0' && *cp != ' ') 826 cmd_right(); 827 while (*cp == ' ') 828 cmd_right(); 829 return (CC_OK); 830 case EC_W_LEFT: 831 not_in_completion(); 832 while (cp > cmdbuf && cp[-1] == ' ') 833 cmd_left(); 834 while (cp > cmdbuf && cp[-1] != ' ') 835 cmd_left(); 836 return (CC_OK); 837 case EC_HOME: 838 not_in_completion(); 839 cmd_offset = 0; 840 cmd_home(); 841 cmd_repaint(cp); 842 return (CC_OK); 843 case EC_END: 844 not_in_completion(); 845 while (*cp != '\0') 846 cmd_right(); 847 return (CC_OK); 848 case EC_INSERT: 849 not_in_completion(); 850 return (CC_OK); 851 case EC_BACKSPACE: 852 not_in_completion(); 853 return (cmd_erase()); 854 case EC_LINEKILL: 855 not_in_completion(); 856 return (cmd_kill()); 857 case EC_W_BACKSPACE: 858 not_in_completion(); 859 return (cmd_werase()); 860 case EC_DELETE: 861 not_in_completion(); 862 return (cmd_delete()); 863 case EC_W_DELETE: 864 not_in_completion(); 865 return (cmd_wdelete()); 866 case EC_LITERAL: 867 literal = 1; 868 return (CC_OK); 869#if CMD_HISTORY 870 case EC_UP: 871 case EC_DOWN: 872 not_in_completion(); 873 return (cmd_updown(action)); 874#endif 875#if TAB_COMPLETE_FILENAME 876 case EC_F_COMPLETE: 877 case EC_B_COMPLETE: 878 case EC_EXPAND: 879 return (cmd_complete(action)); 880#endif 881 case EC_NOACTION: 882 return (CC_OK); 883 default: 884 not_in_completion(); 885 return (CC_PASS); 886 } 887} 888 889#if TAB_COMPLETE_FILENAME 890/* 891 * Insert a string into the command buffer, at the current position. 892 */ 893 static int 894cmd_istr(str) 895 char *str; 896{ 897 char *s; 898 int action; 899 char *endline = str + strlen(str); 900 901 for (s = str; *s != '\0'; ) 902 { 903 char *os = s; 904 step_char(&s, +1, endline); 905 action = cmd_ichar(os, s - os); 906 if (action != CC_OK) 907 { 908 bell(); 909 return (action); 910 } 911 } 912 return (CC_OK); 913} 914 915/* 916 * Find the beginning and end of the "current" word. 917 * This is the word which the cursor (cp) is inside or at the end of. 918 * Return pointer to the beginning of the word and put the 919 * cursor at the end of the word. 920 */ 921 static char * 922delimit_word() 923{ 924 char *word; 925#if SPACES_IN_FILENAMES 926 char *p; 927 int delim_quoted = 0; 928 int meta_quoted = 0; 929 char *esc = get_meta_escape(); 930 int esclen = strlen(esc); 931#endif 932 933 /* 934 * Move cursor to end of word. 935 */ 936 if (*cp != ' ' && *cp != '\0') 937 { 938 /* 939 * Cursor is on a nonspace. 940 * Move cursor right to the next space. 941 */ 942 while (*cp != ' ' && *cp != '\0') 943 cmd_right(); 944 } else if (cp > cmdbuf && cp[-1] != ' ') 945 { 946 /* 947 * Cursor is on a space, and char to the left is a nonspace. 948 * We're already at the end of the word. 949 */ 950 ; 951#if 0 952 } else 953 { 954 /* 955 * Cursor is on a space and char to the left is a space. 956 * Huh? There's no word here. 957 */ 958 return (NULL); 959#endif 960 } 961 /* 962 * Find the beginning of the word which the cursor is in. 963 */ 964 if (cp == cmdbuf) 965 return (NULL); 966#if SPACES_IN_FILENAMES 967 /* 968 * If we have an unbalanced quote (that is, an open quote 969 * without a corresponding close quote), we return everything 970 * from the open quote, including spaces. 971 */ 972 for (word = cmdbuf; word < cp; word++) 973 if (*word != ' ') 974 break; 975 if (word >= cp) 976 return (cp); 977 for (p = cmdbuf; p < cp; p++) 978 { 979 if (meta_quoted) 980 { 981 meta_quoted = 0; 982 } else if (esclen > 0 && p + esclen < cp && 983 strncmp(p, esc, esclen) == 0) 984 { 985 meta_quoted = 1; 986 p += esclen - 1; 987 } else if (delim_quoted) 988 { 989 if (*p == closequote) 990 delim_quoted = 0; 991 } else /* (!delim_quoted) */ 992 { 993 if (*p == openquote) 994 delim_quoted = 1; 995 else if (*p == ' ') 996 word = p+1; 997 } 998 } 999#endif 1000 return (word); 1001} 1002 1003/* 1004 * Set things up to enter completion mode. 1005 * Expand the word under the cursor into a list of filenames 1006 * which start with that word, and set tk_text to that list. 1007 */ 1008 static void 1009init_compl() 1010{ 1011 char *word; 1012 char c; 1013 1014 /* 1015 * Get rid of any previous tk_text. 1016 */ 1017 if (tk_text != NULL) 1018 { 1019 free(tk_text); 1020 tk_text = NULL; 1021 } 1022 /* 1023 * Find the original (uncompleted) word in the command buffer. 1024 */ 1025 word = delimit_word(); 1026 if (word == NULL) 1027 return; 1028 /* 1029 * Set the insertion point to the point in the command buffer 1030 * where the original (uncompleted) word now sits. 1031 */ 1032 tk_ipoint = word; 1033 /* 1034 * Save the original (uncompleted) word 1035 */ 1036 if (tk_original != NULL) 1037 free(tk_original); 1038 tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 1039 strncpy(tk_original, word, cp-word); 1040 /* 1041 * Get the expanded filename. 1042 * This may result in a single filename, or 1043 * a blank-separated list of filenames. 1044 */ 1045 c = *cp; 1046 *cp = '\0'; 1047 if (*word != openquote) 1048 { 1049 tk_text = fcomplete(word); 1050 } else 1051 { 1052 char *qword = shell_quote(word+1); 1053 if (qword == NULL) 1054 tk_text = fcomplete(word+1); 1055 else 1056 { 1057 tk_text = fcomplete(qword); 1058 free(qword); 1059 } 1060 } 1061 *cp = c; 1062} 1063 1064/* 1065 * Return the next word in the current completion list. 1066 */ 1067 static char * 1068next_compl(action, prev) 1069 int action; 1070 char *prev; 1071{ 1072 switch (action) 1073 { 1074 case EC_F_COMPLETE: 1075 return (forw_textlist(&tk_tlist, prev)); 1076 case EC_B_COMPLETE: 1077 return (back_textlist(&tk_tlist, prev)); 1078 } 1079 /* Cannot happen */ 1080 return ("?"); 1081} 1082 1083/* 1084 * Complete the filename before (or under) the cursor. 1085 * cmd_complete may be called multiple times. The global in_completion 1086 * remembers whether this call is the first time (create the list), 1087 * or a subsequent time (step thru the list). 1088 */ 1089 static int 1090cmd_complete(action) 1091 int action; 1092{ 1093 char *s; 1094 1095 if (!in_completion || action == EC_EXPAND) 1096 { 1097 /* 1098 * Expand the word under the cursor and 1099 * use the first word in the expansion 1100 * (or the entire expansion if we're doing EC_EXPAND). 1101 */ 1102 init_compl(); 1103 if (tk_text == NULL) 1104 { 1105 bell(); 1106 return (CC_OK); 1107 } 1108 if (action == EC_EXPAND) 1109 { 1110 /* 1111 * Use the whole list. 1112 */ 1113 tk_trial = tk_text; 1114 } else 1115 { 1116 /* 1117 * Use the first filename in the list. 1118 */ 1119 in_completion = 1; 1120 init_textlist(&tk_tlist, tk_text); 1121 tk_trial = next_compl(action, (char*)NULL); 1122 } 1123 } else 1124 { 1125 /* 1126 * We already have a completion list. 1127 * Use the next/previous filename from the list. 1128 */ 1129 tk_trial = next_compl(action, tk_trial); 1130 } 1131 1132 /* 1133 * Remove the original word, or the previous trial completion. 1134 */ 1135 while (cp > tk_ipoint) 1136 (void) cmd_erase(); 1137 1138 if (tk_trial == NULL) 1139 { 1140 /* 1141 * There are no more trial completions. 1142 * Insert the original (uncompleted) filename. 1143 */ 1144 in_completion = 0; 1145 if (cmd_istr(tk_original) != CC_OK) 1146 goto fail; 1147 } else 1148 { 1149 /* 1150 * Insert trial completion. 1151 */ 1152 if (cmd_istr(tk_trial) != CC_OK) 1153 goto fail; 1154 /* 1155 * If it is a directory, append a slash. 1156 */ 1157 if (is_dir(tk_trial)) 1158 { 1159 if (cp > cmdbuf && cp[-1] == closequote) 1160 (void) cmd_erase(); 1161 s = lgetenv("LESSSEPARATOR"); 1162 if (s == NULL) 1163 s = PATHNAME_SEP; 1164 if (cmd_istr(s) != CC_OK) 1165 goto fail; 1166 } 1167 } 1168 1169 return (CC_OK); 1170 1171fail: 1172 in_completion = 0; 1173 bell(); 1174 return (CC_OK); 1175} 1176 1177#endif /* TAB_COMPLETE_FILENAME */ 1178 1179/* 1180 * Process a single character of a multi-character command, such as 1181 * a number, or the pattern of a search command. 1182 * Returns: 1183 * CC_OK The char was accepted. 1184 * CC_QUIT The char requests the command to be aborted. 1185 * CC_ERROR The char could not be accepted due to an error. 1186 */ 1187 public int 1188cmd_char(c) 1189 int c; 1190{ 1191 int action; 1192 int len; 1193 1194 if (!utf_mode) 1195 { 1196 cmd_mbc_buf[0] = c; 1197 len = 1; 1198 } else 1199 { 1200 /* Perform strict validation in all possible cases. */ 1201 if (cmd_mbc_buf_len == 0) 1202 { 1203 retry: 1204 cmd_mbc_buf_index = 1; 1205 *cmd_mbc_buf = c; 1206 if (IS_ASCII_OCTET(c)) 1207 cmd_mbc_buf_len = 1; 1208 else if (IS_UTF8_LEAD(c)) 1209 { 1210 cmd_mbc_buf_len = utf_len(c); 1211 return (CC_OK); 1212 } else 1213 { 1214 /* UTF8_INVALID or stray UTF8_TRAIL */ 1215 bell(); 1216 return (CC_ERROR); 1217 } 1218 } else if (IS_UTF8_TRAIL(c)) 1219 { 1220 cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1221 if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1222 return (CC_OK); 1223 if (!is_utf8_well_formed(cmd_mbc_buf)) 1224 { 1225 /* complete, but not well formed (non-shortest form), sequence */ 1226 cmd_mbc_buf_len = 0; 1227 bell(); 1228 return (CC_ERROR); 1229 } 1230 } else 1231 { 1232 /* Flush incomplete (truncated) sequence. */ 1233 cmd_mbc_buf_len = 0; 1234 bell(); 1235 /* Handle new char. */ 1236 goto retry; 1237 } 1238 1239 len = cmd_mbc_buf_len; 1240 cmd_mbc_buf_len = 0; 1241 } 1242 1243 if (literal) 1244 { 1245 /* 1246 * Insert the char, even if it is a line-editing char. 1247 */ 1248 literal = 0; 1249 return (cmd_ichar(cmd_mbc_buf, len)); 1250 } 1251 1252 /* 1253 * See if it is a line-editing character. 1254 */ 1255 if (in_mca() && len == 1) 1256 { 1257 action = cmd_edit(c); 1258 switch (action) 1259 { 1260 case CC_OK: 1261 case CC_QUIT: 1262 return (action); 1263 case CC_PASS: 1264 break; 1265 } 1266 } 1267 1268 /* 1269 * Insert the char into the command buffer. 1270 */ 1271 return (cmd_ichar(cmd_mbc_buf, len)); 1272} 1273 1274/* 1275 * Return the number currently in the command buffer. 1276 */ 1277 public LINENUM 1278cmd_int() 1279{ 1280 register char *p; 1281 LINENUM n = 0; 1282 1283 for (p = cmdbuf; *p != '\0'; p++) 1284 n = (10 * n) + (*p - '0'); 1285 return (n); 1286} 1287 1288/* 1289 * Return a pointer to the command buffer. 1290 */ 1291 public char * 1292get_cmdbuf() 1293{ 1294 return (cmdbuf); 1295} 1296 1297#if CMD_HISTORY 1298/* 1299 * Get the name of the history file. 1300 */ 1301 static char * 1302histfile_name() 1303{ 1304 char *home; 1305 char *name; 1306 int len; 1307 1308 /* See if filename is explicitly specified by $LESSHISTFILE. */ 1309 name = lgetenv("LESSHISTFILE"); 1310 if (name != NULL && *name != '\0') 1311 { 1312 if (strcmp(name, "-") == 0) 1313 /* $LESSHISTFILE == "-" means don't use a history file. */ 1314 return (NULL); 1315 return (save(name)); 1316 } 1317 1318 /* Otherwise, file is in $HOME. */ 1319 home = lgetenv("HOME"); 1320 if (home == NULL || *home == '\0') 1321 { 1322#if OS2 1323 home = lgetenv("INIT"); 1324 if (home == NULL || *home == '\0') 1325#endif 1326 return (NULL); 1327 } 1328 len = strlen(home) + strlen(LESSHISTFILE) + 2; 1329 name = (char *) ecalloc(len, sizeof(char)); 1330 SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1331 return (name); 1332} 1333#endif /* CMD_HISTORY */ 1334 1335/* 1336 * Initialize history from a .lesshist file. 1337 */ 1338 public void 1339init_cmdhist() 1340{ 1341#if CMD_HISTORY 1342 struct mlist *ml = NULL; 1343 char line[CMDBUF_SIZE]; 1344 char *filename; 1345 FILE *f; 1346 char *p; 1347 1348 filename = histfile_name(); 1349 if (filename == NULL) 1350 return; 1351 f = fopen(filename, "r"); 1352 free(filename); 1353 if (f == NULL) 1354 return; 1355 if (fgets(line, sizeof(line), f) == NULL || 1356 strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1357 { 1358 fclose(f); 1359 return; 1360 } 1361 while (fgets(line, sizeof(line), f) != NULL) 1362 { 1363 for (p = line; *p != '\0'; p++) 1364 { 1365 if (*p == '\n' || *p == '\r') 1366 { 1367 *p = '\0'; 1368 break; 1369 } 1370 } 1371 if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1372 ml = &mlist_search; 1373#if SHELL_ESCAPE || PIPEC 1374 else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1375 ml = &mlist_shell; 1376#endif 1377 else if (*line == '"') 1378 { 1379 if (ml != NULL) 1380 cmd_addhist(ml, line+1); 1381 } 1382 } 1383 fclose(f); 1384#endif /* CMD_HISTORY */ 1385} 1386 1387/* 1388 * 1389 */ 1390#if CMD_HISTORY 1391 static void 1392save_mlist(ml, f) 1393 struct mlist *ml; 1394 FILE *f; 1395{ 1396 int histsize = 0; 1397 int n; 1398 char *s; 1399 1400 s = lgetenv("LESSHISTSIZE"); 1401 if (s != NULL) 1402 histsize = atoi(s); 1403 if (histsize == 0) 1404 histsize = 100; 1405 1406 ml = ml->prev; 1407 for (n = 0; n < histsize; n++) 1408 { 1409 if (ml->string == NULL) 1410 break; 1411 ml = ml->prev; 1412 } 1413 for (ml = ml->next; ml->string != NULL; ml = ml->next) 1414 fprintf(f, "\"%s\n", ml->string); 1415} 1416#endif /* CMD_HISTORY */ 1417 1418/* 1419 * 1420 */ 1421 public void 1422save_cmdhist() 1423{ 1424#if CMD_HISTORY 1425 char *filename; 1426 FILE *f; 1427 1428 filename = histfile_name(); 1429 if (filename == NULL) 1430 return; 1431 f = fopen(filename, "w"); 1432 free(filename); 1433 if (f == NULL) 1434 return; 1435#if HAVE_FCHMOD 1436 /* Make history file readable only by owner. */ 1437 fchmod(fileno(f), 0600); 1438#endif 1439 1440 fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1441 1442 fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1443 save_mlist(&mlist_search, f); 1444 1445#if SHELL_ESCAPE || PIPEC 1446 fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1447 save_mlist(&mlist_shell, f); 1448#endif 1449 1450 fclose(f); 1451#endif /* CMD_HISTORY */ 1452} 1453