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