160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  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 *
7240121Sdelphij * For more information, see the README file.
860786Sps */
960786Sps
1060786Sps
1160786Sps/*
1260786Sps * Functions which manipulate the command buffer.
1360786Sps * Used only by command() and related functions.
1460786Sps */
1560786Sps
1660786Sps#include "less.h"
1760786Sps#include "cmd.h"
18161475Sdelphij#include "charset.h"
19161475Sdelphij#if HAVE_STAT
20161475Sdelphij#include <sys/stat.h>
21161475Sdelphij#endif
2260786Sps
2360786Spsextern int sc_width;
24161475Sdelphijextern int utf_mode;
2560786Sps
2660786Spsstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
2760786Spsstatic int cmd_col;		/* Current column of the cursor */
2860786Spsstatic int prompt_col;		/* Column of cursor just after prompt */
2960786Spsstatic char *cp;		/* Pointer into cmdbuf */
3060786Spsstatic int cmd_offset;		/* Index into cmdbuf of first displayed char */
3160786Spsstatic int literal;		/* Next input char should not be interpreted */
32240121Sdelphijstatic int updown_match = -1;	/* Prefix length in up/down movement */
3360786Sps
3460786Sps#if TAB_COMPLETE_FILENAME
3560786Spsstatic int cmd_complete();
3660786Sps/*
3760786Sps * These variables are statics used by cmd_complete.
3860786Sps */
3960786Spsstatic int in_completion = 0;
4060786Spsstatic char *tk_text;
4160786Spsstatic char *tk_original;
4260786Spsstatic char *tk_ipoint;
4360786Spsstatic char *tk_trial;
4460786Spsstatic struct textlist tk_tlist;
4560786Sps#endif
4660786Sps
4760786Spsstatic int cmd_left();
4860786Spsstatic int cmd_right();
4960786Sps
5060786Sps#if SPACES_IN_FILENAMES
5160786Spspublic char openquote = '"';
5260786Spspublic char closequote = '"';
5360786Sps#endif
5460786Sps
5560786Sps#if CMD_HISTORY
56161475Sdelphij
57161475Sdelphij/* History file */
58161475Sdelphij#define HISTFILE_FIRST_LINE      ".less-history-file:"
59161475Sdelphij#define HISTFILE_SEARCH_SECTION  ".search"
60161475Sdelphij#define HISTFILE_SHELL_SECTION   ".shell"
61161475Sdelphij
6260786Sps/*
6360786Sps * A mlist structure represents a command history.
6460786Sps */
6560786Spsstruct mlist
6660786Sps{
6760786Sps	struct mlist *next;
6860786Sps	struct mlist *prev;
6960786Sps	struct mlist *curr_mp;
7060786Sps	char *string;
71170256Sdelphij	int modified;
7260786Sps};
7360786Sps
7460786Sps/*
7560786Sps * These are the various command histories that exist.
7660786Sps */
7760786Spsstruct mlist mlist_search =
78170256Sdelphij	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
79128345Stjrpublic void * constant ml_search = (void *) &mlist_search;
8060786Sps
8160786Spsstruct mlist mlist_examine =
82170256Sdelphij	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
83128345Stjrpublic void * constant ml_examine = (void *) &mlist_examine;
8460786Sps
8560786Sps#if SHELL_ESCAPE || PIPEC
8660786Spsstruct mlist mlist_shell =
87170256Sdelphij	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
88128345Stjrpublic void * constant ml_shell = (void *) &mlist_shell;
8960786Sps#endif
9060786Sps
9160786Sps#else /* CMD_HISTORY */
9260786Sps
9360786Sps/* If CMD_HISTORY is off, these are just flags. */
94128345Stjrpublic void * constant ml_search = (void *)1;
95128345Stjrpublic void * constant ml_examine = (void *)2;
9660786Sps#if SHELL_ESCAPE || PIPEC
97128345Stjrpublic void * constant ml_shell = (void *)3;
9860786Sps#endif
9960786Sps
10060786Sps#endif /* CMD_HISTORY */
10160786Sps
10260786Sps/*
10360786Sps * History for the current command.
10460786Sps */
10560786Spsstatic struct mlist *curr_mlist = NULL;
10660786Spsstatic int curr_cmdflags;
10760786Sps
108161475Sdelphijstatic char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
109161475Sdelphijstatic int cmd_mbc_buf_len;
110161475Sdelphijstatic int cmd_mbc_buf_index;
11160786Sps
112161475Sdelphij
11360786Sps/*
11460786Sps * Reset command buffer (to empty).
11560786Sps */
11660786Sps	public void
11760786Spscmd_reset()
11860786Sps{
11960786Sps	cp = cmdbuf;
12060786Sps	*cp = '\0';
12160786Sps	cmd_col = 0;
12260786Sps	cmd_offset = 0;
12360786Sps	literal = 0;
124161475Sdelphij	cmd_mbc_buf_len = 0;
125240121Sdelphij	updown_match = -1;
12660786Sps}
12760786Sps
12860786Sps/*
129170256Sdelphij * Clear command line.
13060786Sps */
13160786Sps	public void
13260786Spsclear_cmd()
13360786Sps{
13460786Sps	cmd_col = prompt_col = 0;
135161475Sdelphij	cmd_mbc_buf_len = 0;
136240121Sdelphij	updown_match = -1;
13760786Sps}
13860786Sps
13960786Sps/*
14060786Sps * Display a string, usually as a prompt for input into the command buffer.
14160786Sps */
14260786Sps	public void
14360786Spscmd_putstr(s)
14460786Sps	char *s;
14560786Sps{
146161475Sdelphij	LWCHAR prev_ch = 0;
147161475Sdelphij	LWCHAR ch;
148161475Sdelphij	char *endline = s + strlen(s);
149161475Sdelphij	while (*s != '\0')
150161475Sdelphij	{
151161475Sdelphij		char *ns = s;
152161475Sdelphij		ch = step_char(&ns, +1, endline);
153161475Sdelphij		while (s < ns)
154161475Sdelphij			putchr(*s++);
155161475Sdelphij		if (!utf_mode)
156161475Sdelphij		{
157161475Sdelphij			cmd_col++;
158161475Sdelphij			prompt_col++;
159161475Sdelphij		} else if (!is_composing_char(ch) &&
160161475Sdelphij		           !is_combining_char(prev_ch, ch))
161161475Sdelphij		{
162161475Sdelphij			int width = is_wide_char(ch) ? 2 : 1;
163161475Sdelphij			cmd_col += width;
164161475Sdelphij			prompt_col += width;
165161475Sdelphij		}
166161475Sdelphij		prev_ch = ch;
167161475Sdelphij	}
16860786Sps}
16960786Sps
17060786Sps/*
17160786Sps * How many characters are in the command buffer?
17260786Sps */
17360786Sps	public int
17460786Spslen_cmdbuf()
17560786Sps{
176161475Sdelphij	char *s = cmdbuf;
177161475Sdelphij	char *endline = s + strlen(s);
178161475Sdelphij	int len = 0;
179161475Sdelphij
180161475Sdelphij	while (*s != '\0')
181161475Sdelphij	{
182161475Sdelphij		step_char(&s, +1, endline);
183161475Sdelphij		len++;
184161475Sdelphij	}
185161475Sdelphij	return (len);
18660786Sps}
18760786Sps
18860786Sps/*
189161475Sdelphij * Common part of cmd_step_right() and cmd_step_left().
190161475Sdelphij */
191161475Sdelphij	static char *
192161475Sdelphijcmd_step_common(p, ch, len, pwidth, bswidth)
193161475Sdelphij	char *p;
194161475Sdelphij	LWCHAR ch;
195161475Sdelphij	int len;
196161475Sdelphij	int *pwidth;
197161475Sdelphij	int *bswidth;
198161475Sdelphij{
199161475Sdelphij	char *pr;
200161475Sdelphij
201161475Sdelphij	if (len == 1)
202161475Sdelphij	{
203161475Sdelphij		pr = prchar((int) ch);
204161475Sdelphij		if (pwidth != NULL || bswidth != NULL)
205161475Sdelphij		{
206161475Sdelphij			int len = strlen(pr);
207161475Sdelphij			if (pwidth != NULL)
208161475Sdelphij				*pwidth = len;
209161475Sdelphij			if (bswidth != NULL)
210161475Sdelphij				*bswidth = len;
211161475Sdelphij		}
212161475Sdelphij	} else
213161475Sdelphij	{
214161475Sdelphij		pr = prutfchar(ch);
215161475Sdelphij		if (pwidth != NULL || bswidth != NULL)
216161475Sdelphij		{
217161475Sdelphij			if (is_composing_char(ch))
218161475Sdelphij			{
219161475Sdelphij				if (pwidth != NULL)
220161475Sdelphij					*pwidth = 0;
221161475Sdelphij				if (bswidth != NULL)
222161475Sdelphij					*bswidth = 0;
223161475Sdelphij			} else if (is_ubin_char(ch))
224161475Sdelphij			{
225161475Sdelphij				int len = strlen(pr);
226161475Sdelphij				if (pwidth != NULL)
227161475Sdelphij					*pwidth = len;
228161475Sdelphij				if (bswidth != NULL)
229161475Sdelphij					*bswidth = len;
230161475Sdelphij			} else
231161475Sdelphij			{
232161475Sdelphij				LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
233161475Sdelphij				if (is_combining_char(prev_ch, ch))
234161475Sdelphij				{
235161475Sdelphij					if (pwidth != NULL)
236161475Sdelphij						*pwidth = 0;
237161475Sdelphij					if (bswidth != NULL)
238161475Sdelphij						*bswidth = 0;
239161475Sdelphij				} else
240161475Sdelphij				{
241161475Sdelphij					if (pwidth != NULL)
242161475Sdelphij						*pwidth	= is_wide_char(ch)
243161475Sdelphij							?	2
244161475Sdelphij							:	1;
245161475Sdelphij					if (bswidth != NULL)
246161475Sdelphij						*bswidth = 1;
247161475Sdelphij				}
248161475Sdelphij			}
249161475Sdelphij		}
250161475Sdelphij	}
251161475Sdelphij
252161475Sdelphij	return (pr);
253161475Sdelphij}
254161475Sdelphij
255161475Sdelphij/*
256161475Sdelphij * Step a pointer one character right in the command buffer.
257161475Sdelphij */
258161475Sdelphij	static char *
259161475Sdelphijcmd_step_right(pp, pwidth, bswidth)
260161475Sdelphij	char **pp;
261161475Sdelphij	int *pwidth;
262161475Sdelphij	int *bswidth;
263161475Sdelphij{
264161475Sdelphij	char *p = *pp;
265161475Sdelphij	LWCHAR ch = step_char(pp, +1, p + strlen(p));
266161475Sdelphij
267161475Sdelphij	return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
268161475Sdelphij}
269161475Sdelphij
270161475Sdelphij/*
271161475Sdelphij * Step a pointer one character left in the command buffer.
272161475Sdelphij */
273161475Sdelphij	static char *
274161475Sdelphijcmd_step_left(pp, pwidth, bswidth)
275161475Sdelphij	char **pp;
276161475Sdelphij	int *pwidth;
277161475Sdelphij	int *bswidth;
278161475Sdelphij{
279161475Sdelphij	char *p = *pp;
280161475Sdelphij	LWCHAR ch = step_char(pp, -1, cmdbuf);
281161475Sdelphij
282161475Sdelphij	return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
283161475Sdelphij}
284161475Sdelphij
285161475Sdelphij/*
28660786Sps * Repaint the line from cp onwards.
28760786Sps * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
28860786Sps */
28960786Sps	static void
29060786Spscmd_repaint(old_cp)
29160786Sps	char *old_cp;
29260786Sps{
29360786Sps	/*
29460786Sps	 * Repaint the line from the current position.
29560786Sps	 */
29660786Sps	clear_eol();
297161475Sdelphij	while (*cp != '\0')
29860786Sps	{
299161475Sdelphij		char *np = cp;
300161475Sdelphij		int width;
301161475Sdelphij		char *pr = cmd_step_right(&np, &width, NULL);
302161475Sdelphij		if (cmd_col + width >= sc_width)
30360786Sps			break;
304161475Sdelphij		cp = np;
305161475Sdelphij		putstr(pr);
306161475Sdelphij		cmd_col += width;
30760786Sps	}
308161475Sdelphij	while (*cp != '\0')
309161475Sdelphij	{
310161475Sdelphij		char *np = cp;
311161475Sdelphij		int width;
312161475Sdelphij		char *pr = cmd_step_right(&np, &width, NULL);
313161475Sdelphij		if (width > 0)
314161475Sdelphij			break;
315161475Sdelphij		cp = np;
316161475Sdelphij		putstr(pr);
317161475Sdelphij	}
31860786Sps
31960786Sps	/*
32060786Sps	 * Back up the cursor to the correct position.
32160786Sps	 */
32260786Sps	while (cp > old_cp)
32360786Sps		cmd_left();
32460786Sps}
32560786Sps
32660786Sps/*
32760786Sps * Put the cursor at "home" (just after the prompt),
32860786Sps * and set cp to the corresponding char in cmdbuf.
32960786Sps */
33060786Sps	static void
33160786Spscmd_home()
33260786Sps{
33360786Sps	while (cmd_col > prompt_col)
33460786Sps	{
335161475Sdelphij		int width, bswidth;
336161475Sdelphij
337161475Sdelphij		cmd_step_left(&cp, &width, &bswidth);
338161475Sdelphij		while (bswidth-- > 0)
339161475Sdelphij			putbs();
340161475Sdelphij		cmd_col -= width;
34160786Sps	}
34260786Sps
34360786Sps	cp = &cmdbuf[cmd_offset];
34460786Sps}
34560786Sps
34660786Sps/*
34760786Sps * Shift the cmdbuf display left a half-screen.
34860786Sps */
34960786Sps	static void
35060786Spscmd_lshift()
35160786Sps{
35260786Sps	char *s;
35360786Sps	char *save_cp;
35460786Sps	int cols;
35560786Sps
35660786Sps	/*
35760786Sps	 * Start at the first displayed char, count how far to the
35860786Sps	 * right we'd have to move to reach the center of the screen.
35960786Sps	 */
36060786Sps	s = cmdbuf + cmd_offset;
36160786Sps	cols = 0;
36260786Sps	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
363161475Sdelphij	{
364161475Sdelphij		int width;
365161475Sdelphij		cmd_step_right(&s, &width, NULL);
366161475Sdelphij		cols += width;
367161475Sdelphij	}
368161475Sdelphij	while (*s != '\0')
369161475Sdelphij	{
370161475Sdelphij		int width;
371161475Sdelphij		char *ns = s;
372161475Sdelphij		cmd_step_right(&ns, &width, NULL);
373161475Sdelphij		if (width > 0)
374161475Sdelphij			break;
375161475Sdelphij		s = ns;
376161475Sdelphij	}
37760786Sps
37860786Sps	cmd_offset = s - cmdbuf;
37960786Sps	save_cp = cp;
38060786Sps	cmd_home();
38160786Sps	cmd_repaint(save_cp);
38260786Sps}
38360786Sps
38460786Sps/*
38560786Sps * Shift the cmdbuf display right a half-screen.
38660786Sps */
38760786Sps	static void
38860786Spscmd_rshift()
38960786Sps{
39060786Sps	char *s;
39160786Sps	char *save_cp;
39260786Sps	int cols;
39360786Sps
39460786Sps	/*
39560786Sps	 * Start at the first displayed char, count how far to the
39660786Sps	 * left we'd have to move to traverse a half-screen width
39760786Sps	 * of displayed characters.
39860786Sps	 */
39960786Sps	s = cmdbuf + cmd_offset;
40060786Sps	cols = 0;
40160786Sps	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
40260786Sps	{
403161475Sdelphij		int width;
404161475Sdelphij		cmd_step_left(&s, &width, NULL);
405161475Sdelphij		cols += width;
40660786Sps	}
40760786Sps
40860786Sps	cmd_offset = s - cmdbuf;
40960786Sps	save_cp = cp;
41060786Sps	cmd_home();
41160786Sps	cmd_repaint(save_cp);
41260786Sps}
41360786Sps
41460786Sps/*
41560786Sps * Move cursor right one character.
41660786Sps */
41760786Sps	static int
41860786Spscmd_right()
41960786Sps{
420161475Sdelphij	char *pr;
421161475Sdelphij	char *ncp;
422161475Sdelphij	int width;
42360786Sps
42460786Sps	if (*cp == '\0')
42560786Sps	{
426161475Sdelphij		/* Already at the end of the line. */
42760786Sps		return (CC_OK);
42860786Sps	}
429161475Sdelphij	ncp = cp;
430161475Sdelphij	pr = cmd_step_right(&ncp, &width, NULL);
431161475Sdelphij	if (cmd_col + width >= sc_width)
43260786Sps		cmd_lshift();
433161475Sdelphij	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
43460786Sps		cmd_lshift();
435161475Sdelphij	cp = ncp;
436161475Sdelphij	cmd_col += width;
437161475Sdelphij	putstr(pr);
438161475Sdelphij	while (*cp != '\0')
439161475Sdelphij	{
440161475Sdelphij		pr = cmd_step_right(&ncp, &width, NULL);
441161475Sdelphij		if (width > 0)
442161475Sdelphij			break;
443161475Sdelphij		putstr(pr);
444161475Sdelphij		cp = ncp;
445161475Sdelphij	}
44660786Sps	return (CC_OK);
44760786Sps}
44860786Sps
44960786Sps/*
45060786Sps * Move cursor left one character.
45160786Sps */
45260786Sps	static int
45360786Spscmd_left()
45460786Sps{
455161475Sdelphij	char *ncp;
456161475Sdelphij	int width, bswidth;
45760786Sps
45860786Sps	if (cp <= cmdbuf)
45960786Sps	{
46060786Sps		/* Already at the beginning of the line */
46160786Sps		return (CC_OK);
46260786Sps	}
463161475Sdelphij	ncp = cp;
464161475Sdelphij	while (ncp > cmdbuf)
465161475Sdelphij	{
466161475Sdelphij		cmd_step_left(&ncp, &width, &bswidth);
467161475Sdelphij		if (width > 0)
468161475Sdelphij			break;
469161475Sdelphij	}
470161475Sdelphij	if (cmd_col < prompt_col + width)
47160786Sps		cmd_rshift();
472161475Sdelphij	cp = ncp;
473161475Sdelphij	cmd_col -= width;
474161475Sdelphij	while (bswidth-- > 0)
47560786Sps		putbs();
47660786Sps	return (CC_OK);
47760786Sps}
47860786Sps
47960786Sps/*
48060786Sps * Insert a char into the command buffer, at the current position.
48160786Sps */
48260786Sps	static int
483161475Sdelphijcmd_ichar(cs, clen)
484161475Sdelphij	char *cs;
485161475Sdelphij	int clen;
48660786Sps{
48760786Sps	char *s;
48860786Sps
489161475Sdelphij	if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
49060786Sps	{
491161475Sdelphij		/* No room in the command buffer for another char. */
49260786Sps		bell();
49360786Sps		return (CC_ERROR);
49460786Sps	}
49560786Sps
49660786Sps	/*
497161475Sdelphij	 * Make room for the new character (shift the tail of the buffer right).
49860786Sps	 */
49960786Sps	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
500161475Sdelphij		s[clen] = s[0];
50160786Sps	/*
502161475Sdelphij	 * Insert the character into the buffer.
503161475Sdelphij	 */
504161475Sdelphij	for (s = cp;  s < cp + clen;  s++)
505161475Sdelphij		*s = *cs++;
506161475Sdelphij	/*
50760786Sps	 * Reprint the tail of the line from the inserted char.
50860786Sps	 */
509240121Sdelphij	updown_match = -1;
51060786Sps	cmd_repaint(cp);
51160786Sps	cmd_right();
51260786Sps	return (CC_OK);
51360786Sps}
51460786Sps
51560786Sps/*
51660786Sps * Backspace in the command buffer.
51760786Sps * Delete the char to the left of the cursor.
51860786Sps */
51960786Sps	static int
52060786Spscmd_erase()
52160786Sps{
52260786Sps	register char *s;
523161475Sdelphij	int clen;
52460786Sps
52560786Sps	if (cp == cmdbuf)
52660786Sps	{
52760786Sps		/*
52860786Sps		 * Backspace past beginning of the buffer:
52960786Sps		 * this usually means abort the command.
53060786Sps		 */
53160786Sps		return (CC_QUIT);
53260786Sps	}
53360786Sps	/*
53460786Sps	 * Move cursor left (to the char being erased).
53560786Sps	 */
536161475Sdelphij	s = cp;
53760786Sps	cmd_left();
538161475Sdelphij	clen = s - cp;
539161475Sdelphij
54060786Sps	/*
54160786Sps	 * Remove the char from the buffer (shift the buffer left).
54260786Sps	 */
543161475Sdelphij	for (s = cp;  ;  s++)
544161475Sdelphij	{
545161475Sdelphij		s[0] = s[clen];
546161475Sdelphij		if (s[0] == '\0')
547161475Sdelphij			break;
548161475Sdelphij	}
549161475Sdelphij
55060786Sps	/*
55160786Sps	 * Repaint the buffer after the erased char.
55260786Sps	 */
553240121Sdelphij	updown_match = -1;
55460786Sps	cmd_repaint(cp);
55560786Sps
55660786Sps	/*
55760786Sps	 * We say that erasing the entire command string causes us
55860786Sps	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
55960786Sps	 */
56060786Sps	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
56160786Sps		return (CC_QUIT);
56260786Sps	return (CC_OK);
56360786Sps}
56460786Sps
56560786Sps/*
56660786Sps * Delete the char under the cursor.
56760786Sps */
56860786Sps	static int
56960786Spscmd_delete()
57060786Sps{
57160786Sps	if (*cp == '\0')
57260786Sps	{
573161475Sdelphij		/* At end of string; there is no char under the cursor. */
57460786Sps		return (CC_OK);
57560786Sps	}
57660786Sps	/*
57760786Sps	 * Move right, then use cmd_erase.
57860786Sps	 */
57960786Sps	cmd_right();
58060786Sps	cmd_erase();
58160786Sps	return (CC_OK);
58260786Sps}
58360786Sps
58460786Sps/*
58560786Sps * Delete the "word" to the left of the cursor.
58660786Sps */
58760786Sps	static int
58860786Spscmd_werase()
58960786Sps{
59060786Sps	if (cp > cmdbuf && cp[-1] == ' ')
59160786Sps	{
59260786Sps		/*
59360786Sps		 * If the char left of cursor is a space,
59460786Sps		 * erase all the spaces left of cursor (to the first non-space).
59560786Sps		 */
59660786Sps		while (cp > cmdbuf && cp[-1] == ' ')
59760786Sps			(void) cmd_erase();
59860786Sps	} else
59960786Sps	{
60060786Sps		/*
60160786Sps		 * If the char left of cursor is not a space,
60260786Sps		 * erase all the nonspaces left of cursor (the whole "word").
60360786Sps		 */
60460786Sps		while (cp > cmdbuf && cp[-1] != ' ')
60560786Sps			(void) cmd_erase();
60660786Sps	}
60760786Sps	return (CC_OK);
60860786Sps}
60960786Sps
61060786Sps/*
61160786Sps * Delete the "word" under the cursor.
61260786Sps */
61360786Sps	static int
61460786Spscmd_wdelete()
61560786Sps{
61660786Sps	if (*cp == ' ')
61760786Sps	{
61860786Sps		/*
61960786Sps		 * If the char under the cursor is a space,
62060786Sps		 * delete it and all the spaces right of cursor.
62160786Sps		 */
62260786Sps		while (*cp == ' ')
62360786Sps			(void) cmd_delete();
62460786Sps	} else
62560786Sps	{
62660786Sps		/*
62760786Sps		 * If the char under the cursor is not a space,
62860786Sps		 * delete it and all nonspaces right of cursor (the whole word).
62960786Sps		 */
63060786Sps		while (*cp != ' ' && *cp != '\0')
63160786Sps			(void) cmd_delete();
63260786Sps	}
63360786Sps	return (CC_OK);
63460786Sps}
63560786Sps
63660786Sps/*
63760786Sps * Delete all chars in the command buffer.
63860786Sps */
63960786Sps	static int
64060786Spscmd_kill()
64160786Sps{
64260786Sps	if (cmdbuf[0] == '\0')
64360786Sps	{
644161475Sdelphij		/* Buffer is already empty; abort the current command. */
64560786Sps		return (CC_QUIT);
64660786Sps	}
64760786Sps	cmd_offset = 0;
64860786Sps	cmd_home();
64960786Sps	*cp = '\0';
650240121Sdelphij	updown_match = -1;
65160786Sps	cmd_repaint(cp);
65260786Sps
65360786Sps	/*
65460786Sps	 * We say that erasing the entire command string causes us
65560786Sps	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
65660786Sps	 */
65760786Sps	if (curr_cmdflags & CF_QUIT_ON_ERASE)
65860786Sps		return (CC_QUIT);
65960786Sps	return (CC_OK);
66060786Sps}
66160786Sps
66260786Sps/*
66360786Sps * Select an mlist structure to be the current command history.
66460786Sps */
66560786Sps	public void
66660786Spsset_mlist(mlist, cmdflags)
66760786Sps	void *mlist;
66860786Sps	int cmdflags;
66960786Sps{
670191930Sdelphij#if CMD_HISTORY
67160786Sps	curr_mlist = (struct mlist *) mlist;
67260786Sps	curr_cmdflags = cmdflags;
673161475Sdelphij
674161475Sdelphij	/* Make sure the next up-arrow moves to the last string in the mlist. */
675161475Sdelphij	if (curr_mlist != NULL)
676161475Sdelphij		curr_mlist->curr_mp = curr_mlist;
677191930Sdelphij#endif
67860786Sps}
67960786Sps
68060786Sps#if CMD_HISTORY
68160786Sps/*
68260786Sps * Move up or down in the currently selected command history list.
683240121Sdelphij * Only consider entries whose first updown_match chars are equal to
684240121Sdelphij * cmdbuf's corresponding chars.
68560786Sps */
68660786Sps	static int
68760786Spscmd_updown(action)
68860786Sps	int action;
68960786Sps{
69060786Sps	char *s;
691240121Sdelphij	struct mlist *ml;
69260786Sps
69360786Sps	if (curr_mlist == NULL)
69460786Sps	{
69560786Sps		/*
69660786Sps		 * The current command has no history list.
69760786Sps		 */
69860786Sps		bell();
69960786Sps		return (CC_OK);
70060786Sps	}
701240121Sdelphij
702240121Sdelphij	if (updown_match < 0)
703240121Sdelphij	{
704240121Sdelphij		updown_match = cp - cmdbuf;
705240121Sdelphij	}
706240121Sdelphij
70760786Sps	/*
708240121Sdelphij	 * Find the next history entry which matches.
70960786Sps	 */
710240121Sdelphij	for (ml = curr_mlist->curr_mp;;)
711240121Sdelphij	{
712240121Sdelphij		ml = (action == EC_UP) ? ml->prev : ml->next;
713240121Sdelphij		if (ml == curr_mlist)
714240121Sdelphij		{
715240121Sdelphij			/*
716240121Sdelphij			 * We reached the end (or beginning) of the list.
717240121Sdelphij			 */
718240121Sdelphij			break;
719240121Sdelphij		}
720240121Sdelphij		if (strncmp(cmdbuf, ml->string, updown_match) == 0)
721240121Sdelphij		{
722240121Sdelphij			/*
723240121Sdelphij			 * This entry matches; stop here.
724240121Sdelphij			 * Copy the entry into cmdbuf and echo it on the screen.
725240121Sdelphij			 */
726240121Sdelphij			curr_mlist->curr_mp = ml;
727240121Sdelphij			s = ml->string;
728240121Sdelphij			if (s == NULL)
729240121Sdelphij				s = "";
730240121Sdelphij			cmd_home();
731240121Sdelphij			clear_eol();
732251154Sdelphij			strcpy(cmdbuf, s);
733240121Sdelphij			for (cp = cmdbuf;  *cp != '\0';  )
734240121Sdelphij				cmd_right();
735240121Sdelphij			return (CC_OK);
736240121Sdelphij		}
737240121Sdelphij	}
73860786Sps	/*
739240121Sdelphij	 * We didn't find a history entry that matches.
74060786Sps	 */
741240121Sdelphij	bell();
74260786Sps	return (CC_OK);
74360786Sps}
74460786Sps#endif
74560786Sps
74660786Sps/*
74760786Sps * Add a string to a history list.
74860786Sps */
74960786Sps	public void
75060786Spscmd_addhist(mlist, cmd)
75160786Sps	struct mlist *mlist;
75260786Sps	char *cmd;
75360786Sps{
75460786Sps#if CMD_HISTORY
75560786Sps	struct mlist *ml;
75660786Sps
75760786Sps	/*
75860786Sps	 * Don't save a trivial command.
75960786Sps	 */
76060786Sps	if (strlen(cmd) == 0)
76160786Sps		return;
762161475Sdelphij
76360786Sps	/*
764161475Sdelphij	 * Save the command unless it's a duplicate of the
765161475Sdelphij	 * last command in the history.
76660786Sps	 */
767161475Sdelphij	ml = mlist->prev;
768161475Sdelphij	if (ml == mlist || strcmp(ml->string, cmd) != 0)
76960786Sps	{
77060786Sps		/*
77160786Sps		 * Did not find command in history.
77260786Sps		 * Save the command and put it at the end of the history list.
77360786Sps		 */
77460786Sps		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
77560786Sps		ml->string = save(cmd);
77660786Sps		ml->next = mlist;
77760786Sps		ml->prev = mlist->prev;
77860786Sps		mlist->prev->next = ml;
77960786Sps		mlist->prev = ml;
78060786Sps	}
78160786Sps	/*
78260786Sps	 * Point to the cmd just after the just-accepted command.
78360786Sps	 * Thus, an UPARROW will always retrieve the previous command.
78460786Sps	 */
78560786Sps	mlist->curr_mp = ml->next;
78660786Sps#endif
78760786Sps}
78860786Sps
78960786Sps/*
79060786Sps * Accept the command in the command buffer.
79160786Sps * Add it to the currently selected history list.
79260786Sps */
79360786Sps	public void
79460786Spscmd_accept()
79560786Sps{
79660786Sps#if CMD_HISTORY
79760786Sps	/*
79860786Sps	 * Nothing to do if there is no currently selected history list.
79960786Sps	 */
80060786Sps	if (curr_mlist == NULL)
80160786Sps		return;
80260786Sps	cmd_addhist(curr_mlist, cmdbuf);
803170256Sdelphij	curr_mlist->modified = 1;
80460786Sps#endif
80560786Sps}
80660786Sps
80760786Sps/*
80860786Sps * Try to perform a line-edit function on the command buffer,
80960786Sps * using a specified char as a line-editing command.
81060786Sps * Returns:
81160786Sps *	CC_PASS	The char does not invoke a line edit function.
81260786Sps *	CC_OK	Line edit function done.
81360786Sps *	CC_QUIT	The char requests the current command to be aborted.
81460786Sps */
81560786Sps	static int
81660786Spscmd_edit(c)
81760786Sps	int c;
81860786Sps{
81960786Sps	int action;
82060786Sps	int flags;
82160786Sps
82260786Sps#if TAB_COMPLETE_FILENAME
82360786Sps#define	not_in_completion()	in_completion = 0
82460786Sps#else
82560786Sps#define	not_in_completion()
82660786Sps#endif
82760786Sps
82860786Sps	/*
82960786Sps	 * See if the char is indeed a line-editing command.
83060786Sps	 */
83160786Sps	flags = 0;
83260786Sps#if CMD_HISTORY
83360786Sps	if (curr_mlist == NULL)
83460786Sps		/*
83560786Sps		 * No current history; don't accept history manipulation cmds.
83660786Sps		 */
83760786Sps		flags |= EC_NOHISTORY;
83860786Sps#endif
83960786Sps#if TAB_COMPLETE_FILENAME
84060786Sps	if (curr_mlist == ml_search)
84160786Sps		/*
84260786Sps		 * In a search command; don't accept file-completion cmds.
84360786Sps		 */
84460786Sps		flags |= EC_NOCOMPLETE;
84560786Sps#endif
84660786Sps
84760786Sps	action = editchar(c, flags);
84860786Sps
84960786Sps	switch (action)
85060786Sps	{
85160786Sps	case EC_RIGHT:
85260786Sps		not_in_completion();
85360786Sps		return (cmd_right());
85460786Sps	case EC_LEFT:
85560786Sps		not_in_completion();
85660786Sps		return (cmd_left());
85760786Sps	case EC_W_RIGHT:
85860786Sps		not_in_completion();
85960786Sps		while (*cp != '\0' && *cp != ' ')
86060786Sps			cmd_right();
86160786Sps		while (*cp == ' ')
86260786Sps			cmd_right();
86360786Sps		return (CC_OK);
86460786Sps	case EC_W_LEFT:
86560786Sps		not_in_completion();
86660786Sps		while (cp > cmdbuf && cp[-1] == ' ')
86760786Sps			cmd_left();
86860786Sps		while (cp > cmdbuf && cp[-1] != ' ')
86960786Sps			cmd_left();
87060786Sps		return (CC_OK);
87160786Sps	case EC_HOME:
87260786Sps		not_in_completion();
87360786Sps		cmd_offset = 0;
87460786Sps		cmd_home();
87560786Sps		cmd_repaint(cp);
87660786Sps		return (CC_OK);
87760786Sps	case EC_END:
87860786Sps		not_in_completion();
87960786Sps		while (*cp != '\0')
88060786Sps			cmd_right();
88160786Sps		return (CC_OK);
88260786Sps	case EC_INSERT:
88360786Sps		not_in_completion();
88460786Sps		return (CC_OK);
88560786Sps	case EC_BACKSPACE:
88660786Sps		not_in_completion();
88760786Sps		return (cmd_erase());
88860786Sps	case EC_LINEKILL:
88960786Sps		not_in_completion();
89060786Sps		return (cmd_kill());
891221715Sdelphij	case EC_ABORT:
892221715Sdelphij		not_in_completion();
893221715Sdelphij		(void) cmd_kill();
894221715Sdelphij		return (CC_QUIT);
89560786Sps	case EC_W_BACKSPACE:
89660786Sps		not_in_completion();
89760786Sps		return (cmd_werase());
89860786Sps	case EC_DELETE:
89960786Sps		not_in_completion();
90060786Sps		return (cmd_delete());
90160786Sps	case EC_W_DELETE:
90260786Sps		not_in_completion();
90360786Sps		return (cmd_wdelete());
90460786Sps	case EC_LITERAL:
90560786Sps		literal = 1;
90660786Sps		return (CC_OK);
90760786Sps#if CMD_HISTORY
90860786Sps	case EC_UP:
90960786Sps	case EC_DOWN:
91060786Sps		not_in_completion();
91160786Sps		return (cmd_updown(action));
91260786Sps#endif
91360786Sps#if TAB_COMPLETE_FILENAME
91460786Sps	case EC_F_COMPLETE:
91560786Sps	case EC_B_COMPLETE:
91660786Sps	case EC_EXPAND:
91760786Sps		return (cmd_complete(action));
91860786Sps#endif
91960786Sps	case EC_NOACTION:
92060786Sps		return (CC_OK);
92160786Sps	default:
92260786Sps		not_in_completion();
92360786Sps		return (CC_PASS);
92460786Sps	}
92560786Sps}
92660786Sps
92760786Sps#if TAB_COMPLETE_FILENAME
92860786Sps/*
92960786Sps * Insert a string into the command buffer, at the current position.
93060786Sps */
93160786Sps	static int
93260786Spscmd_istr(str)
93360786Sps	char *str;
93460786Sps{
93560786Sps	char *s;
93660786Sps	int action;
937161475Sdelphij	char *endline = str + strlen(str);
93860786Sps
939161475Sdelphij	for (s = str;  *s != '\0';  )
94060786Sps	{
941161475Sdelphij		char *os = s;
942161475Sdelphij		step_char(&s, +1, endline);
943161475Sdelphij		action = cmd_ichar(os, s - os);
94460786Sps		if (action != CC_OK)
94560786Sps		{
94660786Sps			bell();
94760786Sps			return (action);
94860786Sps		}
94960786Sps	}
95060786Sps	return (CC_OK);
95160786Sps}
95260786Sps
95360786Sps/*
95460786Sps * Find the beginning and end of the "current" word.
95560786Sps * This is the word which the cursor (cp) is inside or at the end of.
95660786Sps * Return pointer to the beginning of the word and put the
95760786Sps * cursor at the end of the word.
95860786Sps */
95960786Sps	static char *
96060786Spsdelimit_word()
96160786Sps{
96260786Sps	char *word;
96360786Sps#if SPACES_IN_FILENAMES
96460786Sps	char *p;
965128345Stjr	int delim_quoted = 0;
966128345Stjr	int meta_quoted = 0;
967128345Stjr	char *esc = get_meta_escape();
968128345Stjr	int esclen = strlen(esc);
96960786Sps#endif
97060786Sps
97160786Sps	/*
97260786Sps	 * Move cursor to end of word.
97360786Sps	 */
97460786Sps	if (*cp != ' ' && *cp != '\0')
97560786Sps	{
97660786Sps		/*
97760786Sps		 * Cursor is on a nonspace.
97860786Sps		 * Move cursor right to the next space.
97960786Sps		 */
98060786Sps		while (*cp != ' ' && *cp != '\0')
98160786Sps			cmd_right();
98260786Sps	} else if (cp > cmdbuf && cp[-1] != ' ')
98360786Sps	{
98460786Sps		/*
98560786Sps		 * Cursor is on a space, and char to the left is a nonspace.
98660786Sps		 * We're already at the end of the word.
98760786Sps		 */
98860786Sps		;
989128345Stjr#if 0
99060786Sps	} else
99160786Sps	{
99260786Sps		/*
99360786Sps		 * Cursor is on a space and char to the left is a space.
99460786Sps		 * Huh? There's no word here.
99560786Sps		 */
99660786Sps		return (NULL);
997128345Stjr#endif
99860786Sps	}
99960786Sps	/*
1000128345Stjr	 * Find the beginning of the word which the cursor is in.
100160786Sps	 */
100260786Sps	if (cp == cmdbuf)
100360786Sps		return (NULL);
100460786Sps#if SPACES_IN_FILENAMES
100560786Sps	/*
100660786Sps	 * If we have an unbalanced quote (that is, an open quote
100760786Sps	 * without a corresponding close quote), we return everything
100860786Sps	 * from the open quote, including spaces.
100960786Sps	 */
1010128345Stjr	for (word = cmdbuf;  word < cp;  word++)
1011128345Stjr		if (*word != ' ')
1012128345Stjr			break;
1013128345Stjr	if (word >= cp)
1014128345Stjr		return (cp);
101560786Sps	for (p = cmdbuf;  p < cp;  p++)
101660786Sps	{
1017128345Stjr		if (meta_quoted)
101860786Sps		{
1019128345Stjr			meta_quoted = 0;
1020128345Stjr		} else if (esclen > 0 && p + esclen < cp &&
1021128345Stjr		           strncmp(p, esc, esclen) == 0)
102260786Sps		{
1023128345Stjr			meta_quoted = 1;
1024128345Stjr			p += esclen - 1;
1025128345Stjr		} else if (delim_quoted)
1026128345Stjr		{
1027128345Stjr			if (*p == closequote)
1028128345Stjr				delim_quoted = 0;
1029128345Stjr		} else /* (!delim_quoted) */
1030128345Stjr		{
1031128345Stjr			if (*p == openquote)
1032128345Stjr				delim_quoted = 1;
1033128345Stjr			else if (*p == ' ')
1034128345Stjr				word = p+1;
103560786Sps		}
103660786Sps	}
103760786Sps#endif
103860786Sps	return (word);
103960786Sps}
104060786Sps
104160786Sps/*
104260786Sps * Set things up to enter completion mode.
104360786Sps * Expand the word under the cursor into a list of filenames
104460786Sps * which start with that word, and set tk_text to that list.
104560786Sps */
104660786Sps	static void
104760786Spsinit_compl()
104860786Sps{
104960786Sps	char *word;
105060786Sps	char c;
105160786Sps
105260786Sps	/*
105360786Sps	 * Get rid of any previous tk_text.
105460786Sps	 */
105560786Sps	if (tk_text != NULL)
105660786Sps	{
105760786Sps		free(tk_text);
105860786Sps		tk_text = NULL;
105960786Sps	}
106060786Sps	/*
106160786Sps	 * Find the original (uncompleted) word in the command buffer.
106260786Sps	 */
106360786Sps	word = delimit_word();
106460786Sps	if (word == NULL)
106560786Sps		return;
106660786Sps	/*
106760786Sps	 * Set the insertion point to the point in the command buffer
106860786Sps	 * where the original (uncompleted) word now sits.
106960786Sps	 */
107060786Sps	tk_ipoint = word;
107160786Sps	/*
107260786Sps	 * Save the original (uncompleted) word
107360786Sps	 */
107460786Sps	if (tk_original != NULL)
107560786Sps		free(tk_original);
107660786Sps	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
107760786Sps	strncpy(tk_original, word, cp-word);
107860786Sps	/*
107960786Sps	 * Get the expanded filename.
108060786Sps	 * This may result in a single filename, or
108160786Sps	 * a blank-separated list of filenames.
108260786Sps	 */
108360786Sps	c = *cp;
108460786Sps	*cp = '\0';
1085128345Stjr	if (*word != openquote)
1086128345Stjr	{
1087128345Stjr		tk_text = fcomplete(word);
1088128345Stjr	} else
1089128345Stjr	{
1090240121Sdelphij#if MSDOS_COMPILER
1091240121Sdelphij		char *qword = NULL;
1092240121Sdelphij#else
1093128345Stjr		char *qword = shell_quote(word+1);
1094240121Sdelphij#endif
1095128345Stjr		if (qword == NULL)
1096128345Stjr			tk_text = fcomplete(word+1);
1097128345Stjr		else
1098128345Stjr		{
1099128345Stjr			tk_text = fcomplete(qword);
1100128345Stjr			free(qword);
1101128345Stjr		}
1102128345Stjr	}
110360786Sps	*cp = c;
110460786Sps}
110560786Sps
110660786Sps/*
110760786Sps * Return the next word in the current completion list.
110860786Sps */
110960786Sps	static char *
111060786Spsnext_compl(action, prev)
111160786Sps	int action;
111260786Sps	char *prev;
111360786Sps{
111460786Sps	switch (action)
111560786Sps	{
111660786Sps	case EC_F_COMPLETE:
111760786Sps		return (forw_textlist(&tk_tlist, prev));
111860786Sps	case EC_B_COMPLETE:
111960786Sps		return (back_textlist(&tk_tlist, prev));
112060786Sps	}
112160786Sps	/* Cannot happen */
112260786Sps	return ("?");
112360786Sps}
112460786Sps
112560786Sps/*
112660786Sps * Complete the filename before (or under) the cursor.
112760786Sps * cmd_complete may be called multiple times.  The global in_completion
112860786Sps * remembers whether this call is the first time (create the list),
112960786Sps * or a subsequent time (step thru the list).
113060786Sps */
113160786Sps	static int
113260786Spscmd_complete(action)
113360786Sps	int action;
113460786Sps{
113560786Sps	char *s;
113660786Sps
113760786Sps	if (!in_completion || action == EC_EXPAND)
113860786Sps	{
113960786Sps		/*
114060786Sps		 * Expand the word under the cursor and
114160786Sps		 * use the first word in the expansion
114260786Sps		 * (or the entire expansion if we're doing EC_EXPAND).
114360786Sps		 */
114460786Sps		init_compl();
114560786Sps		if (tk_text == NULL)
114660786Sps		{
114760786Sps			bell();
114860786Sps			return (CC_OK);
114960786Sps		}
115060786Sps		if (action == EC_EXPAND)
115160786Sps		{
115260786Sps			/*
115360786Sps			 * Use the whole list.
115460786Sps			 */
115560786Sps			tk_trial = tk_text;
115660786Sps		} else
115760786Sps		{
115860786Sps			/*
115960786Sps			 * Use the first filename in the list.
116060786Sps			 */
116160786Sps			in_completion = 1;
116260786Sps			init_textlist(&tk_tlist, tk_text);
116360786Sps			tk_trial = next_compl(action, (char*)NULL);
116460786Sps		}
116560786Sps	} else
116660786Sps	{
116760786Sps		/*
116860786Sps		 * We already have a completion list.
116960786Sps		 * Use the next/previous filename from the list.
117060786Sps		 */
117160786Sps		tk_trial = next_compl(action, tk_trial);
117260786Sps	}
117360786Sps
117460786Sps  	/*
117560786Sps  	 * Remove the original word, or the previous trial completion.
117660786Sps  	 */
117760786Sps	while (cp > tk_ipoint)
117860786Sps		(void) cmd_erase();
117960786Sps
118060786Sps	if (tk_trial == NULL)
118160786Sps	{
118260786Sps		/*
118360786Sps		 * There are no more trial completions.
118460786Sps		 * Insert the original (uncompleted) filename.
118560786Sps		 */
118660786Sps		in_completion = 0;
118760786Sps		if (cmd_istr(tk_original) != CC_OK)
118860786Sps			goto fail;
118960786Sps	} else
119060786Sps	{
119160786Sps		/*
119260786Sps		 * Insert trial completion.
119360786Sps		 */
119460786Sps		if (cmd_istr(tk_trial) != CC_OK)
119560786Sps			goto fail;
119660786Sps		/*
119760786Sps		 * If it is a directory, append a slash.
119860786Sps		 */
119960786Sps		if (is_dir(tk_trial))
120060786Sps		{
120160786Sps			if (cp > cmdbuf && cp[-1] == closequote)
120260786Sps				(void) cmd_erase();
120360786Sps			s = lgetenv("LESSSEPARATOR");
120460786Sps			if (s == NULL)
120560786Sps				s = PATHNAME_SEP;
120660786Sps			if (cmd_istr(s) != CC_OK)
120760786Sps				goto fail;
120860786Sps		}
120960786Sps	}
121060786Sps
121160786Sps	return (CC_OK);
121260786Sps
121360786Spsfail:
121460786Sps	in_completion = 0;
121560786Sps	bell();
121660786Sps	return (CC_OK);
121760786Sps}
121860786Sps
121960786Sps#endif /* TAB_COMPLETE_FILENAME */
122060786Sps
122160786Sps/*
122260786Sps * Process a single character of a multi-character command, such as
122360786Sps * a number, or the pattern of a search command.
122460786Sps * Returns:
122560786Sps *	CC_OK		The char was accepted.
122660786Sps *	CC_QUIT		The char requests the command to be aborted.
122760786Sps *	CC_ERROR	The char could not be accepted due to an error.
122860786Sps */
122960786Sps	public int
123060786Spscmd_char(c)
123160786Sps	int c;
123260786Sps{
123360786Sps	int action;
1234161475Sdelphij	int len;
123560786Sps
1236161475Sdelphij	if (!utf_mode)
1237161475Sdelphij	{
1238161475Sdelphij		cmd_mbc_buf[0] = c;
1239161475Sdelphij		len = 1;
1240161475Sdelphij	} else
1241161475Sdelphij	{
1242161475Sdelphij		/* Perform strict validation in all possible cases.  */
1243161475Sdelphij		if (cmd_mbc_buf_len == 0)
1244161475Sdelphij		{
1245161475Sdelphij		 retry:
1246161475Sdelphij			cmd_mbc_buf_index = 1;
1247161475Sdelphij			*cmd_mbc_buf = c;
1248161475Sdelphij			if (IS_ASCII_OCTET(c))
1249161475Sdelphij				cmd_mbc_buf_len = 1;
1250161475Sdelphij			else if (IS_UTF8_LEAD(c))
1251161475Sdelphij			{
1252161475Sdelphij				cmd_mbc_buf_len = utf_len(c);
1253161475Sdelphij				return (CC_OK);
1254161475Sdelphij			} else
1255161475Sdelphij			{
1256161475Sdelphij				/* UTF8_INVALID or stray UTF8_TRAIL */
1257161475Sdelphij				bell();
1258161475Sdelphij				return (CC_ERROR);
1259161475Sdelphij			}
1260161475Sdelphij		} else if (IS_UTF8_TRAIL(c))
1261161475Sdelphij		{
1262161475Sdelphij			cmd_mbc_buf[cmd_mbc_buf_index++] = c;
1263161475Sdelphij			if (cmd_mbc_buf_index < cmd_mbc_buf_len)
1264161475Sdelphij				return (CC_OK);
1265161475Sdelphij			if (!is_utf8_well_formed(cmd_mbc_buf))
1266161475Sdelphij			{
1267161475Sdelphij				/* complete, but not well formed (non-shortest form), sequence */
1268161475Sdelphij				cmd_mbc_buf_len = 0;
1269161475Sdelphij				bell();
1270161475Sdelphij				return (CC_ERROR);
1271161475Sdelphij			}
1272161475Sdelphij		} else
1273161475Sdelphij		{
1274161475Sdelphij			/* Flush incomplete (truncated) sequence.  */
1275161475Sdelphij			cmd_mbc_buf_len = 0;
1276161475Sdelphij			bell();
1277161475Sdelphij			/* Handle new char.  */
1278161475Sdelphij			goto retry;
1279161475Sdelphij		}
1280161475Sdelphij
1281161475Sdelphij		len = cmd_mbc_buf_len;
1282161475Sdelphij		cmd_mbc_buf_len = 0;
1283161475Sdelphij	}
1284161475Sdelphij
128560786Sps	if (literal)
128660786Sps	{
128760786Sps		/*
128860786Sps		 * Insert the char, even if it is a line-editing char.
128960786Sps		 */
129060786Sps		literal = 0;
1291161475Sdelphij		return (cmd_ichar(cmd_mbc_buf, len));
129260786Sps	}
129360786Sps
129460786Sps	/*
1295161475Sdelphij	 * See if it is a line-editing character.
129660786Sps	 */
1297161475Sdelphij	if (in_mca() && len == 1)
129860786Sps	{
129960786Sps		action = cmd_edit(c);
130060786Sps		switch (action)
130160786Sps		{
130260786Sps		case CC_OK:
130360786Sps		case CC_QUIT:
130460786Sps			return (action);
130560786Sps		case CC_PASS:
130660786Sps			break;
130760786Sps		}
130860786Sps	}
130960786Sps
131060786Sps	/*
131160786Sps	 * Insert the char into the command buffer.
131260786Sps	 */
1313161475Sdelphij	return (cmd_ichar(cmd_mbc_buf, len));
131460786Sps}
131560786Sps
131660786Sps/*
131760786Sps * Return the number currently in the command buffer.
131860786Sps */
1319128345Stjr	public LINENUM
1320170256Sdelphijcmd_int(frac)
1321170256Sdelphij	long *frac;
132260786Sps{
1323170256Sdelphij	char *p;
1324128345Stjr	LINENUM n = 0;
1325170256Sdelphij	int err;
1326128345Stjr
1327170256Sdelphij	for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
1328170256Sdelphij		n = (n * 10) + (*p - '0');
1329170256Sdelphij	*frac = 0;
1330170256Sdelphij	if (*p++ == '.')
1331170256Sdelphij	{
1332170256Sdelphij		*frac = getfraction(&p, NULL, &err);
1333170256Sdelphij		/* {{ do something if err is set? }} */
1334170256Sdelphij	}
1335128345Stjr	return (n);
133660786Sps}
133760786Sps
133860786Sps/*
133960786Sps * Return a pointer to the command buffer.
134060786Sps */
134160786Sps	public char *
134260786Spsget_cmdbuf()
134360786Sps{
134460786Sps	return (cmdbuf);
134560786Sps}
1346161475Sdelphij
1347191930Sdelphij#if CMD_HISTORY
1348170256Sdelphij/*
1349170256Sdelphij * Return the last (most recent) string in the current command history.
1350170256Sdelphij */
1351170256Sdelphij	public char *
1352170256Sdelphijcmd_lastpattern()
1353170256Sdelphij{
1354170256Sdelphij	if (curr_mlist == NULL)
1355170256Sdelphij		return (NULL);
1356170256Sdelphij	return (curr_mlist->curr_mp->prev->string);
1357170256Sdelphij}
1358191930Sdelphij#endif
1359170256Sdelphij
1360161475Sdelphij#if CMD_HISTORY
1361161475Sdelphij/*
1362161475Sdelphij * Get the name of the history file.
1363161475Sdelphij */
1364161475Sdelphij	static char *
1365161475Sdelphijhistfile_name()
1366161475Sdelphij{
1367161475Sdelphij	char *home;
1368161475Sdelphij	char *name;
1369161475Sdelphij	int len;
1370161475Sdelphij
1371161475Sdelphij	/* See if filename is explicitly specified by $LESSHISTFILE. */
1372161475Sdelphij	name = lgetenv("LESSHISTFILE");
1373161475Sdelphij	if (name != NULL && *name != '\0')
1374161475Sdelphij	{
1375170256Sdelphij		if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
1376161475Sdelphij			/* $LESSHISTFILE == "-" means don't use a history file. */
1377161475Sdelphij			return (NULL);
1378161475Sdelphij		return (save(name));
1379161475Sdelphij	}
1380161475Sdelphij
1381161475Sdelphij	/* Otherwise, file is in $HOME. */
1382161475Sdelphij	home = lgetenv("HOME");
1383161475Sdelphij	if (home == NULL || *home == '\0')
1384161475Sdelphij	{
1385161475Sdelphij#if OS2
1386161475Sdelphij		home = lgetenv("INIT");
1387161475Sdelphij		if (home == NULL || *home == '\0')
1388161475Sdelphij#endif
1389161475Sdelphij			return (NULL);
1390161475Sdelphij	}
1391161475Sdelphij	len = strlen(home) + strlen(LESSHISTFILE) + 2;
1392161475Sdelphij	name = (char *) ecalloc(len, sizeof(char));
1393161475Sdelphij	SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
1394161475Sdelphij	return (name);
1395161475Sdelphij}
1396161475Sdelphij#endif /* CMD_HISTORY */
1397161475Sdelphij
1398161475Sdelphij/*
1399161475Sdelphij * Initialize history from a .lesshist file.
1400161475Sdelphij */
1401161475Sdelphij	public void
1402161475Sdelphijinit_cmdhist()
1403161475Sdelphij{
1404161475Sdelphij#if CMD_HISTORY
1405161475Sdelphij	struct mlist *ml = NULL;
1406161475Sdelphij	char line[CMDBUF_SIZE];
1407161475Sdelphij	char *filename;
1408161475Sdelphij	FILE *f;
1409161475Sdelphij	char *p;
1410161475Sdelphij
1411161475Sdelphij	filename = histfile_name();
1412161475Sdelphij	if (filename == NULL)
1413161475Sdelphij		return;
1414161475Sdelphij	f = fopen(filename, "r");
1415161475Sdelphij	free(filename);
1416161475Sdelphij	if (f == NULL)
1417161475Sdelphij		return;
1418161475Sdelphij	if (fgets(line, sizeof(line), f) == NULL ||
1419161475Sdelphij	    strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
1420161475Sdelphij	{
1421161475Sdelphij		fclose(f);
1422161475Sdelphij		return;
1423161475Sdelphij	}
1424161475Sdelphij	while (fgets(line, sizeof(line), f) != NULL)
1425161475Sdelphij	{
1426161475Sdelphij		for (p = line;  *p != '\0';  p++)
1427161475Sdelphij		{
1428161475Sdelphij			if (*p == '\n' || *p == '\r')
1429161475Sdelphij			{
1430161475Sdelphij				*p = '\0';
1431161475Sdelphij				break;
1432161475Sdelphij			}
1433161475Sdelphij		}
1434161475Sdelphij		if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1435161475Sdelphij			ml = &mlist_search;
1436170964Sdelphij		else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
1437170964Sdelphij		{
1438161475Sdelphij#if SHELL_ESCAPE || PIPEC
1439161475Sdelphij			ml = &mlist_shell;
1440170964Sdelphij#else
1441170964Sdelphij			ml = NULL;
1442161475Sdelphij#endif
1443170964Sdelphij		} else if (*line == '"')
1444161475Sdelphij		{
1445161475Sdelphij			if (ml != NULL)
1446161475Sdelphij				cmd_addhist(ml, line+1);
1447161475Sdelphij		}
1448161475Sdelphij	}
1449161475Sdelphij	fclose(f);
1450161475Sdelphij#endif /* CMD_HISTORY */
1451161475Sdelphij}
1452161475Sdelphij
1453161475Sdelphij/*
1454161475Sdelphij *
1455161475Sdelphij */
1456161475Sdelphij#if CMD_HISTORY
1457161475Sdelphij	static void
1458161475Sdelphijsave_mlist(ml, f)
1459161475Sdelphij	struct mlist *ml;
1460161475Sdelphij	FILE *f;
1461161475Sdelphij{
1462161475Sdelphij	int histsize = 0;
1463161475Sdelphij	int n;
1464161475Sdelphij	char *s;
1465161475Sdelphij
1466161475Sdelphij	s = lgetenv("LESSHISTSIZE");
1467161475Sdelphij	if (s != NULL)
1468161475Sdelphij		histsize = atoi(s);
1469161475Sdelphij	if (histsize == 0)
1470161475Sdelphij		histsize = 100;
1471161475Sdelphij
1472161475Sdelphij	ml = ml->prev;
1473161475Sdelphij	for (n = 0;  n < histsize;  n++)
1474161475Sdelphij	{
1475161475Sdelphij		if (ml->string == NULL)
1476161475Sdelphij			break;
1477161475Sdelphij		ml = ml->prev;
1478161475Sdelphij	}
1479161475Sdelphij	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1480161475Sdelphij		fprintf(f, "\"%s\n", ml->string);
1481161475Sdelphij}
1482161475Sdelphij#endif /* CMD_HISTORY */
1483161475Sdelphij
1484161475Sdelphij/*
1485161475Sdelphij *
1486161475Sdelphij */
1487161475Sdelphij	public void
1488161475Sdelphijsave_cmdhist()
1489161475Sdelphij{
1490161475Sdelphij#if CMD_HISTORY
1491161475Sdelphij	char *filename;
1492161475Sdelphij	FILE *f;
1493170964Sdelphij	int modified = 0;
1494161475Sdelphij
1495170964Sdelphij	if (mlist_search.modified)
1496170964Sdelphij		modified = 1;
1497170964Sdelphij#if SHELL_ESCAPE || PIPEC
1498170964Sdelphij	if (mlist_shell.modified)
1499170964Sdelphij		modified = 1;
1500170964Sdelphij#endif
1501170964Sdelphij	if (!modified)
1502170256Sdelphij		return;
1503240121Sdelphij	filename = histfile_name();
1504240121Sdelphij	if (filename == NULL)
1505240121Sdelphij		return;
1506161475Sdelphij	f = fopen(filename, "w");
1507161475Sdelphij	free(filename);
1508161475Sdelphij	if (f == NULL)
1509161475Sdelphij		return;
1510161475Sdelphij#if HAVE_FCHMOD
1511191930Sdelphij{
1512161475Sdelphij	/* Make history file readable only by owner. */
1513191930Sdelphij	int do_chmod = 1;
1514191930Sdelphij#if HAVE_STAT
1515191930Sdelphij	struct stat statbuf;
1516191930Sdelphij	int r = fstat(fileno(f), &statbuf);
1517191930Sdelphij	if (r < 0 || !S_ISREG(statbuf.st_mode))
1518191930Sdelphij		/* Don't chmod if not a regular file. */
1519191930Sdelphij		do_chmod = 0;
1520161475Sdelphij#endif
1521191930Sdelphij	if (do_chmod)
1522191930Sdelphij		fchmod(fileno(f), 0600);
1523191930Sdelphij}
1524191930Sdelphij#endif
1525161475Sdelphij
1526161475Sdelphij	fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
1527161475Sdelphij
1528161475Sdelphij	fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1529161475Sdelphij	save_mlist(&mlist_search, f);
1530161475Sdelphij
1531161475Sdelphij#if SHELL_ESCAPE || PIPEC
1532161475Sdelphij	fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1533161475Sdelphij	save_mlist(&mlist_shell, f);
1534161475Sdelphij#endif
1535161475Sdelphij
1536161475Sdelphij	fclose(f);
1537161475Sdelphij#endif /* CMD_HISTORY */
1538161475Sdelphij}
1539