cmdbuf.c revision 60786
160786Sps/*
260786Sps * Copyright (C) 1984-2000  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
760786Sps * For more information about less, or for information on how to
860786Sps * contact the author, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * Functions which manipulate the command buffer.
1460786Sps * Used only by command() and related functions.
1560786Sps */
1660786Sps
1760786Sps#include "less.h"
1860786Sps#include "cmd.h"
1960786Sps
2060786Spsextern int sc_width;
2160786Sps
2260786Spsstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
2360786Spsstatic int cmd_col;		/* Current column of the cursor */
2460786Spsstatic int prompt_col;		/* Column of cursor just after prompt */
2560786Spsstatic char *cp;		/* Pointer into cmdbuf */
2660786Spsstatic int cmd_offset;		/* Index into cmdbuf of first displayed char */
2760786Spsstatic int literal;		/* Next input char should not be interpreted */
2860786Sps
2960786Sps#if TAB_COMPLETE_FILENAME
3060786Spsstatic int cmd_complete();
3160786Sps/*
3260786Sps * These variables are statics used by cmd_complete.
3360786Sps */
3460786Spsstatic int in_completion = 0;
3560786Spsstatic char *tk_text;
3660786Spsstatic char *tk_original;
3760786Spsstatic char *tk_ipoint;
3860786Spsstatic char *tk_trial;
3960786Spsstatic struct textlist tk_tlist;
4060786Sps#endif
4160786Sps
4260786Spsstatic int cmd_left();
4360786Spsstatic int cmd_right();
4460786Sps
4560786Sps#if SPACES_IN_FILENAMES
4660786Spspublic char openquote = '"';
4760786Spspublic char closequote = '"';
4860786Sps#endif
4960786Sps
5060786Sps#if CMD_HISTORY
5160786Sps/*
5260786Sps * A mlist structure represents a command history.
5360786Sps */
5460786Spsstruct mlist
5560786Sps{
5660786Sps	struct mlist *next;
5760786Sps	struct mlist *prev;
5860786Sps	struct mlist *curr_mp;
5960786Sps	char *string;
6060786Sps};
6160786Sps
6260786Sps/*
6360786Sps * These are the various command histories that exist.
6460786Sps */
6560786Spsstruct mlist mlist_search =
6660786Sps	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL };
6760786Spspublic void constant *ml_search = (void *) &mlist_search;
6860786Sps
6960786Spsstruct mlist mlist_examine =
7060786Sps	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL };
7160786Spspublic void constant *ml_examine = (void *) &mlist_examine;
7260786Sps
7360786Sps#if SHELL_ESCAPE || PIPEC
7460786Spsstruct mlist mlist_shell =
7560786Sps	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL };
7660786Spspublic void constant *ml_shell = (void *) &mlist_shell;
7760786Sps#endif
7860786Sps
7960786Sps#else /* CMD_HISTORY */
8060786Sps
8160786Sps/* If CMD_HISTORY is off, these are just flags. */
8260786Spspublic void constant *ml_search = (void *)1;
8360786Spspublic void constant *ml_examine = (void *)2;
8460786Sps#if SHELL_ESCAPE || PIPEC
8560786Spspublic void constant *ml_shell = (void *)3;
8660786Sps#endif
8760786Sps
8860786Sps#endif /* CMD_HISTORY */
8960786Sps
9060786Sps/*
9160786Sps * History for the current command.
9260786Sps */
9360786Spsstatic struct mlist *curr_mlist = NULL;
9460786Spsstatic int curr_cmdflags;
9560786Sps
9660786Sps
9760786Sps/*
9860786Sps * Reset command buffer (to empty).
9960786Sps */
10060786Sps	public void
10160786Spscmd_reset()
10260786Sps{
10360786Sps	cp = cmdbuf;
10460786Sps	*cp = '\0';
10560786Sps	cmd_col = 0;
10660786Sps	cmd_offset = 0;
10760786Sps	literal = 0;
10860786Sps}
10960786Sps
11060786Sps/*
11160786Sps * Clear command line on display.
11260786Sps */
11360786Sps	public void
11460786Spsclear_cmd()
11560786Sps{
11660786Sps	clear_bot();
11760786Sps	cmd_col = prompt_col = 0;
11860786Sps}
11960786Sps
12060786Sps/*
12160786Sps * Display a string, usually as a prompt for input into the command buffer.
12260786Sps */
12360786Sps	public void
12460786Spscmd_putstr(s)
12560786Sps	char *s;
12660786Sps{
12760786Sps	putstr(s);
12860786Sps	cmd_col += strlen(s);
12960786Sps	prompt_col += strlen(s);
13060786Sps}
13160786Sps
13260786Sps/*
13360786Sps * How many characters are in the command buffer?
13460786Sps */
13560786Sps	public int
13660786Spslen_cmdbuf()
13760786Sps{
13860786Sps	return (strlen(cmdbuf));
13960786Sps}
14060786Sps
14160786Sps/*
14260786Sps * Repaint the line from cp onwards.
14360786Sps * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
14460786Sps */
14560786Sps	static void
14660786Spscmd_repaint(old_cp)
14760786Sps	char *old_cp;
14860786Sps{
14960786Sps	char *p;
15060786Sps
15160786Sps	/*
15260786Sps	 * Repaint the line from the current position.
15360786Sps	 */
15460786Sps	clear_eol();
15560786Sps	for ( ;  *cp != '\0';  cp++)
15660786Sps	{
15760786Sps		p = prchar(*cp);
15860786Sps		if (cmd_col + strlen(p) >= sc_width)
15960786Sps			break;
16060786Sps		putstr(p);
16160786Sps		cmd_col += strlen(p);
16260786Sps	}
16360786Sps
16460786Sps	/*
16560786Sps	 * Back up the cursor to the correct position.
16660786Sps	 */
16760786Sps	while (cp > old_cp)
16860786Sps		cmd_left();
16960786Sps}
17060786Sps
17160786Sps/*
17260786Sps * Put the cursor at "home" (just after the prompt),
17360786Sps * and set cp to the corresponding char in cmdbuf.
17460786Sps */
17560786Sps	static void
17660786Spscmd_home()
17760786Sps{
17860786Sps	while (cmd_col > prompt_col)
17960786Sps	{
18060786Sps		putbs();
18160786Sps		cmd_col--;
18260786Sps	}
18360786Sps
18460786Sps	cp = &cmdbuf[cmd_offset];
18560786Sps}
18660786Sps
18760786Sps/*
18860786Sps * Shift the cmdbuf display left a half-screen.
18960786Sps */
19060786Sps	static void
19160786Spscmd_lshift()
19260786Sps{
19360786Sps	char *s;
19460786Sps	char *save_cp;
19560786Sps	int cols;
19660786Sps
19760786Sps	/*
19860786Sps	 * Start at the first displayed char, count how far to the
19960786Sps	 * right we'd have to move to reach the center of the screen.
20060786Sps	 */
20160786Sps	s = cmdbuf + cmd_offset;
20260786Sps	cols = 0;
20360786Sps	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
20460786Sps		cols += strlen(prchar(*s++));
20560786Sps
20660786Sps	cmd_offset = s - cmdbuf;
20760786Sps	save_cp = cp;
20860786Sps	cmd_home();
20960786Sps	cmd_repaint(save_cp);
21060786Sps}
21160786Sps
21260786Sps/*
21360786Sps * Shift the cmdbuf display right a half-screen.
21460786Sps */
21560786Sps	static void
21660786Spscmd_rshift()
21760786Sps{
21860786Sps	char *s;
21960786Sps	char *p;
22060786Sps	char *save_cp;
22160786Sps	int cols;
22260786Sps
22360786Sps	/*
22460786Sps	 * Start at the first displayed char, count how far to the
22560786Sps	 * left we'd have to move to traverse a half-screen width
22660786Sps	 * of displayed characters.
22760786Sps	 */
22860786Sps	s = cmdbuf + cmd_offset;
22960786Sps	cols = 0;
23060786Sps	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
23160786Sps	{
23260786Sps		p = prchar(*--s);
23360786Sps		cols += strlen(p);
23460786Sps	}
23560786Sps
23660786Sps	cmd_offset = s - cmdbuf;
23760786Sps	save_cp = cp;
23860786Sps	cmd_home();
23960786Sps	cmd_repaint(save_cp);
24060786Sps}
24160786Sps
24260786Sps/*
24360786Sps * Move cursor right one character.
24460786Sps */
24560786Sps	static int
24660786Spscmd_right()
24760786Sps{
24860786Sps	char *p;
24960786Sps
25060786Sps	if (*cp == '\0')
25160786Sps	{
25260786Sps		/*
25360786Sps		 * Already at the end of the line.
25460786Sps		 */
25560786Sps		return (CC_OK);
25660786Sps	}
25760786Sps	p = prchar(*cp);
25860786Sps	if (cmd_col + strlen(p) >= sc_width)
25960786Sps		cmd_lshift();
26060786Sps	else if (cmd_col + strlen(p) == sc_width - 1 && cp[1] != '\0')
26160786Sps		cmd_lshift();
26260786Sps	cp++;
26360786Sps	putstr(p);
26460786Sps	cmd_col += strlen(p);
26560786Sps	return (CC_OK);
26660786Sps}
26760786Sps
26860786Sps/*
26960786Sps * Move cursor left one character.
27060786Sps */
27160786Sps	static int
27260786Spscmd_left()
27360786Sps{
27460786Sps	char *p;
27560786Sps
27660786Sps	if (cp <= cmdbuf)
27760786Sps	{
27860786Sps		/* Already at the beginning of the line */
27960786Sps		return (CC_OK);
28060786Sps	}
28160786Sps	p = prchar(cp[-1]);
28260786Sps	if (cmd_col < prompt_col + strlen(p))
28360786Sps		cmd_rshift();
28460786Sps	cp--;
28560786Sps	cmd_col -= strlen(p);
28660786Sps	while (*p++ != '\0')
28760786Sps		putbs();
28860786Sps	return (CC_OK);
28960786Sps}
29060786Sps
29160786Sps/*
29260786Sps * Insert a char into the command buffer, at the current position.
29360786Sps */
29460786Sps	static int
29560786Spscmd_ichar(c)
29660786Sps	int c;
29760786Sps{
29860786Sps	char *s;
29960786Sps
30060786Sps	if (strlen(cmdbuf) >= sizeof(cmdbuf)-2)
30160786Sps	{
30260786Sps		/*
30360786Sps		 * No room in the command buffer for another char.
30460786Sps		 */
30560786Sps		bell();
30660786Sps		return (CC_ERROR);
30760786Sps	}
30860786Sps
30960786Sps	/*
31060786Sps	 * Insert the character into the buffer.
31160786Sps	 */
31260786Sps	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
31360786Sps		s[1] = s[0];
31460786Sps	*cp = c;
31560786Sps	/*
31660786Sps	 * Reprint the tail of the line from the inserted char.
31760786Sps	 */
31860786Sps	cmd_repaint(cp);
31960786Sps	cmd_right();
32060786Sps	return (CC_OK);
32160786Sps}
32260786Sps
32360786Sps/*
32460786Sps * Backspace in the command buffer.
32560786Sps * Delete the char to the left of the cursor.
32660786Sps */
32760786Sps	static int
32860786Spscmd_erase()
32960786Sps{
33060786Sps	register char *s;
33160786Sps
33260786Sps	if (cp == cmdbuf)
33360786Sps	{
33460786Sps		/*
33560786Sps		 * Backspace past beginning of the buffer:
33660786Sps		 * this usually means abort the command.
33760786Sps		 */
33860786Sps		return (CC_QUIT);
33960786Sps	}
34060786Sps	/*
34160786Sps	 * Move cursor left (to the char being erased).
34260786Sps	 */
34360786Sps	cmd_left();
34460786Sps	/*
34560786Sps	 * Remove the char from the buffer (shift the buffer left).
34660786Sps	 */
34760786Sps	for (s = cp;  *s != '\0';  s++)
34860786Sps		s[0] = s[1];
34960786Sps	/*
35060786Sps	 * Repaint the buffer after the erased char.
35160786Sps	 */
35260786Sps	cmd_repaint(cp);
35360786Sps
35460786Sps	/*
35560786Sps	 * We say that erasing the entire command string causes us
35660786Sps	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
35760786Sps	 */
35860786Sps	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
35960786Sps		return (CC_QUIT);
36060786Sps	return (CC_OK);
36160786Sps}
36260786Sps
36360786Sps/*
36460786Sps * Delete the char under the cursor.
36560786Sps */
36660786Sps	static int
36760786Spscmd_delete()
36860786Sps{
36960786Sps	if (*cp == '\0')
37060786Sps	{
37160786Sps		/*
37260786Sps		 * At end of string; there is no char under the cursor.
37360786Sps		 */
37460786Sps		return (CC_OK);
37560786Sps	}
37660786Sps	/*
37760786Sps	 * Move right, then use cmd_erase.
37860786Sps	 */
37960786Sps	cmd_right();
38060786Sps	cmd_erase();
38160786Sps	return (CC_OK);
38260786Sps}
38360786Sps
38460786Sps/*
38560786Sps * Delete the "word" to the left of the cursor.
38660786Sps */
38760786Sps	static int
38860786Spscmd_werase()
38960786Sps{
39060786Sps	if (cp > cmdbuf && cp[-1] == ' ')
39160786Sps	{
39260786Sps		/*
39360786Sps		 * If the char left of cursor is a space,
39460786Sps		 * erase all the spaces left of cursor (to the first non-space).
39560786Sps		 */
39660786Sps		while (cp > cmdbuf && cp[-1] == ' ')
39760786Sps			(void) cmd_erase();
39860786Sps	} else
39960786Sps	{
40060786Sps		/*
40160786Sps		 * If the char left of cursor is not a space,
40260786Sps		 * erase all the nonspaces left of cursor (the whole "word").
40360786Sps		 */
40460786Sps		while (cp > cmdbuf && cp[-1] != ' ')
40560786Sps			(void) cmd_erase();
40660786Sps	}
40760786Sps	return (CC_OK);
40860786Sps}
40960786Sps
41060786Sps/*
41160786Sps * Delete the "word" under the cursor.
41260786Sps */
41360786Sps	static int
41460786Spscmd_wdelete()
41560786Sps{
41660786Sps	if (*cp == ' ')
41760786Sps	{
41860786Sps		/*
41960786Sps		 * If the char under the cursor is a space,
42060786Sps		 * delete it and all the spaces right of cursor.
42160786Sps		 */
42260786Sps		while (*cp == ' ')
42360786Sps			(void) cmd_delete();
42460786Sps	} else
42560786Sps	{
42660786Sps		/*
42760786Sps		 * If the char under the cursor is not a space,
42860786Sps		 * delete it and all nonspaces right of cursor (the whole word).
42960786Sps		 */
43060786Sps		while (*cp != ' ' && *cp != '\0')
43160786Sps			(void) cmd_delete();
43260786Sps	}
43360786Sps	return (CC_OK);
43460786Sps}
43560786Sps
43660786Sps/*
43760786Sps * Delete all chars in the command buffer.
43860786Sps */
43960786Sps	static int
44060786Spscmd_kill()
44160786Sps{
44260786Sps	if (cmdbuf[0] == '\0')
44360786Sps	{
44460786Sps		/*
44560786Sps		 * Buffer is already empty; abort the current command.
44660786Sps		 */
44760786Sps		return (CC_QUIT);
44860786Sps	}
44960786Sps	cmd_offset = 0;
45060786Sps	cmd_home();
45160786Sps	*cp = '\0';
45260786Sps	cmd_repaint(cp);
45360786Sps
45460786Sps	/*
45560786Sps	 * We say that erasing the entire command string causes us
45660786Sps	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
45760786Sps	 */
45860786Sps	if (curr_cmdflags & CF_QUIT_ON_ERASE)
45960786Sps		return (CC_QUIT);
46060786Sps	return (CC_OK);
46160786Sps}
46260786Sps
46360786Sps/*
46460786Sps * Select an mlist structure to be the current command history.
46560786Sps */
46660786Sps	public void
46760786Spsset_mlist(mlist, cmdflags)
46860786Sps	void *mlist;
46960786Sps	int cmdflags;
47060786Sps{
47160786Sps	curr_mlist = (struct mlist *) mlist;
47260786Sps	curr_cmdflags = cmdflags;
47360786Sps}
47460786Sps
47560786Sps#if CMD_HISTORY
47660786Sps/*
47760786Sps * Move up or down in the currently selected command history list.
47860786Sps */
47960786Sps	static int
48060786Spscmd_updown(action)
48160786Sps	int action;
48260786Sps{
48360786Sps	char *s;
48460786Sps
48560786Sps	if (curr_mlist == NULL)
48660786Sps	{
48760786Sps		/*
48860786Sps		 * The current command has no history list.
48960786Sps		 */
49060786Sps		bell();
49160786Sps		return (CC_OK);
49260786Sps	}
49360786Sps	cmd_home();
49460786Sps	clear_eol();
49560786Sps	/*
49660786Sps	 * Move curr_mp to the next/prev entry.
49760786Sps	 */
49860786Sps	if (action == EC_UP)
49960786Sps		curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
50060786Sps	else
50160786Sps		curr_mlist->curr_mp = curr_mlist->curr_mp->next;
50260786Sps	/*
50360786Sps	 * Copy the entry into cmdbuf and echo it on the screen.
50460786Sps	 */
50560786Sps	s = curr_mlist->curr_mp->string;
50660786Sps	if (s == NULL)
50760786Sps		s = "";
50860786Sps	for (cp = cmdbuf;  *s != '\0';  s++)
50960786Sps	{
51060786Sps		*cp = *s;
51160786Sps		cmd_right();
51260786Sps	}
51360786Sps	*cp = '\0';
51460786Sps	return (CC_OK);
51560786Sps}
51660786Sps#endif
51760786Sps
51860786Sps/*
51960786Sps * Add a string to a history list.
52060786Sps */
52160786Sps	public void
52260786Spscmd_addhist(mlist, cmd)
52360786Sps	struct mlist *mlist;
52460786Sps	char *cmd;
52560786Sps{
52660786Sps#if CMD_HISTORY
52760786Sps	struct mlist *ml;
52860786Sps
52960786Sps	/*
53060786Sps	 * Don't save a trivial command.
53160786Sps	 */
53260786Sps	if (strlen(cmd) == 0)
53360786Sps		return;
53460786Sps	/*
53560786Sps	 * Don't save if a duplicate of a command which is already
53660786Sps	 * in the history.
53760786Sps	 * But select the one already in the history to be current.
53860786Sps	 */
53960786Sps	for (ml = mlist->next;  ml != mlist;  ml = ml->next)
54060786Sps	{
54160786Sps		if (strcmp(ml->string, cmd) == 0)
54260786Sps			break;
54360786Sps	}
54460786Sps	if (ml == mlist)
54560786Sps	{
54660786Sps		/*
54760786Sps		 * Did not find command in history.
54860786Sps		 * Save the command and put it at the end of the history list.
54960786Sps		 */
55060786Sps		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
55160786Sps		ml->string = save(cmd);
55260786Sps		ml->next = mlist;
55360786Sps		ml->prev = mlist->prev;
55460786Sps		mlist->prev->next = ml;
55560786Sps		mlist->prev = ml;
55660786Sps	}
55760786Sps	/*
55860786Sps	 * Point to the cmd just after the just-accepted command.
55960786Sps	 * Thus, an UPARROW will always retrieve the previous command.
56060786Sps	 */
56160786Sps	mlist->curr_mp = ml->next;
56260786Sps#endif
56360786Sps}
56460786Sps
56560786Sps/*
56660786Sps * Accept the command in the command buffer.
56760786Sps * Add it to the currently selected history list.
56860786Sps */
56960786Sps	public void
57060786Spscmd_accept()
57160786Sps{
57260786Sps#if CMD_HISTORY
57360786Sps	/*
57460786Sps	 * Nothing to do if there is no currently selected history list.
57560786Sps	 */
57660786Sps	if (curr_mlist == NULL)
57760786Sps		return;
57860786Sps	cmd_addhist(curr_mlist, cmdbuf);
57960786Sps#endif
58060786Sps}
58160786Sps
58260786Sps/*
58360786Sps * Try to perform a line-edit function on the command buffer,
58460786Sps * using a specified char as a line-editing command.
58560786Sps * Returns:
58660786Sps *	CC_PASS	The char does not invoke a line edit function.
58760786Sps *	CC_OK	Line edit function done.
58860786Sps *	CC_QUIT	The char requests the current command to be aborted.
58960786Sps */
59060786Sps	static int
59160786Spscmd_edit(c)
59260786Sps	int c;
59360786Sps{
59460786Sps	int action;
59560786Sps	int flags;
59660786Sps
59760786Sps#if TAB_COMPLETE_FILENAME
59860786Sps#define	not_in_completion()	in_completion = 0
59960786Sps#else
60060786Sps#define	not_in_completion()
60160786Sps#endif
60260786Sps
60360786Sps	/*
60460786Sps	 * See if the char is indeed a line-editing command.
60560786Sps	 */
60660786Sps	flags = 0;
60760786Sps#if CMD_HISTORY
60860786Sps	if (curr_mlist == NULL)
60960786Sps		/*
61060786Sps		 * No current history; don't accept history manipulation cmds.
61160786Sps		 */
61260786Sps		flags |= EC_NOHISTORY;
61360786Sps#endif
61460786Sps#if TAB_COMPLETE_FILENAME
61560786Sps	if (curr_mlist == ml_search)
61660786Sps		/*
61760786Sps		 * In a search command; don't accept file-completion cmds.
61860786Sps		 */
61960786Sps		flags |= EC_NOCOMPLETE;
62060786Sps#endif
62160786Sps
62260786Sps	action = editchar(c, flags);
62360786Sps
62460786Sps	switch (action)
62560786Sps	{
62660786Sps	case EC_RIGHT:
62760786Sps		not_in_completion();
62860786Sps		return (cmd_right());
62960786Sps	case EC_LEFT:
63060786Sps		not_in_completion();
63160786Sps		return (cmd_left());
63260786Sps	case EC_W_RIGHT:
63360786Sps		not_in_completion();
63460786Sps		while (*cp != '\0' && *cp != ' ')
63560786Sps			cmd_right();
63660786Sps		while (*cp == ' ')
63760786Sps			cmd_right();
63860786Sps		return (CC_OK);
63960786Sps	case EC_W_LEFT:
64060786Sps		not_in_completion();
64160786Sps		while (cp > cmdbuf && cp[-1] == ' ')
64260786Sps			cmd_left();
64360786Sps		while (cp > cmdbuf && cp[-1] != ' ')
64460786Sps			cmd_left();
64560786Sps		return (CC_OK);
64660786Sps	case EC_HOME:
64760786Sps		not_in_completion();
64860786Sps		cmd_offset = 0;
64960786Sps		cmd_home();
65060786Sps		cmd_repaint(cp);
65160786Sps		return (CC_OK);
65260786Sps	case EC_END:
65360786Sps		not_in_completion();
65460786Sps		while (*cp != '\0')
65560786Sps			cmd_right();
65660786Sps		return (CC_OK);
65760786Sps	case EC_INSERT:
65860786Sps		not_in_completion();
65960786Sps		return (CC_OK);
66060786Sps	case EC_BACKSPACE:
66160786Sps		not_in_completion();
66260786Sps		return (cmd_erase());
66360786Sps	case EC_LINEKILL:
66460786Sps		not_in_completion();
66560786Sps		return (cmd_kill());
66660786Sps	case EC_W_BACKSPACE:
66760786Sps		not_in_completion();
66860786Sps		return (cmd_werase());
66960786Sps	case EC_DELETE:
67060786Sps		not_in_completion();
67160786Sps		return (cmd_delete());
67260786Sps	case EC_W_DELETE:
67360786Sps		not_in_completion();
67460786Sps		return (cmd_wdelete());
67560786Sps	case EC_LITERAL:
67660786Sps		literal = 1;
67760786Sps		return (CC_OK);
67860786Sps#if CMD_HISTORY
67960786Sps	case EC_UP:
68060786Sps	case EC_DOWN:
68160786Sps		not_in_completion();
68260786Sps		return (cmd_updown(action));
68360786Sps#endif
68460786Sps#if TAB_COMPLETE_FILENAME
68560786Sps	case EC_F_COMPLETE:
68660786Sps	case EC_B_COMPLETE:
68760786Sps	case EC_EXPAND:
68860786Sps		return (cmd_complete(action));
68960786Sps#endif
69060786Sps	case EC_NOACTION:
69160786Sps		return (CC_OK);
69260786Sps	default:
69360786Sps		not_in_completion();
69460786Sps		return (CC_PASS);
69560786Sps	}
69660786Sps}
69760786Sps
69860786Sps#if TAB_COMPLETE_FILENAME
69960786Sps/*
70060786Sps * Insert a string into the command buffer, at the current position.
70160786Sps */
70260786Sps	static int
70360786Spscmd_istr(str)
70460786Sps	char *str;
70560786Sps{
70660786Sps	char *s;
70760786Sps	int action;
70860786Sps
70960786Sps	for (s = str;  *s != '\0';  s++)
71060786Sps	{
71160786Sps		action = cmd_ichar(*s);
71260786Sps		if (action != CC_OK)
71360786Sps		{
71460786Sps			bell();
71560786Sps			return (action);
71660786Sps		}
71760786Sps	}
71860786Sps	return (CC_OK);
71960786Sps}
72060786Sps
72160786Sps/*
72260786Sps * Find the beginning and end of the "current" word.
72360786Sps * This is the word which the cursor (cp) is inside or at the end of.
72460786Sps * Return pointer to the beginning of the word and put the
72560786Sps * cursor at the end of the word.
72660786Sps */
72760786Sps	static char *
72860786Spsdelimit_word()
72960786Sps{
73060786Sps	char *word;
73160786Sps#if SPACES_IN_FILENAMES
73260786Sps	char *p;
73360786Sps	int quoted;
73460786Sps#endif
73560786Sps
73660786Sps	/*
73760786Sps	 * Move cursor to end of word.
73860786Sps	 */
73960786Sps	if (*cp != ' ' && *cp != '\0')
74060786Sps	{
74160786Sps		/*
74260786Sps		 * Cursor is on a nonspace.
74360786Sps		 * Move cursor right to the next space.
74460786Sps		 */
74560786Sps		while (*cp != ' ' && *cp != '\0')
74660786Sps			cmd_right();
74760786Sps	} else if (cp > cmdbuf && cp[-1] != ' ')
74860786Sps	{
74960786Sps		/*
75060786Sps		 * Cursor is on a space, and char to the left is a nonspace.
75160786Sps		 * We're already at the end of the word.
75260786Sps		 */
75360786Sps		;
75460786Sps	} else
75560786Sps	{
75660786Sps		/*
75760786Sps		 * Cursor is on a space and char to the left is a space.
75860786Sps		 * Huh? There's no word here.
75960786Sps		 */
76060786Sps		return (NULL);
76160786Sps	}
76260786Sps	/*
76360786Sps	 * Search backwards for beginning of the word.
76460786Sps	 */
76560786Sps	if (cp == cmdbuf)
76660786Sps		return (NULL);
76760786Sps#if SPACES_IN_FILENAMES
76860786Sps	/*
76960786Sps	 * If we have an unbalanced quote (that is, an open quote
77060786Sps	 * without a corresponding close quote), we return everything
77160786Sps	 * from the open quote, including spaces.
77260786Sps	 */
77360786Sps	quoted = 0;
77460786Sps	for (p = cmdbuf;  p < cp;  p++)
77560786Sps	{
77660786Sps		if (!quoted && *p == openquote)
77760786Sps		{
77860786Sps			quoted = 1;
77960786Sps			word = p;
78060786Sps		} else if (quoted && *p == closequote)
78160786Sps		{
78260786Sps			quoted = 0;
78360786Sps		}
78460786Sps	}
78560786Sps	if (quoted)
78660786Sps		return (word);
78760786Sps#endif
78860786Sps	for (word = cp-1;  word > cmdbuf;  word--)
78960786Sps		if (word[-1] == ' ')
79060786Sps			break;
79160786Sps	return (word);
79260786Sps}
79360786Sps
79460786Sps/*
79560786Sps * Set things up to enter completion mode.
79660786Sps * Expand the word under the cursor into a list of filenames
79760786Sps * which start with that word, and set tk_text to that list.
79860786Sps */
79960786Sps	static void
80060786Spsinit_compl()
80160786Sps{
80260786Sps	char *word;
80360786Sps	char c;
80460786Sps
80560786Sps	/*
80660786Sps	 * Get rid of any previous tk_text.
80760786Sps	 */
80860786Sps	if (tk_text != NULL)
80960786Sps	{
81060786Sps		free(tk_text);
81160786Sps		tk_text = NULL;
81260786Sps	}
81360786Sps	/*
81460786Sps	 * Find the original (uncompleted) word in the command buffer.
81560786Sps	 */
81660786Sps	word = delimit_word();
81760786Sps	if (word == NULL)
81860786Sps		return;
81960786Sps	/*
82060786Sps	 * Set the insertion point to the point in the command buffer
82160786Sps	 * where the original (uncompleted) word now sits.
82260786Sps	 */
82360786Sps	tk_ipoint = word;
82460786Sps	/*
82560786Sps	 * Save the original (uncompleted) word
82660786Sps	 */
82760786Sps	if (tk_original != NULL)
82860786Sps		free(tk_original);
82960786Sps	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
83060786Sps	strncpy(tk_original, word, cp-word);
83160786Sps	/*
83260786Sps	 * Get the expanded filename.
83360786Sps	 * This may result in a single filename, or
83460786Sps	 * a blank-separated list of filenames.
83560786Sps	 */
83660786Sps	c = *cp;
83760786Sps	*cp = '\0';
83860786Sps#if SPACES_IN_FILENAMES
83960786Sps	if (*word == openquote)
84060786Sps		word++;
84160786Sps#endif
84260786Sps	tk_text = fcomplete(word);
84360786Sps	*cp = c;
84460786Sps}
84560786Sps
84660786Sps/*
84760786Sps * Return the next word in the current completion list.
84860786Sps */
84960786Sps	static char *
85060786Spsnext_compl(action, prev)
85160786Sps	int action;
85260786Sps	char *prev;
85360786Sps{
85460786Sps	switch (action)
85560786Sps	{
85660786Sps	case EC_F_COMPLETE:
85760786Sps		return (forw_textlist(&tk_tlist, prev));
85860786Sps	case EC_B_COMPLETE:
85960786Sps		return (back_textlist(&tk_tlist, prev));
86060786Sps	}
86160786Sps	/* Cannot happen */
86260786Sps	return ("?");
86360786Sps}
86460786Sps
86560786Sps/*
86660786Sps * Complete the filename before (or under) the cursor.
86760786Sps * cmd_complete may be called multiple times.  The global in_completion
86860786Sps * remembers whether this call is the first time (create the list),
86960786Sps * or a subsequent time (step thru the list).
87060786Sps */
87160786Sps	static int
87260786Spscmd_complete(action)
87360786Sps	int action;
87460786Sps{
87560786Sps	char *s;
87660786Sps
87760786Sps	if (!in_completion || action == EC_EXPAND)
87860786Sps	{
87960786Sps		/*
88060786Sps		 * Expand the word under the cursor and
88160786Sps		 * use the first word in the expansion
88260786Sps		 * (or the entire expansion if we're doing EC_EXPAND).
88360786Sps		 */
88460786Sps		init_compl();
88560786Sps		if (tk_text == NULL)
88660786Sps		{
88760786Sps			bell();
88860786Sps			return (CC_OK);
88960786Sps		}
89060786Sps		if (action == EC_EXPAND)
89160786Sps		{
89260786Sps			/*
89360786Sps			 * Use the whole list.
89460786Sps			 */
89560786Sps			tk_trial = tk_text;
89660786Sps		} else
89760786Sps		{
89860786Sps			/*
89960786Sps			 * Use the first filename in the list.
90060786Sps			 */
90160786Sps			in_completion = 1;
90260786Sps			init_textlist(&tk_tlist, tk_text);
90360786Sps			tk_trial = next_compl(action, (char*)NULL);
90460786Sps		}
90560786Sps	} else
90660786Sps	{
90760786Sps		/*
90860786Sps		 * We already have a completion list.
90960786Sps		 * Use the next/previous filename from the list.
91060786Sps		 */
91160786Sps		tk_trial = next_compl(action, tk_trial);
91260786Sps	}
91360786Sps
91460786Sps  	/*
91560786Sps  	 * Remove the original word, or the previous trial completion.
91660786Sps  	 */
91760786Sps	while (cp > tk_ipoint)
91860786Sps		(void) cmd_erase();
91960786Sps
92060786Sps	if (tk_trial == NULL)
92160786Sps	{
92260786Sps		/*
92360786Sps		 * There are no more trial completions.
92460786Sps		 * Insert the original (uncompleted) filename.
92560786Sps		 */
92660786Sps		in_completion = 0;
92760786Sps		if (cmd_istr(tk_original) != CC_OK)
92860786Sps			goto fail;
92960786Sps	} else
93060786Sps	{
93160786Sps		/*
93260786Sps		 * Insert trial completion.
93360786Sps		 */
93460786Sps		if (cmd_istr(tk_trial) != CC_OK)
93560786Sps			goto fail;
93660786Sps		/*
93760786Sps		 * If it is a directory, append a slash.
93860786Sps		 */
93960786Sps		if (is_dir(tk_trial))
94060786Sps		{
94160786Sps			if (cp > cmdbuf && cp[-1] == closequote)
94260786Sps				(void) cmd_erase();
94360786Sps			s = lgetenv("LESSSEPARATOR");
94460786Sps			if (s == NULL)
94560786Sps				s = PATHNAME_SEP;
94660786Sps			if (cmd_istr(s) != CC_OK)
94760786Sps				goto fail;
94860786Sps		}
94960786Sps	}
95060786Sps
95160786Sps	return (CC_OK);
95260786Sps
95360786Spsfail:
95460786Sps	in_completion = 0;
95560786Sps	bell();
95660786Sps	return (CC_OK);
95760786Sps}
95860786Sps
95960786Sps#endif /* TAB_COMPLETE_FILENAME */
96060786Sps
96160786Sps/*
96260786Sps * Process a single character of a multi-character command, such as
96360786Sps * a number, or the pattern of a search command.
96460786Sps * Returns:
96560786Sps *	CC_OK		The char was accepted.
96660786Sps *	CC_QUIT		The char requests the command to be aborted.
96760786Sps *	CC_ERROR	The char could not be accepted due to an error.
96860786Sps */
96960786Sps	public int
97060786Spscmd_char(c)
97160786Sps	int c;
97260786Sps{
97360786Sps	int action;
97460786Sps
97560786Sps	if (literal)
97660786Sps	{
97760786Sps		/*
97860786Sps		 * Insert the char, even if it is a line-editing char.
97960786Sps		 */
98060786Sps		literal = 0;
98160786Sps		return (cmd_ichar(c));
98260786Sps	}
98360786Sps
98460786Sps	/*
98560786Sps	 * See if it is a special line-editing character.
98660786Sps	 */
98760786Sps	if (in_mca())
98860786Sps	{
98960786Sps		action = cmd_edit(c);
99060786Sps		switch (action)
99160786Sps		{
99260786Sps		case CC_OK:
99360786Sps		case CC_QUIT:
99460786Sps			return (action);
99560786Sps		case CC_PASS:
99660786Sps			break;
99760786Sps		}
99860786Sps	}
99960786Sps
100060786Sps	/*
100160786Sps	 * Insert the char into the command buffer.
100260786Sps	 */
100360786Sps	return (cmd_ichar(c));
100460786Sps}
100560786Sps
100660786Sps/*
100760786Sps * Return the number currently in the command buffer.
100860786Sps */
100960786Sps	public int
101060786Spscmd_int()
101160786Sps{
101260786Sps	return (atoi(cmdbuf));
101360786Sps}
101460786Sps
101560786Sps/*
101660786Sps * Return a pointer to the command buffer.
101760786Sps */
101860786Sps	public char *
101960786Spsget_cmdbuf()
102060786Sps{
102160786Sps	return (cmdbuf);
102260786Sps}
1023