cmdbuf.c revision 191930
160786Sps/*
2191930Sdelphij * Copyright (C) 1984-2008  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"
19161475Sdelphij#include "charset.h"
20161475Sdelphij#if HAVE_STAT
21161475Sdelphij#include <sys/stat.h>
22161475Sdelphij#endif
2360786Sps
2460786Spsextern int sc_width;
25161475Sdelphijextern int utf_mode;
2660786Sps
2760786Spsstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
2860786Spsstatic int cmd_col;		/* Current column of the cursor */
2960786Spsstatic int prompt_col;		/* Column of cursor just after prompt */
3060786Spsstatic char *cp;		/* Pointer into cmdbuf */
3160786Spsstatic int cmd_offset;		/* Index into cmdbuf of first displayed char */
3260786Spsstatic int literal;		/* Next input char should not be interpreted */
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;
12560786Sps}
12660786Sps
12760786Sps/*
128170256Sdelphij * Clear command line.
12960786Sps */
13060786Sps	public void
13160786Spsclear_cmd()
13260786Sps{
13360786Sps	cmd_col = prompt_col = 0;
134161475Sdelphij	cmd_mbc_buf_len = 0;
13560786Sps}
13660786Sps
13760786Sps/*
13860786Sps * Display a string, usually as a prompt for input into the command buffer.
13960786Sps */
14060786Sps	public void
14160786Spscmd_putstr(s)
14260786Sps	char *s;
14360786Sps{
144161475Sdelphij	LWCHAR prev_ch = 0;
145161475Sdelphij	LWCHAR ch;
146161475Sdelphij	char *endline = s + strlen(s);
147161475Sdelphij	while (*s != '\0')
148161475Sdelphij	{
149161475Sdelphij		char *ns = s;
150161475Sdelphij		ch = step_char(&ns, +1, endline);
151161475Sdelphij		while (s < ns)
152161475Sdelphij			putchr(*s++);
153161475Sdelphij		if (!utf_mode)
154161475Sdelphij		{
155161475Sdelphij			cmd_col++;
156161475Sdelphij			prompt_col++;
157161475Sdelphij		} else if (!is_composing_char(ch) &&
158161475Sdelphij		           !is_combining_char(prev_ch, ch))
159161475Sdelphij		{
160161475Sdelphij			int width = is_wide_char(ch) ? 2 : 1;
161161475Sdelphij			cmd_col += width;
162161475Sdelphij			prompt_col += width;
163161475Sdelphij		}
164161475Sdelphij		prev_ch = ch;
165161475Sdelphij	}
16660786Sps}
16760786Sps
16860786Sps/*
16960786Sps * How many characters are in the command buffer?
17060786Sps */
17160786Sps	public int
17260786Spslen_cmdbuf()
17360786Sps{
174161475Sdelphij	char *s = cmdbuf;
175161475Sdelphij	char *endline = s + strlen(s);
176161475Sdelphij	int len = 0;
177161475Sdelphij
178161475Sdelphij	while (*s != '\0')
179161475Sdelphij	{
180161475Sdelphij		step_char(&s, +1, endline);
181161475Sdelphij		len++;
182161475Sdelphij	}
183161475Sdelphij	return (len);
18460786Sps}
18560786Sps
18660786Sps/*
187161475Sdelphij * Common part of cmd_step_right() and cmd_step_left().
188161475Sdelphij */
189161475Sdelphij	static char *
190161475Sdelphijcmd_step_common(p, ch, len, pwidth, bswidth)
191161475Sdelphij	char *p;
192161475Sdelphij	LWCHAR ch;
193161475Sdelphij	int len;
194161475Sdelphij	int *pwidth;
195161475Sdelphij	int *bswidth;
196161475Sdelphij{
197161475Sdelphij	char *pr;
198161475Sdelphij
199161475Sdelphij	if (len == 1)
200161475Sdelphij	{
201161475Sdelphij		pr = prchar((int) ch);
202161475Sdelphij		if (pwidth != NULL || bswidth != NULL)
203161475Sdelphij		{
204161475Sdelphij			int len = strlen(pr);
205161475Sdelphij			if (pwidth != NULL)
206161475Sdelphij				*pwidth = len;
207161475Sdelphij			if (bswidth != NULL)
208161475Sdelphij				*bswidth = len;
209161475Sdelphij		}
210161475Sdelphij	} else
211161475Sdelphij	{
212161475Sdelphij		pr = prutfchar(ch);
213161475Sdelphij		if (pwidth != NULL || bswidth != NULL)
214161475Sdelphij		{
215161475Sdelphij			if (is_composing_char(ch))
216161475Sdelphij			{
217161475Sdelphij				if (pwidth != NULL)
218161475Sdelphij					*pwidth = 0;
219161475Sdelphij				if (bswidth != NULL)
220161475Sdelphij					*bswidth = 0;
221161475Sdelphij			} else if (is_ubin_char(ch))
222161475Sdelphij			{
223161475Sdelphij				int len = strlen(pr);
224161475Sdelphij				if (pwidth != NULL)
225161475Sdelphij					*pwidth = len;
226161475Sdelphij				if (bswidth != NULL)
227161475Sdelphij					*bswidth = len;
228161475Sdelphij			} else
229161475Sdelphij			{
230161475Sdelphij				LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
231161475Sdelphij				if (is_combining_char(prev_ch, ch))
232161475Sdelphij				{
233161475Sdelphij					if (pwidth != NULL)
234161475Sdelphij						*pwidth = 0;
235161475Sdelphij					if (bswidth != NULL)
236161475Sdelphij						*bswidth = 0;
237161475Sdelphij				} else
238161475Sdelphij				{
239161475Sdelphij					if (pwidth != NULL)
240161475Sdelphij						*pwidth	= is_wide_char(ch)
241161475Sdelphij							?	2
242161475Sdelphij							:	1;
243161475Sdelphij					if (bswidth != NULL)
244161475Sdelphij						*bswidth = 1;
245161475Sdelphij				}
246161475Sdelphij			}
247161475Sdelphij		}
248161475Sdelphij	}
249161475Sdelphij
250161475Sdelphij	return (pr);
251161475Sdelphij}
252161475Sdelphij
253161475Sdelphij/*
254161475Sdelphij * Step a pointer one character right in the command buffer.
255161475Sdelphij */
256161475Sdelphij	static char *
257161475Sdelphijcmd_step_right(pp, pwidth, bswidth)
258161475Sdelphij	char **pp;
259161475Sdelphij	int *pwidth;
260161475Sdelphij	int *bswidth;
261161475Sdelphij{
262161475Sdelphij	char *p = *pp;
263161475Sdelphij	LWCHAR ch = step_char(pp, +1, p + strlen(p));
264161475Sdelphij
265161475Sdelphij	return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
266161475Sdelphij}
267161475Sdelphij
268161475Sdelphij/*
269161475Sdelphij * Step a pointer one character left in the command buffer.
270161475Sdelphij */
271161475Sdelphij	static char *
272161475Sdelphijcmd_step_left(pp, pwidth, bswidth)
273161475Sdelphij	char **pp;
274161475Sdelphij	int *pwidth;
275161475Sdelphij	int *bswidth;
276161475Sdelphij{
277161475Sdelphij	char *p = *pp;
278161475Sdelphij	LWCHAR ch = step_char(pp, -1, cmdbuf);
279161475Sdelphij
280161475Sdelphij	return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
281161475Sdelphij}
282161475Sdelphij
283161475Sdelphij/*
28460786Sps * Repaint the line from cp onwards.
28560786Sps * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
28660786Sps */
28760786Sps	static void
28860786Spscmd_repaint(old_cp)
28960786Sps	char *old_cp;
29060786Sps{
29160786Sps	/*
29260786Sps	 * Repaint the line from the current position.
29360786Sps	 */
29460786Sps	clear_eol();
295161475Sdelphij	while (*cp != '\0')
29660786Sps	{
297161475Sdelphij		char *np = cp;
298161475Sdelphij		int width;
299161475Sdelphij		char *pr = cmd_step_right(&np, &width, NULL);
300161475Sdelphij		if (cmd_col + width >= sc_width)
30160786Sps			break;
302161475Sdelphij		cp = np;
303161475Sdelphij		putstr(pr);
304161475Sdelphij		cmd_col += width;
30560786Sps	}
306161475Sdelphij	while (*cp != '\0')
307161475Sdelphij	{
308161475Sdelphij		char *np = cp;
309161475Sdelphij		int width;
310161475Sdelphij		char *pr = cmd_step_right(&np, &width, NULL);
311161475Sdelphij		if (width > 0)
312161475Sdelphij			break;
313161475Sdelphij		cp = np;
314161475Sdelphij		putstr(pr);
315161475Sdelphij	}
31660786Sps
31760786Sps	/*
31860786Sps	 * Back up the cursor to the correct position.
31960786Sps	 */
32060786Sps	while (cp > old_cp)
32160786Sps		cmd_left();
32260786Sps}
32360786Sps
32460786Sps/*
32560786Sps * Put the cursor at "home" (just after the prompt),
32660786Sps * and set cp to the corresponding char in cmdbuf.
32760786Sps */
32860786Sps	static void
32960786Spscmd_home()
33060786Sps{
33160786Sps	while (cmd_col > prompt_col)
33260786Sps	{
333161475Sdelphij		int width, bswidth;
334161475Sdelphij
335161475Sdelphij		cmd_step_left(&cp, &width, &bswidth);
336161475Sdelphij		while (bswidth-- > 0)
337161475Sdelphij			putbs();
338161475Sdelphij		cmd_col -= width;
33960786Sps	}
34060786Sps
34160786Sps	cp = &cmdbuf[cmd_offset];
34260786Sps}
34360786Sps
34460786Sps/*
34560786Sps * Shift the cmdbuf display left a half-screen.
34660786Sps */
34760786Sps	static void
34860786Spscmd_lshift()
34960786Sps{
35060786Sps	char *s;
35160786Sps	char *save_cp;
35260786Sps	int cols;
35360786Sps
35460786Sps	/*
35560786Sps	 * Start at the first displayed char, count how far to the
35660786Sps	 * right we'd have to move to reach the center of the screen.
35760786Sps	 */
35860786Sps	s = cmdbuf + cmd_offset;
35960786Sps	cols = 0;
36060786Sps	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
361161475Sdelphij	{
362161475Sdelphij		int width;
363161475Sdelphij		cmd_step_right(&s, &width, NULL);
364161475Sdelphij		cols += width;
365161475Sdelphij	}
366161475Sdelphij	while (*s != '\0')
367161475Sdelphij	{
368161475Sdelphij		int width;
369161475Sdelphij		char *ns = s;
370161475Sdelphij		cmd_step_right(&ns, &width, NULL);
371161475Sdelphij		if (width > 0)
372161475Sdelphij			break;
373161475Sdelphij		s = ns;
374161475Sdelphij	}
37560786Sps
37660786Sps	cmd_offset = s - cmdbuf;
37760786Sps	save_cp = cp;
37860786Sps	cmd_home();
37960786Sps	cmd_repaint(save_cp);
38060786Sps}
38160786Sps
38260786Sps/*
38360786Sps * Shift the cmdbuf display right a half-screen.
38460786Sps */
38560786Sps	static void
38660786Spscmd_rshift()
38760786Sps{
38860786Sps	char *s;
38960786Sps	char *save_cp;
39060786Sps	int cols;
39160786Sps
39260786Sps	/*
39360786Sps	 * Start at the first displayed char, count how far to the
39460786Sps	 * left we'd have to move to traverse a half-screen width
39560786Sps	 * of displayed characters.
39660786Sps	 */
39760786Sps	s = cmdbuf + cmd_offset;
39860786Sps	cols = 0;
39960786Sps	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
40060786Sps	{
401161475Sdelphij		int width;
402161475Sdelphij		cmd_step_left(&s, &width, NULL);
403161475Sdelphij		cols += width;
40460786Sps	}
40560786Sps
40660786Sps	cmd_offset = s - cmdbuf;
40760786Sps	save_cp = cp;
40860786Sps	cmd_home();
40960786Sps	cmd_repaint(save_cp);
41060786Sps}
41160786Sps
41260786Sps/*
41360786Sps * Move cursor right one character.
41460786Sps */
41560786Sps	static int
41660786Spscmd_right()
41760786Sps{
418161475Sdelphij	char *pr;
419161475Sdelphij	char *ncp;
420161475Sdelphij	int width;
42160786Sps
42260786Sps	if (*cp == '\0')
42360786Sps	{
424161475Sdelphij		/* Already at the end of the line. */
42560786Sps		return (CC_OK);
42660786Sps	}
427161475Sdelphij	ncp = cp;
428161475Sdelphij	pr = cmd_step_right(&ncp, &width, NULL);
429161475Sdelphij	if (cmd_col + width >= sc_width)
43060786Sps		cmd_lshift();
431161475Sdelphij	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
43260786Sps		cmd_lshift();
433161475Sdelphij	cp = ncp;
434161475Sdelphij	cmd_col += width;
435161475Sdelphij	putstr(pr);
436161475Sdelphij	while (*cp != '\0')
437161475Sdelphij	{
438161475Sdelphij		pr = cmd_step_right(&ncp, &width, NULL);
439161475Sdelphij		if (width > 0)
440161475Sdelphij			break;
441161475Sdelphij		putstr(pr);
442161475Sdelphij		cp = ncp;
443161475Sdelphij	}
44460786Sps	return (CC_OK);
44560786Sps}
44660786Sps
44760786Sps/*
44860786Sps * Move cursor left one character.
44960786Sps */
45060786Sps	static int
45160786Spscmd_left()
45260786Sps{
453161475Sdelphij	char *ncp;
454161475Sdelphij	int width, bswidth;
45560786Sps
45660786Sps	if (cp <= cmdbuf)
45760786Sps	{
45860786Sps		/* Already at the beginning of the line */
45960786Sps		return (CC_OK);
46060786Sps	}
461161475Sdelphij	ncp = cp;
462161475Sdelphij	while (ncp > cmdbuf)
463161475Sdelphij	{
464161475Sdelphij		cmd_step_left(&ncp, &width, &bswidth);
465161475Sdelphij		if (width > 0)
466161475Sdelphij			break;
467161475Sdelphij	}
468161475Sdelphij	if (cmd_col < prompt_col + width)
46960786Sps		cmd_rshift();
470161475Sdelphij	cp = ncp;
471161475Sdelphij	cmd_col -= width;
472161475Sdelphij	while (bswidth-- > 0)
47360786Sps		putbs();
47460786Sps	return (CC_OK);
47560786Sps}
47660786Sps
47760786Sps/*
47860786Sps * Insert a char into the command buffer, at the current position.
47960786Sps */
48060786Sps	static int
481161475Sdelphijcmd_ichar(cs, clen)
482161475Sdelphij	char *cs;
483161475Sdelphij	int clen;
48460786Sps{
48560786Sps	char *s;
48660786Sps
487161475Sdelphij	if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
48860786Sps	{
489161475Sdelphij		/* No room in the command buffer for another char. */
49060786Sps		bell();
49160786Sps		return (CC_ERROR);
49260786Sps	}
49360786Sps
49460786Sps	/*
495161475Sdelphij	 * Make room for the new character (shift the tail of the buffer right).
49660786Sps	 */
49760786Sps	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
498161475Sdelphij		s[clen] = s[0];
49960786Sps	/*
500161475Sdelphij	 * Insert the character into the buffer.
501161475Sdelphij	 */
502161475Sdelphij	for (s = cp;  s < cp + clen;  s++)
503161475Sdelphij		*s = *cs++;
504161475Sdelphij	/*
50560786Sps	 * Reprint the tail of the line from the inserted char.
50660786Sps	 */
50760786Sps	cmd_repaint(cp);
50860786Sps	cmd_right();
50960786Sps	return (CC_OK);
51060786Sps}
51160786Sps
51260786Sps/*
51360786Sps * Backspace in the command buffer.
51460786Sps * Delete the char to the left of the cursor.
51560786Sps */
51660786Sps	static int
51760786Spscmd_erase()
51860786Sps{
51960786Sps	register char *s;
520161475Sdelphij	int clen;
52160786Sps
52260786Sps	if (cp == cmdbuf)
52360786Sps	{
52460786Sps		/*
52560786Sps		 * Backspace past beginning of the buffer:
52660786Sps		 * this usually means abort the command.
52760786Sps		 */
52860786Sps		return (CC_QUIT);
52960786Sps	}
53060786Sps	/*
53160786Sps	 * Move cursor left (to the char being erased).
53260786Sps	 */
533161475Sdelphij	s = cp;
53460786Sps	cmd_left();
535161475Sdelphij	clen = s - cp;
536161475Sdelphij
53760786Sps	/*
53860786Sps	 * Remove the char from the buffer (shift the buffer left).
53960786Sps	 */
540161475Sdelphij	for (s = cp;  ;  s++)
541161475Sdelphij	{
542161475Sdelphij		s[0] = s[clen];
543161475Sdelphij		if (s[0] == '\0')
544161475Sdelphij			break;
545161475Sdelphij	}
546161475Sdelphij
54760786Sps	/*
54860786Sps	 * Repaint the buffer after the erased char.
54960786Sps	 */
55060786Sps	cmd_repaint(cp);
55160786Sps
55260786Sps	/*
55360786Sps	 * We say that erasing the entire command string causes us
55460786Sps	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
55560786Sps	 */
55660786Sps	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
55760786Sps		return (CC_QUIT);
55860786Sps	return (CC_OK);
55960786Sps}
56060786Sps
56160786Sps/*
56260786Sps * Delete the char under the cursor.
56360786Sps */
56460786Sps	static int
56560786Spscmd_delete()
56660786Sps{
56760786Sps	if (*cp == '\0')
56860786Sps	{
569161475Sdelphij		/* At end of string; there is no char under the cursor. */
57060786Sps		return (CC_OK);
57160786Sps	}
57260786Sps	/*
57360786Sps	 * Move right, then use cmd_erase.
57460786Sps	 */
57560786Sps	cmd_right();
57660786Sps	cmd_erase();
57760786Sps	return (CC_OK);
57860786Sps}
57960786Sps
58060786Sps/*
58160786Sps * Delete the "word" to the left of the cursor.
58260786Sps */
58360786Sps	static int
58460786Spscmd_werase()
58560786Sps{
58660786Sps	if (cp > cmdbuf && cp[-1] == ' ')
58760786Sps	{
58860786Sps		/*
58960786Sps		 * If the char left of cursor is a space,
59060786Sps		 * erase all the spaces left of cursor (to the first non-space).
59160786Sps		 */
59260786Sps		while (cp > cmdbuf && cp[-1] == ' ')
59360786Sps			(void) cmd_erase();
59460786Sps	} else
59560786Sps	{
59660786Sps		/*
59760786Sps		 * If the char left of cursor is not a space,
59860786Sps		 * erase all the nonspaces left of cursor (the whole "word").
59960786Sps		 */
60060786Sps		while (cp > cmdbuf && cp[-1] != ' ')
60160786Sps			(void) cmd_erase();
60260786Sps	}
60360786Sps	return (CC_OK);
60460786Sps}
60560786Sps
60660786Sps/*
60760786Sps * Delete the "word" under the cursor.
60860786Sps */
60960786Sps	static int
61060786Spscmd_wdelete()
61160786Sps{
61260786Sps	if (*cp == ' ')
61360786Sps	{
61460786Sps		/*
61560786Sps		 * If the char under the cursor is a space,
61660786Sps		 * delete it and all the spaces right of cursor.
61760786Sps		 */
61860786Sps		while (*cp == ' ')
61960786Sps			(void) cmd_delete();
62060786Sps	} else
62160786Sps	{
62260786Sps		/*
62360786Sps		 * If the char under the cursor is not a space,
62460786Sps		 * delete it and all nonspaces right of cursor (the whole word).
62560786Sps		 */
62660786Sps		while (*cp != ' ' && *cp != '\0')
62760786Sps			(void) cmd_delete();
62860786Sps	}
62960786Sps	return (CC_OK);
63060786Sps}
63160786Sps
63260786Sps/*
63360786Sps * Delete all chars in the command buffer.
63460786Sps */
63560786Sps	static int
63660786Spscmd_kill()
63760786Sps{
63860786Sps	if (cmdbuf[0] == '\0')
63960786Sps	{
640161475Sdelphij		/* Buffer is already empty; abort the current command. */
64160786Sps		return (CC_QUIT);
64260786Sps	}
64360786Sps	cmd_offset = 0;
64460786Sps	cmd_home();
64560786Sps	*cp = '\0';
64660786Sps	cmd_repaint(cp);
64760786Sps
64860786Sps	/*
64960786Sps	 * We say that erasing the entire command string causes us
65060786Sps	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
65160786Sps	 */
65260786Sps	if (curr_cmdflags & CF_QUIT_ON_ERASE)
65360786Sps		return (CC_QUIT);
65460786Sps	return (CC_OK);
65560786Sps}
65660786Sps
65760786Sps/*
65860786Sps * Select an mlist structure to be the current command history.
65960786Sps */
66060786Sps	public void
66160786Spsset_mlist(mlist, cmdflags)
66260786Sps	void *mlist;
66360786Sps	int cmdflags;
66460786Sps{
665191930Sdelphij#if CMD_HISTORY
66660786Sps	curr_mlist = (struct mlist *) mlist;
66760786Sps	curr_cmdflags = cmdflags;
668161475Sdelphij
669161475Sdelphij	/* Make sure the next up-arrow moves to the last string in the mlist. */
670161475Sdelphij	if (curr_mlist != NULL)
671161475Sdelphij		curr_mlist->curr_mp = curr_mlist;
672191930Sdelphij#endif
67360786Sps}
67460786Sps
67560786Sps#if CMD_HISTORY
67660786Sps/*
67760786Sps * Move up or down in the currently selected command history list.
67860786Sps */
67960786Sps	static int
68060786Spscmd_updown(action)
68160786Sps	int action;
68260786Sps{
68360786Sps	char *s;
68460786Sps
68560786Sps	if (curr_mlist == NULL)
68660786Sps	{
68760786Sps		/*
68860786Sps		 * The current command has no history list.
68960786Sps		 */
69060786Sps		bell();
69160786Sps		return (CC_OK);
69260786Sps	}
69360786Sps	cmd_home();
69460786Sps	clear_eol();
69560786Sps	/*
69660786Sps	 * Move curr_mp to the next/prev entry.
69760786Sps	 */
69860786Sps	if (action == EC_UP)
69960786Sps		curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
70060786Sps	else
70160786Sps		curr_mlist->curr_mp = curr_mlist->curr_mp->next;
70260786Sps	/*
70360786Sps	 * Copy the entry into cmdbuf and echo it on the screen.
70460786Sps	 */
70560786Sps	s = curr_mlist->curr_mp->string;
70660786Sps	if (s == NULL)
70760786Sps		s = "";
708161475Sdelphij	strcpy(cmdbuf, s);
709161475Sdelphij	for (cp = cmdbuf;  *cp != '\0';  )
71060786Sps		cmd_right();
71160786Sps	return (CC_OK);
71260786Sps}
71360786Sps#endif
71460786Sps
71560786Sps/*
71660786Sps * Add a string to a history list.
71760786Sps */
71860786Sps	public void
71960786Spscmd_addhist(mlist, cmd)
72060786Sps	struct mlist *mlist;
72160786Sps	char *cmd;
72260786Sps{
72360786Sps#if CMD_HISTORY
72460786Sps	struct mlist *ml;
72560786Sps
72660786Sps	/*
72760786Sps	 * Don't save a trivial command.
72860786Sps	 */
72960786Sps	if (strlen(cmd) == 0)
73060786Sps		return;
731161475Sdelphij
73260786Sps	/*
733161475Sdelphij	 * Save the command unless it's a duplicate of the
734161475Sdelphij	 * last command in the history.
73560786Sps	 */
736161475Sdelphij	ml = mlist->prev;
737161475Sdelphij	if (ml == mlist || strcmp(ml->string, cmd) != 0)
73860786Sps	{
73960786Sps		/*
74060786Sps		 * Did not find command in history.
74160786Sps		 * Save the command and put it at the end of the history list.
74260786Sps		 */
74360786Sps		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
74460786Sps		ml->string = save(cmd);
74560786Sps		ml->next = mlist;
74660786Sps		ml->prev = mlist->prev;
74760786Sps		mlist->prev->next = ml;
74860786Sps		mlist->prev = ml;
74960786Sps	}
75060786Sps	/*
75160786Sps	 * Point to the cmd just after the just-accepted command.
75260786Sps	 * Thus, an UPARROW will always retrieve the previous command.
75360786Sps	 */
75460786Sps	mlist->curr_mp = ml->next;
75560786Sps#endif
75660786Sps}
75760786Sps
75860786Sps/*
75960786Sps * Accept the command in the command buffer.
76060786Sps * Add it to the currently selected history list.
76160786Sps */
76260786Sps	public void
76360786Spscmd_accept()
76460786Sps{
76560786Sps#if CMD_HISTORY
76660786Sps	/*
76760786Sps	 * Nothing to do if there is no currently selected history list.
76860786Sps	 */
76960786Sps	if (curr_mlist == NULL)
77060786Sps		return;
77160786Sps	cmd_addhist(curr_mlist, cmdbuf);
772170256Sdelphij	curr_mlist->modified = 1;
77360786Sps#endif
77460786Sps}
77560786Sps
77660786Sps/*
77760786Sps * Try to perform a line-edit function on the command buffer,
77860786Sps * using a specified char as a line-editing command.
77960786Sps * Returns:
78060786Sps *	CC_PASS	The char does not invoke a line edit function.
78160786Sps *	CC_OK	Line edit function done.
78260786Sps *	CC_QUIT	The char requests the current command to be aborted.
78360786Sps */
78460786Sps	static int
78560786Spscmd_edit(c)
78660786Sps	int c;
78760786Sps{
78860786Sps	int action;
78960786Sps	int flags;
79060786Sps
79160786Sps#if TAB_COMPLETE_FILENAME
79260786Sps#define	not_in_completion()	in_completion = 0
79360786Sps#else
79460786Sps#define	not_in_completion()
79560786Sps#endif
79660786Sps
79760786Sps	/*
79860786Sps	 * See if the char is indeed a line-editing command.
79960786Sps	 */
80060786Sps	flags = 0;
80160786Sps#if CMD_HISTORY
80260786Sps	if (curr_mlist == NULL)
80360786Sps		/*
80460786Sps		 * No current history; don't accept history manipulation cmds.
80560786Sps		 */
80660786Sps		flags |= EC_NOHISTORY;
80760786Sps#endif
80860786Sps#if TAB_COMPLETE_FILENAME
80960786Sps	if (curr_mlist == ml_search)
81060786Sps		/*
81160786Sps		 * In a search command; don't accept file-completion cmds.
81260786Sps		 */
81360786Sps		flags |= EC_NOCOMPLETE;
81460786Sps#endif
81560786Sps
81660786Sps	action = editchar(c, flags);
81760786Sps
81860786Sps	switch (action)
81960786Sps	{
82060786Sps	case EC_RIGHT:
82160786Sps		not_in_completion();
82260786Sps		return (cmd_right());
82360786Sps	case EC_LEFT:
82460786Sps		not_in_completion();
82560786Sps		return (cmd_left());
82660786Sps	case EC_W_RIGHT:
82760786Sps		not_in_completion();
82860786Sps		while (*cp != '\0' && *cp != ' ')
82960786Sps			cmd_right();
83060786Sps		while (*cp == ' ')
83160786Sps			cmd_right();
83260786Sps		return (CC_OK);
83360786Sps	case EC_W_LEFT:
83460786Sps		not_in_completion();
83560786Sps		while (cp > cmdbuf && cp[-1] == ' ')
83660786Sps			cmd_left();
83760786Sps		while (cp > cmdbuf && cp[-1] != ' ')
83860786Sps			cmd_left();
83960786Sps		return (CC_OK);
84060786Sps	case EC_HOME:
84160786Sps		not_in_completion();
84260786Sps		cmd_offset = 0;
84360786Sps		cmd_home();
84460786Sps		cmd_repaint(cp);
84560786Sps		return (CC_OK);
84660786Sps	case EC_END:
84760786Sps		not_in_completion();
84860786Sps		while (*cp != '\0')
84960786Sps			cmd_right();
85060786Sps		return (CC_OK);
85160786Sps	case EC_INSERT:
85260786Sps		not_in_completion();
85360786Sps		return (CC_OK);
85460786Sps	case EC_BACKSPACE:
85560786Sps		not_in_completion();
85660786Sps		return (cmd_erase());
85760786Sps	case EC_LINEKILL:
85860786Sps		not_in_completion();
85960786Sps		return (cmd_kill());
86060786Sps	case EC_W_BACKSPACE:
86160786Sps		not_in_completion();
86260786Sps		return (cmd_werase());
86360786Sps	case EC_DELETE:
86460786Sps		not_in_completion();
86560786Sps		return (cmd_delete());
86660786Sps	case EC_W_DELETE:
86760786Sps		not_in_completion();
86860786Sps		return (cmd_wdelete());
86960786Sps	case EC_LITERAL:
87060786Sps		literal = 1;
87160786Sps		return (CC_OK);
87260786Sps#if CMD_HISTORY
87360786Sps	case EC_UP:
87460786Sps	case EC_DOWN:
87560786Sps		not_in_completion();
87660786Sps		return (cmd_updown(action));
87760786Sps#endif
87860786Sps#if TAB_COMPLETE_FILENAME
87960786Sps	case EC_F_COMPLETE:
88060786Sps	case EC_B_COMPLETE:
88160786Sps	case EC_EXPAND:
88260786Sps		return (cmd_complete(action));
88360786Sps#endif
88460786Sps	case EC_NOACTION:
88560786Sps		return (CC_OK);
88660786Sps	default:
88760786Sps		not_in_completion();
88860786Sps		return (CC_PASS);
88960786Sps	}
89060786Sps}
89160786Sps
89260786Sps#if TAB_COMPLETE_FILENAME
89360786Sps/*
89460786Sps * Insert a string into the command buffer, at the current position.
89560786Sps */
89660786Sps	static int
89760786Spscmd_istr(str)
89860786Sps	char *str;
89960786Sps{
90060786Sps	char *s;
90160786Sps	int action;
902161475Sdelphij	char *endline = str + strlen(str);
90360786Sps
904161475Sdelphij	for (s = str;  *s != '\0';  )
90560786Sps	{
906161475Sdelphij		char *os = s;
907161475Sdelphij		step_char(&s, +1, endline);
908161475Sdelphij		action = cmd_ichar(os, s - os);
90960786Sps		if (action != CC_OK)
91060786Sps		{
91160786Sps			bell();
91260786Sps			return (action);
91360786Sps		}
91460786Sps	}
91560786Sps	return (CC_OK);
91660786Sps}
91760786Sps
91860786Sps/*
91960786Sps * Find the beginning and end of the "current" word.
92060786Sps * This is the word which the cursor (cp) is inside or at the end of.
92160786Sps * Return pointer to the beginning of the word and put the
92260786Sps * cursor at the end of the word.
92360786Sps */
92460786Sps	static char *
92560786Spsdelimit_word()
92660786Sps{
92760786Sps	char *word;
92860786Sps#if SPACES_IN_FILENAMES
92960786Sps	char *p;
930128345Stjr	int delim_quoted = 0;
931128345Stjr	int meta_quoted = 0;
932128345Stjr	char *esc = get_meta_escape();
933128345Stjr	int esclen = strlen(esc);
93460786Sps#endif
93560786Sps
93660786Sps	/*
93760786Sps	 * Move cursor to end of word.
93860786Sps	 */
93960786Sps	if (*cp != ' ' && *cp != '\0')
94060786Sps	{
94160786Sps		/*
94260786Sps		 * Cursor is on a nonspace.
94360786Sps		 * Move cursor right to the next space.
94460786Sps		 */
94560786Sps		while (*cp != ' ' && *cp != '\0')
94660786Sps			cmd_right();
94760786Sps	} else if (cp > cmdbuf && cp[-1] != ' ')
94860786Sps	{
94960786Sps		/*
95060786Sps		 * Cursor is on a space, and char to the left is a nonspace.
95160786Sps		 * We're already at the end of the word.
95260786Sps		 */
95360786Sps		;
954128345Stjr#if 0
95560786Sps	} else
95660786Sps	{
95760786Sps		/*
95860786Sps		 * Cursor is on a space and char to the left is a space.
95960786Sps		 * Huh? There's no word here.
96060786Sps		 */
96160786Sps		return (NULL);
962128345Stjr#endif
96360786Sps	}
96460786Sps	/*
965128345Stjr	 * Find the beginning of the word which the cursor is in.
96660786Sps	 */
96760786Sps	if (cp == cmdbuf)
96860786Sps		return (NULL);
96960786Sps#if SPACES_IN_FILENAMES
97060786Sps	/*
97160786Sps	 * If we have an unbalanced quote (that is, an open quote
97260786Sps	 * without a corresponding close quote), we return everything
97360786Sps	 * from the open quote, including spaces.
97460786Sps	 */
975128345Stjr	for (word = cmdbuf;  word < cp;  word++)
976128345Stjr		if (*word != ' ')
977128345Stjr			break;
978128345Stjr	if (word >= cp)
979128345Stjr		return (cp);
98060786Sps	for (p = cmdbuf;  p < cp;  p++)
98160786Sps	{
982128345Stjr		if (meta_quoted)
98360786Sps		{
984128345Stjr			meta_quoted = 0;
985128345Stjr		} else if (esclen > 0 && p + esclen < cp &&
986128345Stjr		           strncmp(p, esc, esclen) == 0)
98760786Sps		{
988128345Stjr			meta_quoted = 1;
989128345Stjr			p += esclen - 1;
990128345Stjr		} else if (delim_quoted)
991128345Stjr		{
992128345Stjr			if (*p == closequote)
993128345Stjr				delim_quoted = 0;
994128345Stjr		} else /* (!delim_quoted) */
995128345Stjr		{
996128345Stjr			if (*p == openquote)
997128345Stjr				delim_quoted = 1;
998128345Stjr			else if (*p == ' ')
999128345Stjr				word = p+1;
100060786Sps		}
100160786Sps	}
100260786Sps#endif
100360786Sps	return (word);
100460786Sps}
100560786Sps
100660786Sps/*
100760786Sps * Set things up to enter completion mode.
100860786Sps * Expand the word under the cursor into a list of filenames
100960786Sps * which start with that word, and set tk_text to that list.
101060786Sps */
101160786Sps	static void
101260786Spsinit_compl()
101360786Sps{
101460786Sps	char *word;
101560786Sps	char c;
101660786Sps
101760786Sps	/*
101860786Sps	 * Get rid of any previous tk_text.
101960786Sps	 */
102060786Sps	if (tk_text != NULL)
102160786Sps	{
102260786Sps		free(tk_text);
102360786Sps		tk_text = NULL;
102460786Sps	}
102560786Sps	/*
102660786Sps	 * Find the original (uncompleted) word in the command buffer.
102760786Sps	 */
102860786Sps	word = delimit_word();
102960786Sps	if (word == NULL)
103060786Sps		return;
103160786Sps	/*
103260786Sps	 * Set the insertion point to the point in the command buffer
103360786Sps	 * where the original (uncompleted) word now sits.
103460786Sps	 */
103560786Sps	tk_ipoint = word;
103660786Sps	/*
103760786Sps	 * Save the original (uncompleted) word
103860786Sps	 */
103960786Sps	if (tk_original != NULL)
104060786Sps		free(tk_original);
104160786Sps	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
104260786Sps	strncpy(tk_original, word, cp-word);
104360786Sps	/*
104460786Sps	 * Get the expanded filename.
104560786Sps	 * This may result in a single filename, or
104660786Sps	 * a blank-separated list of filenames.
104760786Sps	 */
104860786Sps	c = *cp;
104960786Sps	*cp = '\0';
1050128345Stjr	if (*word != openquote)
1051128345Stjr	{
1052128345Stjr		tk_text = fcomplete(word);
1053128345Stjr	} else
1054128345Stjr	{
1055128345Stjr		char *qword = shell_quote(word+1);
1056128345Stjr		if (qword == NULL)
1057128345Stjr			tk_text = fcomplete(word+1);
1058128345Stjr		else
1059128345Stjr		{
1060128345Stjr			tk_text = fcomplete(qword);
1061128345Stjr			free(qword);
1062128345Stjr		}
1063128345Stjr	}
106460786Sps	*cp = c;
106560786Sps}
106660786Sps
106760786Sps/*
106860786Sps * Return the next word in the current completion list.
106960786Sps */
107060786Sps	static char *
107160786Spsnext_compl(action, prev)
107260786Sps	int action;
107360786Sps	char *prev;
107460786Sps{
107560786Sps	switch (action)
107660786Sps	{
107760786Sps	case EC_F_COMPLETE:
107860786Sps		return (forw_textlist(&tk_tlist, prev));
107960786Sps	case EC_B_COMPLETE:
108060786Sps		return (back_textlist(&tk_tlist, prev));
108160786Sps	}
108260786Sps	/* Cannot happen */
108360786Sps	return ("?");
108460786Sps}
108560786Sps
108660786Sps/*
108760786Sps * Complete the filename before (or under) the cursor.
108860786Sps * cmd_complete may be called multiple times.  The global in_completion
108960786Sps * remembers whether this call is the first time (create the list),
109060786Sps * or a subsequent time (step thru the list).
109160786Sps */
109260786Sps	static int
109360786Spscmd_complete(action)
109460786Sps	int action;
109560786Sps{
109660786Sps	char *s;
109760786Sps
109860786Sps	if (!in_completion || action == EC_EXPAND)
109960786Sps	{
110060786Sps		/*
110160786Sps		 * Expand the word under the cursor and
110260786Sps		 * use the first word in the expansion
110360786Sps		 * (or the entire expansion if we're doing EC_EXPAND).
110460786Sps		 */
110560786Sps		init_compl();
110660786Sps		if (tk_text == NULL)
110760786Sps		{
110860786Sps			bell();
110960786Sps			return (CC_OK);
111060786Sps		}
111160786Sps		if (action == EC_EXPAND)
111260786Sps		{
111360786Sps			/*
111460786Sps			 * Use the whole list.
111560786Sps			 */
111660786Sps			tk_trial = tk_text;
111760786Sps		} else
111860786Sps		{
111960786Sps			/*
112060786Sps			 * Use the first filename in the list.
112160786Sps			 */
112260786Sps			in_completion = 1;
112360786Sps			init_textlist(&tk_tlist, tk_text);
112460786Sps			tk_trial = next_compl(action, (char*)NULL);
112560786Sps		}
112660786Sps	} else
112760786Sps	{
112860786Sps		/*
112960786Sps		 * We already have a completion list.
113060786Sps		 * Use the next/previous filename from the list.
113160786Sps		 */
113260786Sps		tk_trial = next_compl(action, tk_trial);
113360786Sps	}
113460786Sps
113560786Sps  	/*
113660786Sps  	 * Remove the original word, or the previous trial completion.
113760786Sps  	 */
113860786Sps	while (cp > tk_ipoint)
113960786Sps		(void) cmd_erase();
114060786Sps
114160786Sps	if (tk_trial == NULL)
114260786Sps	{
114360786Sps		/*
114460786Sps		 * There are no more trial completions.
114560786Sps		 * Insert the original (uncompleted) filename.
114660786Sps		 */
114760786Sps		in_completion = 0;
114860786Sps		if (cmd_istr(tk_original) != CC_OK)
114960786Sps			goto fail;
115060786Sps	} else
115160786Sps	{
115260786Sps		/*
115360786Sps		 * Insert trial completion.
115460786Sps		 */
115560786Sps		if (cmd_istr(tk_trial) != CC_OK)
115660786Sps			goto fail;
115760786Sps		/*
115860786Sps		 * If it is a directory, append a slash.
115960786Sps		 */
116060786Sps		if (is_dir(tk_trial))
116160786Sps		{
116260786Sps			if (cp > cmdbuf && cp[-1] == closequote)
116360786Sps				(void) cmd_erase();
116460786Sps			s = lgetenv("LESSSEPARATOR");
116560786Sps			if (s == NULL)
116660786Sps				s = PATHNAME_SEP;
116760786Sps			if (cmd_istr(s) != CC_OK)
116860786Sps				goto fail;
116960786Sps		}
117060786Sps	}
117160786Sps
117260786Sps	return (CC_OK);
117360786Sps
117460786Spsfail:
117560786Sps	in_completion = 0;
117660786Sps	bell();
117760786Sps	return (CC_OK);
117860786Sps}
117960786Sps
118060786Sps#endif /* TAB_COMPLETE_FILENAME */
118160786Sps
118260786Sps/*
118360786Sps * Process a single character of a multi-character command, such as
118460786Sps * a number, or the pattern of a search command.
118560786Sps * Returns:
118660786Sps *	CC_OK		The char was accepted.
118760786Sps *	CC_QUIT		The char requests the command to be aborted.
118860786Sps *	CC_ERROR	The char could not be accepted due to an error.
118960786Sps */
119060786Sps	public int
119160786Spscmd_char(c)
119260786Sps	int c;
119360786Sps{
119460786Sps	int action;
1195161475Sdelphij	int len;
119660786Sps
1197161475Sdelphij	if (!utf_mode)
1198161475Sdelphij	{
1199161475Sdelphij		cmd_mbc_buf[0] = c;
1200161475Sdelphij		len = 1;
1201161475Sdelphij	} else
1202161475Sdelphij	{
1203161475Sdelphij		/* Perform strict validation in all possible cases.  */
1204161475Sdelphij		if (cmd_mbc_buf_len == 0)
1205161475Sdelphij		{
1206161475Sdelphij		 retry:
1207161475Sdelphij			cmd_mbc_buf_index = 1;
1208161475Sdelphij			*cmd_mbc_buf = c;
1209161475Sdelphij			if (IS_ASCII_OCTET(c))
1210161475Sdelphij				cmd_mbc_buf_len = 1;
1211161475Sdelphij			else if (IS_UTF8_LEAD(c))
1212161475Sdelphij			{
1213161475Sdelphij				cmd_mbc_buf_len = utf_len(c);
1214161475Sdelphij				return (CC_OK);
1215161475Sdelphij			} else
1216161475Sdelphij			{
1217161475Sdelphij				/* UTF8_INVALID or stray UTF8_TRAIL */
1218161475Sdelphij				bell();
1219161475Sdelphij				return (CC_ERROR);
1220161475Sdelphij			}
1221161475Sdelphij		} else if (IS_UTF8_TRAIL(c))
1222161475Sdelphij		{
1223161475Sdelphij			cmd_mbc_buf[cmd_mbc_buf_index++] = c;
1224161475Sdelphij			if (cmd_mbc_buf_index < cmd_mbc_buf_len)
1225161475Sdelphij				return (CC_OK);
1226161475Sdelphij			if (!is_utf8_well_formed(cmd_mbc_buf))
1227161475Sdelphij			{
1228161475Sdelphij				/* complete, but not well formed (non-shortest form), sequence */
1229161475Sdelphij				cmd_mbc_buf_len = 0;
1230161475Sdelphij				bell();
1231161475Sdelphij				return (CC_ERROR);
1232161475Sdelphij			}
1233161475Sdelphij		} else
1234161475Sdelphij		{
1235161475Sdelphij			/* Flush incomplete (truncated) sequence.  */
1236161475Sdelphij			cmd_mbc_buf_len = 0;
1237161475Sdelphij			bell();
1238161475Sdelphij			/* Handle new char.  */
1239161475Sdelphij			goto retry;
1240161475Sdelphij		}
1241161475Sdelphij
1242161475Sdelphij		len = cmd_mbc_buf_len;
1243161475Sdelphij		cmd_mbc_buf_len = 0;
1244161475Sdelphij	}
1245161475Sdelphij
124660786Sps	if (literal)
124760786Sps	{
124860786Sps		/*
124960786Sps		 * Insert the char, even if it is a line-editing char.
125060786Sps		 */
125160786Sps		literal = 0;
1252161475Sdelphij		return (cmd_ichar(cmd_mbc_buf, len));
125360786Sps	}
125460786Sps
125560786Sps	/*
1256161475Sdelphij	 * See if it is a line-editing character.
125760786Sps	 */
1258161475Sdelphij	if (in_mca() && len == 1)
125960786Sps	{
126060786Sps		action = cmd_edit(c);
126160786Sps		switch (action)
126260786Sps		{
126360786Sps		case CC_OK:
126460786Sps		case CC_QUIT:
126560786Sps			return (action);
126660786Sps		case CC_PASS:
126760786Sps			break;
126860786Sps		}
126960786Sps	}
127060786Sps
127160786Sps	/*
127260786Sps	 * Insert the char into the command buffer.
127360786Sps	 */
1274161475Sdelphij	return (cmd_ichar(cmd_mbc_buf, len));
127560786Sps}
127660786Sps
127760786Sps/*
127860786Sps * Return the number currently in the command buffer.
127960786Sps */
1280128345Stjr	public LINENUM
1281170256Sdelphijcmd_int(frac)
1282170256Sdelphij	long *frac;
128360786Sps{
1284170256Sdelphij	char *p;
1285128345Stjr	LINENUM n = 0;
1286170256Sdelphij	int err;
1287128345Stjr
1288170256Sdelphij	for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
1289170256Sdelphij		n = (n * 10) + (*p - '0');
1290170256Sdelphij	*frac = 0;
1291170256Sdelphij	if (*p++ == '.')
1292170256Sdelphij	{
1293170256Sdelphij		*frac = getfraction(&p, NULL, &err);
1294170256Sdelphij		/* {{ do something if err is set? }} */
1295170256Sdelphij	}
1296128345Stjr	return (n);
129760786Sps}
129860786Sps
129960786Sps/*
130060786Sps * Return a pointer to the command buffer.
130160786Sps */
130260786Sps	public char *
130360786Spsget_cmdbuf()
130460786Sps{
130560786Sps	return (cmdbuf);
130660786Sps}
1307161475Sdelphij
1308191930Sdelphij#if CMD_HISTORY
1309170256Sdelphij/*
1310170256Sdelphij * Return the last (most recent) string in the current command history.
1311170256Sdelphij */
1312170256Sdelphij	public char *
1313170256Sdelphijcmd_lastpattern()
1314170256Sdelphij{
1315170256Sdelphij	if (curr_mlist == NULL)
1316170256Sdelphij		return (NULL);
1317170256Sdelphij	return (curr_mlist->curr_mp->prev->string);
1318170256Sdelphij}
1319191930Sdelphij#endif
1320170256Sdelphij
1321161475Sdelphij#if CMD_HISTORY
1322161475Sdelphij/*
1323161475Sdelphij * Get the name of the history file.
1324161475Sdelphij */
1325161475Sdelphij	static char *
1326161475Sdelphijhistfile_name()
1327161475Sdelphij{
1328161475Sdelphij	char *home;
1329161475Sdelphij	char *name;
1330161475Sdelphij	int len;
1331161475Sdelphij
1332161475Sdelphij	/* See if filename is explicitly specified by $LESSHISTFILE. */
1333161475Sdelphij	name = lgetenv("LESSHISTFILE");
1334161475Sdelphij	if (name != NULL && *name != '\0')
1335161475Sdelphij	{
1336170256Sdelphij		if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
1337161475Sdelphij			/* $LESSHISTFILE == "-" means don't use a history file. */
1338161475Sdelphij			return (NULL);
1339161475Sdelphij		return (save(name));
1340161475Sdelphij	}
1341161475Sdelphij
1342161475Sdelphij	/* Otherwise, file is in $HOME. */
1343161475Sdelphij	home = lgetenv("HOME");
1344161475Sdelphij	if (home == NULL || *home == '\0')
1345161475Sdelphij	{
1346161475Sdelphij#if OS2
1347161475Sdelphij		home = lgetenv("INIT");
1348161475Sdelphij		if (home == NULL || *home == '\0')
1349161475Sdelphij#endif
1350161475Sdelphij			return (NULL);
1351161475Sdelphij	}
1352161475Sdelphij	len = strlen(home) + strlen(LESSHISTFILE) + 2;
1353161475Sdelphij	name = (char *) ecalloc(len, sizeof(char));
1354161475Sdelphij	SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
1355161475Sdelphij	return (name);
1356161475Sdelphij}
1357161475Sdelphij#endif /* CMD_HISTORY */
1358161475Sdelphij
1359161475Sdelphij/*
1360161475Sdelphij * Initialize history from a .lesshist file.
1361161475Sdelphij */
1362161475Sdelphij	public void
1363161475Sdelphijinit_cmdhist()
1364161475Sdelphij{
1365161475Sdelphij#if CMD_HISTORY
1366161475Sdelphij	struct mlist *ml = NULL;
1367161475Sdelphij	char line[CMDBUF_SIZE];
1368161475Sdelphij	char *filename;
1369161475Sdelphij	FILE *f;
1370161475Sdelphij	char *p;
1371161475Sdelphij
1372161475Sdelphij	filename = histfile_name();
1373161475Sdelphij	if (filename == NULL)
1374161475Sdelphij		return;
1375161475Sdelphij	f = fopen(filename, "r");
1376161475Sdelphij	free(filename);
1377161475Sdelphij	if (f == NULL)
1378161475Sdelphij		return;
1379161475Sdelphij	if (fgets(line, sizeof(line), f) == NULL ||
1380161475Sdelphij	    strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
1381161475Sdelphij	{
1382161475Sdelphij		fclose(f);
1383161475Sdelphij		return;
1384161475Sdelphij	}
1385161475Sdelphij	while (fgets(line, sizeof(line), f) != NULL)
1386161475Sdelphij	{
1387161475Sdelphij		for (p = line;  *p != '\0';  p++)
1388161475Sdelphij		{
1389161475Sdelphij			if (*p == '\n' || *p == '\r')
1390161475Sdelphij			{
1391161475Sdelphij				*p = '\0';
1392161475Sdelphij				break;
1393161475Sdelphij			}
1394161475Sdelphij		}
1395161475Sdelphij		if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1396161475Sdelphij			ml = &mlist_search;
1397170964Sdelphij		else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
1398170964Sdelphij		{
1399161475Sdelphij#if SHELL_ESCAPE || PIPEC
1400161475Sdelphij			ml = &mlist_shell;
1401170964Sdelphij#else
1402170964Sdelphij			ml = NULL;
1403161475Sdelphij#endif
1404170964Sdelphij		} else if (*line == '"')
1405161475Sdelphij		{
1406161475Sdelphij			if (ml != NULL)
1407161475Sdelphij				cmd_addhist(ml, line+1);
1408161475Sdelphij		}
1409161475Sdelphij	}
1410161475Sdelphij	fclose(f);
1411161475Sdelphij#endif /* CMD_HISTORY */
1412161475Sdelphij}
1413161475Sdelphij
1414161475Sdelphij/*
1415161475Sdelphij *
1416161475Sdelphij */
1417161475Sdelphij#if CMD_HISTORY
1418161475Sdelphij	static void
1419161475Sdelphijsave_mlist(ml, f)
1420161475Sdelphij	struct mlist *ml;
1421161475Sdelphij	FILE *f;
1422161475Sdelphij{
1423161475Sdelphij	int histsize = 0;
1424161475Sdelphij	int n;
1425161475Sdelphij	char *s;
1426161475Sdelphij
1427161475Sdelphij	s = lgetenv("LESSHISTSIZE");
1428161475Sdelphij	if (s != NULL)
1429161475Sdelphij		histsize = atoi(s);
1430161475Sdelphij	if (histsize == 0)
1431161475Sdelphij		histsize = 100;
1432161475Sdelphij
1433161475Sdelphij	ml = ml->prev;
1434161475Sdelphij	for (n = 0;  n < histsize;  n++)
1435161475Sdelphij	{
1436161475Sdelphij		if (ml->string == NULL)
1437161475Sdelphij			break;
1438161475Sdelphij		ml = ml->prev;
1439161475Sdelphij	}
1440161475Sdelphij	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1441161475Sdelphij		fprintf(f, "\"%s\n", ml->string);
1442161475Sdelphij}
1443161475Sdelphij#endif /* CMD_HISTORY */
1444161475Sdelphij
1445161475Sdelphij/*
1446161475Sdelphij *
1447161475Sdelphij */
1448161475Sdelphij	public void
1449161475Sdelphijsave_cmdhist()
1450161475Sdelphij{
1451161475Sdelphij#if CMD_HISTORY
1452161475Sdelphij	char *filename;
1453161475Sdelphij	FILE *f;
1454170964Sdelphij	int modified = 0;
1455161475Sdelphij
1456161475Sdelphij	filename = histfile_name();
1457161475Sdelphij	if (filename == NULL)
1458161475Sdelphij		return;
1459170964Sdelphij	if (mlist_search.modified)
1460170964Sdelphij		modified = 1;
1461170964Sdelphij#if SHELL_ESCAPE || PIPEC
1462170964Sdelphij	if (mlist_shell.modified)
1463170964Sdelphij		modified = 1;
1464170964Sdelphij#endif
1465170964Sdelphij	if (!modified)
1466170256Sdelphij		return;
1467161475Sdelphij	f = fopen(filename, "w");
1468161475Sdelphij	free(filename);
1469161475Sdelphij	if (f == NULL)
1470161475Sdelphij		return;
1471161475Sdelphij#if HAVE_FCHMOD
1472191930Sdelphij{
1473161475Sdelphij	/* Make history file readable only by owner. */
1474191930Sdelphij	int do_chmod = 1;
1475191930Sdelphij#if HAVE_STAT
1476191930Sdelphij	struct stat statbuf;
1477191930Sdelphij	int r = fstat(fileno(f), &statbuf);
1478191930Sdelphij	if (r < 0 || !S_ISREG(statbuf.st_mode))
1479191930Sdelphij		/* Don't chmod if not a regular file. */
1480191930Sdelphij		do_chmod = 0;
1481161475Sdelphij#endif
1482191930Sdelphij	if (do_chmod)
1483191930Sdelphij		fchmod(fileno(f), 0600);
1484191930Sdelphij}
1485191930Sdelphij#endif
1486161475Sdelphij
1487161475Sdelphij	fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
1488161475Sdelphij
1489161475Sdelphij	fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1490161475Sdelphij	save_mlist(&mlist_search, f);
1491161475Sdelphij
1492161475Sdelphij#if SHELL_ESCAPE || PIPEC
1493161475Sdelphij	fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1494161475Sdelphij	save_mlist(&mlist_shell, f);
1495161475Sdelphij#endif
1496161475Sdelphij
1497161475Sdelphij	fclose(f);
1498161475Sdelphij#endif /* CMD_HISTORY */
1499161475Sdelphij}
1500