"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/cmdbuf.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  * Functions which manipulate the command buffer.
   14  * Used only by command() and related functions.
   15  */
   16 
   17 #include "less.h"
   18 #include "cmd.h"
   19 #include "charset.h"
   20 #if HAVE_STAT
   21 #include <sys/stat.h>
   22 #endif
   23 
   24 extern int sc_width;
   25 extern int utf_mode;
   26 
   27 static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
   28 static int cmd_col;		/* Current column of the cursor */
   29 static int prompt_col;		/* Column of cursor just after prompt */
   30 static char *cp;		/* Pointer into cmdbuf */
   31 static int cmd_offset;		/* Index into cmdbuf of first displayed char */
   32 static int literal;		/* Next input char should not be interpreted */
   33 
   34 #if TAB_COMPLETE_FILENAME
   35 static int cmd_complete();
   36 /*
   37  * These variables are statics used by cmd_complete.
   38  */
   39 static int in_completion = 0;
   40 static char *tk_text;
   41 static char *tk_original;
   42 static char *tk_ipoint;
   43 static char *tk_trial;
   44 static struct textlist tk_tlist;
   45 #endif
   46 
   47 static int cmd_left();
   48 static int cmd_right();
   49 
   50 #if SPACES_IN_FILENAMES
   51 public char openquote = '"';
   52 public char closequote = '"';
   53 #endif
   54 
   55 #if CMD_HISTORY
   56 
   57 /* History file */
   58 #define HISTFILE_FIRST_LINE      ".less-history-file:"
   59 #define HISTFILE_SEARCH_SECTION  ".search"
   60 #define HISTFILE_SHELL_SECTION   ".shell"
   61 
   62 /*
   63  * A mlist structure represents a command history.
   64  */
   65 struct mlist
   66 {
   67 	struct mlist *next;
   68 	struct mlist *prev;
   69 	struct mlist *curr_mp;
   70 	char *string;
   71 	int modified;
   72 };
   73 
   74 /*
   75  * These are the various command histories that exist.
   76  */
   77 struct mlist mlist_search =
   78 	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
   79 public void * constant ml_search = (void *) &mlist_search;
   80 
   81 struct mlist mlist_examine =
   82 	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
   83 public void * constant ml_examine = (void *) &mlist_examine;
   84 
   85 #if SHELL_ESCAPE || PIPEC
   86 struct mlist mlist_shell =
   87 	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
   88 public void * constant ml_shell = (void *) &mlist_shell;
   89 #endif
   90 
   91 #else /* CMD_HISTORY */
   92 
   93 /* If CMD_HISTORY is off, these are just flags. */
   94 public void * constant ml_search = (void *)1;
   95 public void * constant ml_examine = (void *)2;
   96 #if SHELL_ESCAPE || PIPEC
   97 public void * constant ml_shell = (void *)3;
   98 #endif
   99 
  100 #endif /* CMD_HISTORY */
  101 
  102 /*
  103  * History for the current command.
  104  */
  105 static struct mlist *curr_mlist = NULL;
  106 static int curr_cmdflags;
  107 
  108 static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
  109 static int cmd_mbc_buf_len;
  110 static int cmd_mbc_buf_index;
  111 
  112 
  113 /*
  114  * Reset command buffer (to empty).
  115  */
  116 	public void
  117 cmd_reset()
  118 {
  119 	cp = cmdbuf;
  120 	*cp = '\0';
  121 	cmd_col = 0;
  122 	cmd_offset = 0;
  123 	literal = 0;
  124 	cmd_mbc_buf_len = 0;
  125 }
  126 
  127 /*
  128  * Clear command line.
  129  */
  130 	public void
  131 clear_cmd()
  132 {
  133 	cmd_col = prompt_col = 0;
  134 	cmd_mbc_buf_len = 0;
  135 }
  136 
  137 /*
  138  * Display a string, usually as a prompt for input into the command buffer.
  139  */
  140 	public void
  141 cmd_putstr(s)
  142 	char *s;
  143 {
  144 	LWCHAR prev_ch = 0;
  145 	LWCHAR ch;
  146 	char *endline = s + strlen(s);
  147 	while (*s != '\0')
  148 	{
  149 		char *ns = s;
  150 		ch = step_char(&ns, +1, endline);
  151 		while (s < ns)
  152 			putchr(*s++);
  153 		if (!utf_mode)
  154 		{
  155 			cmd_col++;
  156 			prompt_col++;
  157 		} else if (!is_composing_char(ch) &&
  158 		           !is_combining_char(prev_ch, ch))
  159 		{
  160 			int width = is_wide_char(ch) ? 2 : 1;
  161 			cmd_col += width;
  162 			prompt_col += width;
  163 		}
  164 		prev_ch = ch;
  165 	}
  166 }
  167 
  168 /*
  169  * How many characters are in the command buffer?
  170  */
  171 	public int
  172 len_cmdbuf()
  173 {
  174 	char *s = cmdbuf;
  175 	char *endline = s + strlen(s);
  176 	int len = 0;
  177 
  178 	while (*s != '\0')
  179 	{
  180 		step_char(&s, +1, endline);
  181 		len++;
  182 	}
  183 	return (len);
  184 }
  185 
  186 /*
  187  * Common part of cmd_step_right() and cmd_step_left().
  188  */
  189 	static char *
  190 cmd_step_common(p, ch, len, pwidth, bswidth)
  191 	char *p;
  192 	LWCHAR ch;
  193 	int len;
  194 	int *pwidth;
  195 	int *bswidth;
  196 {
  197 	char *pr;
  198 
  199 	if (len == 1)
  200 	{
  201 		pr = prchar((int) ch);
  202 		if (pwidth != NULL || bswidth != NULL)
  203 		{
  204 			int len = strlen(pr);
  205 			if (pwidth != NULL)
  206 				*pwidth = len;
  207 			if (bswidth != NULL)
  208 				*bswidth = len;
  209 		}
  210 	} else
  211 	{
  212 		pr = prutfchar(ch);
  213 		if (pwidth != NULL || bswidth != NULL)
  214 		{
  215 			if (is_composing_char(ch))
  216 			{
  217 				if (pwidth != NULL)
  218 					*pwidth = 0;
  219 				if (bswidth != NULL)
  220 					*bswidth = 0;
  221 			} else if (is_ubin_char(ch))
  222 			{
  223 				int len = strlen(pr);
  224 				if (pwidth != NULL)
  225 					*pwidth = len;
  226 				if (bswidth != NULL)
  227 					*bswidth = len;
  228 			} else
  229 			{
  230 				LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
  231 				if (is_combining_char(prev_ch, ch))
  232 				{
  233 					if (pwidth != NULL)
  234 						*pwidth = 0;
  235 					if (bswidth != NULL)
  236 						*bswidth = 0;
  237 				} else
  238 				{
  239 					if (pwidth != NULL)
  240 						*pwidth	= is_wide_char(ch)
  241 							?	2
  242 							:	1;
  243 					if (bswidth != NULL)
  244 						*bswidth = 1;
  245 				}
  246 			}
  247 		}
  248 	}
  249 
  250 	return (pr);
  251 }
  252 
  253 /*
  254  * Step a pointer one character right in the command buffer.
  255  */
  256 	static char *
  257 cmd_step_right(pp, pwidth, bswidth)
  258 	char **pp;
  259 	int *pwidth;
  260 	int *bswidth;
  261 {
  262 	char *p = *pp;
  263 	LWCHAR ch = step_char(pp, +1, p + strlen(p));
  264 
  265 	return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
  266 }
  267 
  268 /*
  269  * Step a pointer one character left in the command buffer.
  270  */
  271 	static char *
  272 cmd_step_left(pp, pwidth, bswidth)
  273 	char **pp;
  274 	int *pwidth;
  275 	int *bswidth;
  276 {
  277 	char *p = *pp;
  278 	LWCHAR ch = step_char(pp, -1, cmdbuf);
  279 
  280 	return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
  281 }
  282 
  283 /*
  284  * Repaint the line from cp onwards.
  285  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
  286  */
  287 	static void
  288 cmd_repaint(old_cp)
  289 	char *old_cp;
  290 {
  291 	/*
  292 	 * Repaint the line from the current position.
  293 	 */
  294 	clear_eol();
  295 	while (*cp != '\0')
  296 	{
  297 		char *np = cp;
  298 		int width;
  299 		char *pr = cmd_step_right(&np, &width, NULL);
  300 		if (cmd_col + width >= sc_width)
  301 			break;
  302 		cp = np;
  303 		putstr(pr);
  304 		cmd_col += width;
  305 	}
  306 	while (*cp != '\0')
  307 	{
  308 		char *np = cp;
  309 		int width;
  310 		char *pr = cmd_step_right(&np, &width, NULL);
  311 		if (width > 0)
  312 			break;
  313 		cp = np;
  314 		putstr(pr);
  315 	}
  316 
  317 	/*
  318 	 * Back up the cursor to the correct position.
  319 	 */
  320 	while (cp > old_cp)
  321 		cmd_left();
  322 }
  323 
  324 /*
  325  * Put the cursor at "home" (just after the prompt),
  326  * and set cp to the corresponding char in cmdbuf.
  327  */
  328 	static void
  329 cmd_home()
  330 {
  331 	while (cmd_col > prompt_col)
  332 	{
  333 		int width, bswidth;
  334 
  335 		cmd_step_left(&cp, &width, &bswidth);
  336 		while (bswidth-- > 0)
  337 			putbs();
  338 		cmd_col -= width;
  339 	}
  340 
  341 	cp = &cmdbuf[cmd_offset];
  342 }
  343 
  344 /*
  345  * Shift the cmdbuf display left a half-screen.
  346  */
  347 	static void
  348 cmd_lshift()
  349 {
  350 	char *s;
  351 	char *save_cp;
  352 	int cols;
  353 
  354 	/*
  355 	 * Start at the first displayed char, count how far to the
  356 	 * right we'd have to move to reach the center of the screen.
  357 	 */
  358 	s = cmdbuf + cmd_offset;
  359 	cols = 0;
  360 	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
  361 	{
  362 		int width;
  363 		cmd_step_right(&s, &width, NULL);
  364 		cols += width;
  365 	}
  366 	while (*s != '\0')
  367 	{
  368 		int width;
  369 		char *ns = s;
  370 		cmd_step_right(&ns, &width, NULL);
  371 		if (width > 0)
  372 			break;
  373 		s = ns;
  374 	}
  375 
  376 	cmd_offset = s - cmdbuf;
  377 	save_cp = cp;
  378 	cmd_home();
  379 	cmd_repaint(save_cp);
  380 }
  381 
  382 /*
  383  * Shift the cmdbuf display right a half-screen.
  384  */
  385 	static void
  386 cmd_rshift()
  387 {
  388 	char *s;
  389 	char *save_cp;
  390 	int cols;
  391 
  392 	/*
  393 	 * Start at the first displayed char, count how far to the
  394 	 * left we'd have to move to traverse a half-screen width
  395 	 * of displayed characters.
  396 	 */
  397 	s = cmdbuf + cmd_offset;
  398 	cols = 0;
  399 	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
  400 	{
  401 		int width;
  402 		cmd_step_left(&s, &width, NULL);
  403 		cols += width;
  404 	}
  405 
  406 	cmd_offset = s - cmdbuf;
  407 	save_cp = cp;
  408 	cmd_home();
  409 	cmd_repaint(save_cp);
  410 }
  411 
  412 /*
  413  * Move cursor right one character.
  414  */
  415 	static int
  416 cmd_right()
  417 {
  418 	char *pr;
  419 	char *ncp;
  420 	int width;
  421 
  422 	if (*cp == '\0')
  423 	{
  424 		/* Already at the end of the line. */
  425 		return (CC_OK);
  426 	}
  427 	ncp = cp;
  428 	pr = cmd_step_right(&ncp, &width, NULL);
  429 	if (cmd_col + width >= sc_width)
  430 		cmd_lshift();
  431 	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
  432 		cmd_lshift();
  433 	cp = ncp;
  434 	cmd_col += width;
  435 	putstr(pr);
  436 	while (*cp != '\0')
  437 	{
  438 		pr = cmd_step_right(&ncp, &width, NULL);
  439 		if (width > 0)
  440 			break;
  441 		putstr(pr);
  442 		cp = ncp;
  443 	}
  444 	return (CC_OK);
  445 }
  446 
  447 /*
  448  * Move cursor left one character.
  449  */
  450 	static int
  451 cmd_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
  481 cmd_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
  517 cmd_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
  565 cmd_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
  584 cmd_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
  610 cmd_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
  636 cmd_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
  661 set_mlist(mlist, cmdflags)
  662 	void *mlist;
  663 	int cmdflags;
  664 {
  665 #if CMD_HISTORY
  666 	curr_mlist = (struct mlist *) mlist;
  667 	curr_cmdflags = cmdflags;
  668 
  669 	/* Make sure the next up-arrow moves to the last string in the mlist. */
  670 	if (curr_mlist != NULL)
  671 		curr_mlist->curr_mp = curr_mlist;
  672 #endif
  673 }
  674 
  675 #if CMD_HISTORY
  676 /*
  677  * Move up or down in the currently selected command history list.
  678  */
  679 	static int
  680 cmd_updown(action)
  681 	int action;
  682 {
  683 	char *s;
  684 
  685 	if (curr_mlist == NULL)
  686 	{
  687 		/*
  688 		 * The current command has no history list.
  689 		 */
  690 		bell();
  691 		return (CC_OK);
  692 	}
  693 	cmd_home();
  694 	clear_eol();
  695 	/*
  696 	 * Move curr_mp to the next/prev entry.
  697 	 */
  698 	if (action == EC_UP)
  699 		curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
  700 	else
  701 		curr_mlist->curr_mp = curr_mlist->curr_mp->next;
  702 	/*
  703 	 * Copy the entry into cmdbuf and echo it on the screen.
  704 	 */
  705 	s = curr_mlist->curr_mp->string;
  706 	if (s == NULL)
  707 		s = "";
  708 	strcpy(cmdbuf, s);
  709 	for (cp = cmdbuf;  *cp != '\0';  )
  710 		cmd_right();
  711 	return (CC_OK);
  712 }
  713 #endif
  714 
  715 /*
  716  * Add a string to a history list.
  717  */
  718 	public void
  719 cmd_addhist(mlist, cmd)
  720 	struct mlist *mlist;
  721 	char *cmd;
  722 {
  723 #if CMD_HISTORY
  724 	struct mlist *ml;
  725 
  726 	/*
  727 	 * Don't save a trivial command.
  728 	 */
  729 	if (strlen(cmd) == 0)
  730 		return;
  731 
  732 	/*
  733 	 * Save the command unless it's a duplicate of the
  734 	 * last command in the history.
  735 	 */
  736 	ml = mlist->prev;
  737 	if (ml == mlist || strcmp(ml->string, cmd) != 0)
  738 	{
  739 		/*
  740 		 * Did not find command in history.
  741 		 * Save the command and put it at the end of the history list.
  742 		 */
  743 		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
  744 		ml->string = save(cmd);
  745 		ml->next = mlist;
  746 		ml->prev = mlist->prev;
  747 		mlist->prev->next = ml;
  748 		mlist->prev = ml;
  749 	}
  750 	/*
  751 	 * Point to the cmd just after the just-accepted command.
  752 	 * Thus, an UPARROW will always retrieve the previous command.
  753 	 */
  754 	mlist->curr_mp = ml->next;
  755 #endif
  756 }
  757 
  758 /*
  759  * Accept the command in the command buffer.
  760  * Add it to the currently selected history list.
  761  */
  762 	public void
  763 cmd_accept()
  764 {
  765 #if CMD_HISTORY
  766 	/*
  767 	 * Nothing to do if there is no currently selected history list.
  768 	 */
  769 	if (curr_mlist == NULL)
  770 		return;
  771 	cmd_addhist(curr_mlist, cmdbuf);
  772 	curr_mlist->modified = 1;
  773 #endif
  774 }
  775 
  776 /*
  777  * Try to perform a line-edit function on the command buffer,
  778  * using a specified char as a line-editing command.
  779  * Returns:
  780  *	CC_PASS	The char does not invoke a line edit function.
  781  *	CC_OK	Line edit function done.
  782  *	CC_QUIT	The char requests the current command to be aborted.
  783  */
  784 	static int
  785 cmd_edit(c)
  786 	int c;
  787 {
  788 	int action;
  789 	int flags;
  790 
  791 #if TAB_COMPLETE_FILENAME
  792 #define	not_in_completion()	in_completion = 0
  793 #else
  794 #define	not_in_completion()
  795 #endif
  796 
  797 	/*
  798 	 * See if the char is indeed a line-editing command.
  799 	 */
  800 	flags = 0;
  801 #if CMD_HISTORY
  802 	if (curr_mlist == NULL)
  803 		/*
  804 		 * No current history; don't accept history manipulation cmds.
  805 		 */
  806 		flags |= EC_NOHISTORY;
  807 #endif
  808 #if TAB_COMPLETE_FILENAME
  809 	if (curr_mlist == ml_search)
  810 		/*
  811 		 * In a search command; don't accept file-completion cmds.
  812 		 */
  813 		flags |= EC_NOCOMPLETE;
  814 #endif
  815 
  816 	action = editchar(c, flags);
  817 
  818 	switch (action)
  819 	{
  820 	case EC_RIGHT:
  821 		not_in_completion();
  822 		return (cmd_right());
  823 	case EC_LEFT:
  824 		not_in_completion();
  825 		return (cmd_left());
  826 	case EC_W_RIGHT:
  827 		not_in_completion();
  828 		while (*cp != '\0' && *cp != ' ')
  829 			cmd_right();
  830 		while (*cp == ' ')
  831 			cmd_right();
  832 		return (CC_OK);
  833 	case EC_W_LEFT:
  834 		not_in_completion();
  835 		while (cp > cmdbuf && cp[-1] == ' ')
  836 			cmd_left();
  837 		while (cp > cmdbuf && cp[-1] != ' ')
  838 			cmd_left();
  839 		return (CC_OK);
  840 	case EC_HOME:
  841 		not_in_completion();
  842 		cmd_offset = 0;
  843 		cmd_home();
  844 		cmd_repaint(cp);
  845 		return (CC_OK);
  846 	case EC_END:
  847 		not_in_completion();
  848 		while (*cp != '\0')
  849 			cmd_right();
  850 		return (CC_OK);
  851 	case EC_INSERT:
  852 		not_in_completion();
  853 		return (CC_OK);
  854 	case EC_BACKSPACE:
  855 		not_in_completion();
  856 		return (cmd_erase());
  857 	case EC_LINEKILL:
  858 		not_in_completion();
  859 		return (cmd_kill());
  860 	case EC_W_BACKSPACE:
  861 		not_in_completion();
  862 		return (cmd_werase());
  863 	case EC_DELETE:
  864 		not_in_completion();
  865 		return (cmd_delete());
  866 	case EC_W_DELETE:
  867 		not_in_completion();
  868 		return (cmd_wdelete());
  869 	case EC_LITERAL:
  870 		literal = 1;
  871 		return (CC_OK);
  872 #if CMD_HISTORY
  873 	case EC_UP:
  874 	case EC_DOWN:
  875 		not_in_completion();
  876 		return (cmd_updown(action));
  877 #endif
  878 #if TAB_COMPLETE_FILENAME
  879 	case EC_F_COMPLETE:
  880 	case EC_B_COMPLETE:
  881 	case EC_EXPAND:
  882 		return (cmd_complete(action));
  883 #endif
  884 	case EC_NOACTION:
  885 		return (CC_OK);
  886 	default:
  887 		not_in_completion();
  888 		return (CC_PASS);
  889 	}
  890 }
  891 
  892 #if TAB_COMPLETE_FILENAME
  893 /*
  894  * Insert a string into the command buffer, at the current position.
  895  */
  896 	static int
  897 cmd_istr(str)
  898 	char *str;
  899 {
  900 	char *s;
  901 	int action;
  902 	char *endline = str + strlen(str);
  903 
  904 	for (s = str;  *s != '\0';  )
  905 	{
  906 		char *os = s;
  907 		step_char(&s, +1, endline);
  908 		action = cmd_ichar(os, s - os);
  909 		if (action != CC_OK)
  910 		{
  911 			bell();
  912 			return (action);
  913 		}
  914 	}
  915 	return (CC_OK);
  916 }
  917 
  918 /*
  919  * Find the beginning and end of the "current" word.
  920  * This is the word which the cursor (cp) is inside or at the end of.
  921  * Return pointer to the beginning of the word and put the
  922  * cursor at the end of the word.
  923  */
  924 	static char *
  925 delimit_word()
  926 {
  927 	char *word;
  928 #if SPACES_IN_FILENAMES
  929 	char *p;
  930 	int delim_quoted = 0;
  931 	int meta_quoted = 0;
  932 	char *esc = get_meta_escape();
  933 	int esclen = strlen(esc);
  934 #endif
  935 
  936 	/*
  937 	 * Move cursor to end of word.
  938 	 */
  939 	if (*cp != ' ' && *cp != '\0')
  940 	{
  941 		/*
  942 		 * Cursor is on a nonspace.
  943 		 * Move cursor right to the next space.
  944 		 */
  945 		while (*cp != ' ' && *cp != '\0')
  946 			cmd_right();
  947 	} else if (cp > cmdbuf && cp[-1] != ' ')
  948 	{
  949 		/*
  950 		 * Cursor is on a space, and char to the left is a nonspace.
  951 		 * We're already at the end of the word.
  952 		 */
  953 		;
  954 #if 0
  955 	} else
  956 	{
  957 		/*
  958 		 * Cursor is on a space and char to the left is a space.
  959 		 * Huh? There's no word here.
  960 		 */
  961 		return (NULL);
  962 #endif
  963 	}
  964 	/*
  965 	 * Find the beginning of the word which the cursor is in.
  966 	 */
  967 	if (cp == cmdbuf)
  968 		return (NULL);
  969 #if SPACES_IN_FILENAMES
  970 	/*
  971 	 * If we have an unbalanced quote (that is, an open quote
  972 	 * without a corresponding close quote), we return everything
  973 	 * from the open quote, including spaces.
  974 	 */
  975 	for (word = cmdbuf;  word < cp;  word++)
  976 		if (*word != ' ')
  977 			break;
  978 	if (word >= cp)
  979 		return (cp);
  980 	for (p = cmdbuf;  p < cp;  p++)
  981 	{
  982 		if (meta_quoted)
  983 		{
  984 			meta_quoted = 0;
  985 		} else if (esclen > 0 && p + esclen < cp &&
  986 		           strncmp(p, esc, esclen) == 0)
  987 		{
  988 			meta_quoted = 1;
  989 			p += esclen - 1;
  990 		} else if (delim_quoted)
  991 		{
  992 			if (*p == closequote)
  993 				delim_quoted = 0;
  994 		} else /* (!delim_quoted) */
  995 		{
  996 			if (*p == openquote)
  997 				delim_quoted = 1;
  998 			else if (*p == ' ')
  999 				word = p+1;
 1000 		}
 1001 	}
 1002 #endif
 1003 	return (word);
 1004 }
 1005 
 1006 /*
 1007  * Set things up to enter completion mode.
 1008  * Expand the word under the cursor into a list of filenames
 1009  * which start with that word, and set tk_text to that list.
 1010  */
 1011 	static void
 1012 init_compl()
 1013 {
 1014 	char *word;
 1015 	char c;
 1016 
 1017 	/*
 1018 	 * Get rid of any previous tk_text.
 1019 	 */
 1020 	if (tk_text != NULL)
 1021 	{
 1022 		free(tk_text);
 1023 		tk_text = NULL;
 1024 	}
 1025 	/*
 1026 	 * Find the original (uncompleted) word in the command buffer.
 1027 	 */
 1028 	word = delimit_word();
 1029 	if (word == NULL)
 1030 		return;
 1031 	/*
 1032 	 * Set the insertion point to the point in the command buffer
 1033 	 * where the original (uncompleted) word now sits.
 1034 	 */
 1035 	tk_ipoint = word;
 1036 	/*
 1037 	 * Save the original (uncompleted) word
 1038 	 */
 1039 	if (tk_original != NULL)
 1040 		free(tk_original);
 1041 	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
 1042 	strncpy(tk_original, word, cp-word);
 1043 	/*
 1044 	 * Get the expanded filename.
 1045 	 * This may result in a single filename, or
 1046 	 * a blank-separated list of filenames.
 1047 	 */
 1048 	c = *cp;
 1049 	*cp = '\0';
 1050 	if (*word != openquote)
 1051 	{
 1052 		tk_text = fcomplete(word);
 1053 	} else
 1054 	{
 1055 		char *qword = shell_quote(word+1);
 1056 		if (qword == NULL)
 1057 			tk_text = fcomplete(word+1);
 1058 		else
 1059 		{
 1060 			tk_text = fcomplete(qword);
 1061 			free(qword);
 1062 		}
 1063 	}
 1064 	*cp = c;
 1065 }
 1066 
 1067 /*
 1068  * Return the next word in the current completion list.
 1069  */
 1070 	static char *
 1071 next_compl(action, prev)
 1072 	int action;
 1073 	char *prev;
 1074 {
 1075 	switch (action)
 1076 	{
 1077 	case EC_F_COMPLETE:
 1078 		return (forw_textlist(&tk_tlist, prev));
 1079 	case EC_B_COMPLETE:
 1080 		return (back_textlist(&tk_tlist, prev));
 1081 	}
 1082 	/* Cannot happen */
 1083 	return ("?");
 1084 }
 1085 
 1086 /*
 1087  * Complete the filename before (or under) the cursor.
 1088  * cmd_complete may be called multiple times.  The global in_completion
 1089  * remembers whether this call is the first time (create the list),
 1090  * or a subsequent time (step thru the list).
 1091  */
 1092 	static int
 1093 cmd_complete(action)
 1094 	int action;
 1095 {
 1096 	char *s;
 1097 
 1098 	if (!in_completion || action == EC_EXPAND)
 1099 	{
 1100 		/*
 1101 		 * Expand the word under the cursor and
 1102 		 * use the first word in the expansion
 1103 		 * (or the entire expansion if we're doing EC_EXPAND).
 1104 		 */
 1105 		init_compl();
 1106 		if (tk_text == NULL)
 1107 		{
 1108 			bell();
 1109 			return (CC_OK);
 1110 		}
 1111 		if (action == EC_EXPAND)
 1112 		{
 1113 			/*
 1114 			 * Use the whole list.
 1115 			 */
 1116 			tk_trial = tk_text;
 1117 		} else
 1118 		{
 1119 			/*
 1120 			 * Use the first filename in the list.
 1121 			 */
 1122 			in_completion = 1;
 1123 			init_textlist(&tk_tlist, tk_text);
 1124 			tk_tri