xref: /freebsd-src/contrib/less/cmdbuf.c (revision c77c488926555ca344ae3a417544cf7a720e1de1)
1a5f0fb15SPaul Saab /*
2*c77c4889SXin LI  * Copyright (C) 1984-2024  Mark Nudelman
3a5f0fb15SPaul Saab  *
4a5f0fb15SPaul Saab  * You may distribute under the terms of either the GNU General Public
5a5f0fb15SPaul Saab  * License or the Less License, as specified in the README file.
6a5f0fb15SPaul Saab  *
796e55cc7SXin LI  * For more information, see the README file.
8a5f0fb15SPaul Saab  */
9a5f0fb15SPaul Saab 
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab /*
12a5f0fb15SPaul Saab  * Functions which manipulate the command buffer.
13a5f0fb15SPaul Saab  * Used only by command() and related functions.
14a5f0fb15SPaul Saab  */
15a5f0fb15SPaul Saab 
16a5f0fb15SPaul Saab #include "less.h"
17a5f0fb15SPaul Saab #include "cmd.h"
186dcb072bSXin LI #include "charset.h"
196dcb072bSXin LI #if HAVE_STAT
206dcb072bSXin LI #include <sys/stat.h>
216dcb072bSXin LI #endif
22a5f0fb15SPaul Saab 
23a5f0fb15SPaul Saab extern int sc_width;
246dcb072bSXin LI extern int utf_mode;
25b7780dbeSXin LI extern int no_hist_dups;
26b7780dbeSXin LI extern int marks_modified;
27a5f0fb15SPaul Saab 
28a5f0fb15SPaul Saab static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
29a5f0fb15SPaul Saab static int cmd_col;              /* Current column of the cursor */
30a5f0fb15SPaul Saab static int prompt_col;           /* Column of cursor just after prompt */
31a5f0fb15SPaul Saab static char *cp;                 /* Pointer into cmdbuf */
32a5f0fb15SPaul Saab static int cmd_offset;           /* Index into cmdbuf of first displayed char */
33*c77c4889SXin LI static lbool literal;            /* Next input char should not be interpreted */
34*c77c4889SXin LI static size_t updown_match;      /* Prefix length in up/down movement */
35*c77c4889SXin LI static lbool have_updown_match = FALSE;
36a5f0fb15SPaul Saab 
37a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME
38d713e089SXin LI static int cmd_complete(int action);
39a5f0fb15SPaul Saab /*
40a5f0fb15SPaul Saab  * These variables are statics used by cmd_complete.
41a5f0fb15SPaul Saab  */
42*c77c4889SXin LI static lbool in_completion = FALSE;
43a5f0fb15SPaul Saab static char *tk_text;
44a5f0fb15SPaul Saab static char *tk_original;
45*c77c4889SXin LI static constant char *tk_ipoint;
46*c77c4889SXin LI static constant char *tk_trial = NULL;
47a5f0fb15SPaul Saab static struct textlist tk_tlist;
48a5f0fb15SPaul Saab #endif
49a5f0fb15SPaul Saab 
50a5f0fb15SPaul Saab static int cmd_left();
51a5f0fb15SPaul Saab static int cmd_right();
52a5f0fb15SPaul Saab 
53a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
54a5f0fb15SPaul Saab public char openquote = '"';
55a5f0fb15SPaul Saab public char closequote = '"';
56a5f0fb15SPaul Saab #endif
57a5f0fb15SPaul Saab 
58a5f0fb15SPaul Saab #if CMD_HISTORY
596dcb072bSXin LI 
606dcb072bSXin LI /* History file */
616dcb072bSXin LI #define HISTFILE_FIRST_LINE      ".less-history-file:"
626dcb072bSXin LI #define HISTFILE_SEARCH_SECTION  ".search"
636dcb072bSXin LI #define HISTFILE_SHELL_SECTION   ".shell"
64b7780dbeSXin LI #define HISTFILE_MARK_SECTION    ".mark"
656dcb072bSXin LI 
66a5f0fb15SPaul Saab /*
67a5f0fb15SPaul Saab  * A mlist structure represents a command history.
68a5f0fb15SPaul Saab  */
69a5f0fb15SPaul Saab struct mlist
70a5f0fb15SPaul Saab {
71a5f0fb15SPaul Saab 	struct mlist *next;
72a5f0fb15SPaul Saab 	struct mlist *prev;
73a5f0fb15SPaul Saab 	struct mlist *curr_mp;
74a5f0fb15SPaul Saab 	char *string;
75*c77c4889SXin LI 	lbool modified;
76a5f0fb15SPaul Saab };
77a5f0fb15SPaul Saab 
78a5f0fb15SPaul Saab /*
79a5f0fb15SPaul Saab  * These are the various command histories that exist.
80a5f0fb15SPaul Saab  */
81a5f0fb15SPaul Saab struct mlist mlist_search =
827f074f9cSXin LI 	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
83f6b74a7dSXin LI public void *ml_search = (void *) &mlist_search;
84a5f0fb15SPaul Saab 
85a5f0fb15SPaul Saab struct mlist mlist_examine =
867f074f9cSXin LI 	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
87f6b74a7dSXin LI public void *ml_examine = (void *) &mlist_examine;
88a5f0fb15SPaul Saab 
89a5f0fb15SPaul Saab #if SHELL_ESCAPE || PIPEC
90a5f0fb15SPaul Saab struct mlist mlist_shell =
917f074f9cSXin LI 	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
92f6b74a7dSXin LI public void *ml_shell = (void *) &mlist_shell;
93a5f0fb15SPaul Saab #endif
94a5f0fb15SPaul Saab 
95a5f0fb15SPaul Saab #else /* CMD_HISTORY */
96a5f0fb15SPaul Saab 
97a5f0fb15SPaul Saab /* If CMD_HISTORY is off, these are just flags. */
98f6b74a7dSXin LI public void *ml_search = (void *)1;
99f6b74a7dSXin LI public void *ml_examine = (void *)2;
100a5f0fb15SPaul Saab #if SHELL_ESCAPE || PIPEC
101f6b74a7dSXin LI public void *ml_shell = (void *)3;
102a5f0fb15SPaul Saab #endif
103a5f0fb15SPaul Saab 
104a5f0fb15SPaul Saab #endif /* CMD_HISTORY */
105a5f0fb15SPaul Saab 
106a5f0fb15SPaul Saab /*
107a5f0fb15SPaul Saab  * History for the current command.
108a5f0fb15SPaul Saab  */
109a5f0fb15SPaul Saab static struct mlist *curr_mlist = NULL;
110a5f0fb15SPaul Saab static int curr_cmdflags;
111a5f0fb15SPaul Saab 
1126dcb072bSXin LI static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
1136dcb072bSXin LI static int cmd_mbc_buf_len;
1146dcb072bSXin LI static int cmd_mbc_buf_index;
1156dcb072bSXin LI 
116a5f0fb15SPaul Saab 
117a5f0fb15SPaul Saab /*
118a5f0fb15SPaul Saab  * Reset command buffer (to empty).
119a5f0fb15SPaul Saab  */
120d713e089SXin LI public void cmd_reset(void)
121a5f0fb15SPaul Saab {
122a5f0fb15SPaul Saab 	cp = cmdbuf;
123a5f0fb15SPaul Saab 	*cp = '\0';
124a5f0fb15SPaul Saab 	cmd_col = 0;
125a5f0fb15SPaul Saab 	cmd_offset = 0;
126*c77c4889SXin LI 	literal = FALSE;
1276dcb072bSXin LI 	cmd_mbc_buf_len = 0;
128*c77c4889SXin LI 	have_updown_match = FALSE;
129a5f0fb15SPaul Saab }
130a5f0fb15SPaul Saab 
131a5f0fb15SPaul Saab /*
1327f074f9cSXin LI  * Clear command line.
133a5f0fb15SPaul Saab  */
134d713e089SXin LI public void clear_cmd(void)
135a5f0fb15SPaul Saab {
136a5f0fb15SPaul Saab 	cmd_col = prompt_col = 0;
1376dcb072bSXin LI 	cmd_mbc_buf_len = 0;
138*c77c4889SXin LI 	have_updown_match = FALSE;
139a5f0fb15SPaul Saab }
140a5f0fb15SPaul Saab 
141a5f0fb15SPaul Saab /*
142a5f0fb15SPaul Saab  * Display a string, usually as a prompt for input into the command buffer.
143a5f0fb15SPaul Saab  */
144d713e089SXin LI public void cmd_putstr(constant char *s)
145a5f0fb15SPaul Saab {
1466dcb072bSXin LI 	LWCHAR prev_ch = 0;
1476dcb072bSXin LI 	LWCHAR ch;
1481ea31627SRobert Watson 	constant char *endline = s + strlen(s);
1496dcb072bSXin LI 	while (*s != '\0')
1506dcb072bSXin LI 	{
151*c77c4889SXin LI 		constant char *os = s;
152f6b74a7dSXin LI 		int width;
153*c77c4889SXin LI 		ch = step_charc(&s, +1, endline);
154*c77c4889SXin LI 		while (os < s)
155*c77c4889SXin LI 			putchr(*os++);
1566dcb072bSXin LI 		if (!utf_mode)
157f6b74a7dSXin LI 			width = 1;
158f6b74a7dSXin LI 		else if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
159f6b74a7dSXin LI 			width = 0;
160f6b74a7dSXin LI 		else
161f6b74a7dSXin LI 			width = is_wide_char(ch) ? 2 : 1;
1626dcb072bSXin LI 		cmd_col += width;
1636dcb072bSXin LI 		prompt_col += width;
1646dcb072bSXin LI 		prev_ch = ch;
1656dcb072bSXin LI 	}
166a5f0fb15SPaul Saab }
167a5f0fb15SPaul Saab 
168a5f0fb15SPaul Saab /*
169a5f0fb15SPaul Saab  * How many characters are in the command buffer?
170a5f0fb15SPaul Saab  */
171d713e089SXin LI public int len_cmdbuf(void)
172a5f0fb15SPaul Saab {
173*c77c4889SXin LI 	constant char *s = cmdbuf;
174*c77c4889SXin LI 	constant char *endline = s + strlen(s);
1756dcb072bSXin LI 	int len = 0;
1766dcb072bSXin LI 
1776dcb072bSXin LI 	while (*s != '\0')
1786dcb072bSXin LI 	{
179*c77c4889SXin LI 		step_charc(&s, +1, endline);
1806dcb072bSXin LI 		len++;
1816dcb072bSXin LI 	}
1826dcb072bSXin LI 	return (len);
1836dcb072bSXin LI }
1846dcb072bSXin LI 
1856dcb072bSXin LI /*
1866dcb072bSXin LI  * Common part of cmd_step_right() and cmd_step_left().
187f6b74a7dSXin LI  * {{ Returning pwidth and bswidth separately is a historical artifact
188f6b74a7dSXin LI  *    since they're always the same. Maybe clean this up someday. }}
1896dcb072bSXin LI  */
190*c77c4889SXin LI static constant char * cmd_step_common(char *p, LWCHAR ch, size_t len, int *pwidth, int *bswidth)
1916dcb072bSXin LI {
192*c77c4889SXin LI 	constant char *pr;
193f6b74a7dSXin LI 	int width;
1946dcb072bSXin LI 
1956dcb072bSXin LI 	if (len == 1)
1966dcb072bSXin LI 	{
197*c77c4889SXin LI 		pr = prchar(ch);
198f6b74a7dSXin LI 		width = (int) strlen(pr);
1996dcb072bSXin LI 	} else
2006dcb072bSXin LI 	{
2016dcb072bSXin LI 		pr = prutfchar(ch);
2026dcb072bSXin LI 		if (is_composing_char(ch))
203f6b74a7dSXin LI 			width = 0;
204f6b74a7dSXin LI 		else if (is_ubin_char(ch))
205f6b74a7dSXin LI 			width = (int) strlen(pr);
206f6b74a7dSXin LI 		else
2076dcb072bSXin LI 		{
2086dcb072bSXin LI 			LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
2096dcb072bSXin LI 			if (is_combining_char(prev_ch, ch))
210f6b74a7dSXin LI 				width = 0;
211f6b74a7dSXin LI 			else
212f6b74a7dSXin LI 				width = is_wide_char(ch) ? 2 : 1;
213f6b74a7dSXin LI 		}
214f6b74a7dSXin LI 	}
2156dcb072bSXin LI 	if (pwidth != NULL)
216f6b74a7dSXin LI 		*pwidth = width;
2176dcb072bSXin LI 	if (bswidth != NULL)
218f6b74a7dSXin LI 		*bswidth = width;
2196dcb072bSXin LI 	return (pr);
2206dcb072bSXin LI }
2216dcb072bSXin LI 
2226dcb072bSXin LI /*
2236dcb072bSXin LI  * Step a pointer one character right in the command buffer.
2246dcb072bSXin LI  */
225*c77c4889SXin LI static constant char * cmd_step_right(char **pp, int *pwidth, int *bswidth)
2266dcb072bSXin LI {
2276dcb072bSXin LI 	char *p = *pp;
228f6b74a7dSXin LI 	LWCHAR ch = step_char(pp, +1, p + strlen(p));
2296dcb072bSXin LI 
230*c77c4889SXin LI 	return cmd_step_common(p, ch, ptr_diff(*pp, p), pwidth, bswidth);
2316dcb072bSXin LI }
2326dcb072bSXin LI 
2336dcb072bSXin LI /*
2346dcb072bSXin LI  * Step a pointer one character left in the command buffer.
2356dcb072bSXin LI  */
236*c77c4889SXin LI static constant char * cmd_step_left(char **pp, int *pwidth, int *bswidth)
2376dcb072bSXin LI {
2386dcb072bSXin LI 	char *p = *pp;
239f6b74a7dSXin LI 	LWCHAR ch = step_char(pp, -1, cmdbuf);
2406dcb072bSXin LI 
241*c77c4889SXin LI 	return cmd_step_common(*pp, ch, ptr_diff(p, *pp), pwidth, bswidth);
242a5f0fb15SPaul Saab }
243a5f0fb15SPaul Saab 
244a5f0fb15SPaul Saab /*
2452235c7feSXin LI  * Put the cursor at "home" (just after the prompt),
2462235c7feSXin LI  * and set cp to the corresponding char in cmdbuf.
2472235c7feSXin LI  */
248d713e089SXin LI static void cmd_home(void)
2492235c7feSXin LI {
2502235c7feSXin LI 	while (cmd_col > prompt_col)
2512235c7feSXin LI 	{
2522235c7feSXin LI 		int width, bswidth;
2532235c7feSXin LI 
2542235c7feSXin LI 		cmd_step_left(&cp, &width, &bswidth);
2552235c7feSXin LI 		while (bswidth-- > 0)
2562235c7feSXin LI 			putbs();
2572235c7feSXin LI 		cmd_col -= width;
2582235c7feSXin LI 	}
2592235c7feSXin LI 
2602235c7feSXin LI 	cp = &cmdbuf[cmd_offset];
2612235c7feSXin LI }
2622235c7feSXin LI 
2632235c7feSXin LI /*
264a5f0fb15SPaul Saab  * Repaint the line from cp onwards.
265a5f0fb15SPaul Saab  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
266a5f0fb15SPaul Saab  */
267d713e089SXin LI public void cmd_repaint(constant char *old_cp)
268a5f0fb15SPaul Saab {
269a5f0fb15SPaul Saab 	/*
270a5f0fb15SPaul Saab 	 * Repaint the line from the current position.
271a5f0fb15SPaul Saab 	 */
2722235c7feSXin LI 	if (old_cp == NULL)
2732235c7feSXin LI 	{
2742235c7feSXin LI 		old_cp = cp;
2752235c7feSXin LI 		cmd_home();
2762235c7feSXin LI 	}
277a5f0fb15SPaul Saab 	clear_eol();
2786dcb072bSXin LI 	while (*cp != '\0')
279a5f0fb15SPaul Saab 	{
2806dcb072bSXin LI 		char *np = cp;
2816dcb072bSXin LI 		int width;
282*c77c4889SXin LI 		constant char *pr = cmd_step_right(&np, &width, NULL);
2836dcb072bSXin LI 		if (cmd_col + width >= sc_width)
284a5f0fb15SPaul Saab 			break;
2856dcb072bSXin LI 		cp = np;
2866dcb072bSXin LI 		putstr(pr);
2876dcb072bSXin LI 		cmd_col += width;
2886dcb072bSXin LI 	}
2896dcb072bSXin LI 	while (*cp != '\0')
2906dcb072bSXin LI 	{
2916dcb072bSXin LI 		char *np = cp;
2926dcb072bSXin LI 		int width;
293*c77c4889SXin LI 		constant char *pr = cmd_step_right(&np, &width, NULL);
2946dcb072bSXin LI 		if (width > 0)
2956dcb072bSXin LI 			break;
2966dcb072bSXin LI 		cp = np;
2976dcb072bSXin LI 		putstr(pr);
298a5f0fb15SPaul Saab 	}
299a5f0fb15SPaul Saab 
300a5f0fb15SPaul Saab 	/*
301a5f0fb15SPaul Saab 	 * Back up the cursor to the correct position.
302a5f0fb15SPaul Saab 	 */
303a5f0fb15SPaul Saab 	while (cp > old_cp)
304a5f0fb15SPaul Saab 		cmd_left();
305a5f0fb15SPaul Saab }
306a5f0fb15SPaul Saab 
307a5f0fb15SPaul Saab /*
308a5f0fb15SPaul Saab  * Shift the cmdbuf display left a half-screen.
309a5f0fb15SPaul Saab  */
310d713e089SXin LI static void cmd_lshift(void)
311a5f0fb15SPaul Saab {
312a5f0fb15SPaul Saab 	char *s;
313a5f0fb15SPaul Saab 	char *save_cp;
314a5f0fb15SPaul Saab 	int cols;
315a5f0fb15SPaul Saab 
316a5f0fb15SPaul Saab 	/*
317a5f0fb15SPaul Saab 	 * Start at the first displayed char, count how far to the
318a5f0fb15SPaul Saab 	 * right we'd have to move to reach the center of the screen.
319a5f0fb15SPaul Saab 	 */
320a5f0fb15SPaul Saab 	s = cmdbuf + cmd_offset;
321a5f0fb15SPaul Saab 	cols = 0;
322a5f0fb15SPaul Saab 	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
3236dcb072bSXin LI 	{
3246dcb072bSXin LI 		int width;
3256dcb072bSXin LI 		cmd_step_right(&s, &width, NULL);
3266dcb072bSXin LI 		cols += width;
3276dcb072bSXin LI 	}
3286dcb072bSXin LI 	while (*s != '\0')
3296dcb072bSXin LI 	{
3306dcb072bSXin LI 		int width;
3316dcb072bSXin LI 		char *ns = s;
3326dcb072bSXin LI 		cmd_step_right(&ns, &width, NULL);
3336dcb072bSXin LI 		if (width > 0)
3346dcb072bSXin LI 			break;
3356dcb072bSXin LI 		s = ns;
3366dcb072bSXin LI 	}
337a5f0fb15SPaul Saab 
338a15691bfSXin LI 	cmd_offset = (int) (s - cmdbuf);
339a5f0fb15SPaul Saab 	save_cp = cp;
340a5f0fb15SPaul Saab 	cmd_home();
341a5f0fb15SPaul Saab 	cmd_repaint(save_cp);
342a5f0fb15SPaul Saab }
343a5f0fb15SPaul Saab 
344a5f0fb15SPaul Saab /*
345a5f0fb15SPaul Saab  * Shift the cmdbuf display right a half-screen.
346a5f0fb15SPaul Saab  */
347d713e089SXin LI static void cmd_rshift(void)
348a5f0fb15SPaul Saab {
349a5f0fb15SPaul Saab 	char *s;
350a5f0fb15SPaul Saab 	char *save_cp;
351a5f0fb15SPaul Saab 	int cols;
352a5f0fb15SPaul Saab 
353a5f0fb15SPaul Saab 	/*
354a5f0fb15SPaul Saab 	 * Start at the first displayed char, count how far to the
355a5f0fb15SPaul Saab 	 * left we'd have to move to traverse a half-screen width
356a5f0fb15SPaul Saab 	 * of displayed characters.
357a5f0fb15SPaul Saab 	 */
358a5f0fb15SPaul Saab 	s = cmdbuf + cmd_offset;
359a5f0fb15SPaul Saab 	cols = 0;
360a5f0fb15SPaul Saab 	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
361a5f0fb15SPaul Saab 	{
3626dcb072bSXin LI 		int width;
3636dcb072bSXin LI 		cmd_step_left(&s, &width, NULL);
3646dcb072bSXin LI 		cols += width;
365a5f0fb15SPaul Saab 	}
366a5f0fb15SPaul Saab 
367a15691bfSXin LI 	cmd_offset = (int) (s - cmdbuf);
368a5f0fb15SPaul Saab 	save_cp = cp;
369a5f0fb15SPaul Saab 	cmd_home();
370a5f0fb15SPaul Saab 	cmd_repaint(save_cp);
371a5f0fb15SPaul Saab }
372a5f0fb15SPaul Saab 
373a5f0fb15SPaul Saab /*
374a5f0fb15SPaul Saab  * Move cursor right one character.
375a5f0fb15SPaul Saab  */
376d713e089SXin LI static int cmd_right(void)
377a5f0fb15SPaul Saab {
378*c77c4889SXin LI 	constant char *pr;
3796dcb072bSXin LI 	char *ncp;
3806dcb072bSXin LI 	int width;
381a5f0fb15SPaul Saab 
382a5f0fb15SPaul Saab 	if (*cp == '\0')
383a5f0fb15SPaul Saab 	{
3846dcb072bSXin LI 		/* Already at the end of the line. */
385a5f0fb15SPaul Saab 		return (CC_OK);
386a5f0fb15SPaul Saab 	}
3876dcb072bSXin LI 	ncp = cp;
3886dcb072bSXin LI 	pr = cmd_step_right(&ncp, &width, NULL);
3896dcb072bSXin LI 	if (cmd_col + width >= sc_width)
390a5f0fb15SPaul Saab 		cmd_lshift();
3916dcb072bSXin LI 	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
392a5f0fb15SPaul Saab 		cmd_lshift();
3936dcb072bSXin LI 	cp = ncp;
3946dcb072bSXin LI 	cmd_col += width;
3956dcb072bSXin LI 	putstr(pr);
3966dcb072bSXin LI 	while (*cp != '\0')
3976dcb072bSXin LI 	{
3986dcb072bSXin LI 		pr = cmd_step_right(&ncp, &width, NULL);
3996dcb072bSXin LI 		if (width > 0)
4006dcb072bSXin LI 			break;
4016dcb072bSXin LI 		putstr(pr);
4026dcb072bSXin LI 		cp = ncp;
4036dcb072bSXin LI 	}
404a5f0fb15SPaul Saab 	return (CC_OK);
405a5f0fb15SPaul Saab }
406a5f0fb15SPaul Saab 
407a5f0fb15SPaul Saab /*
408a5f0fb15SPaul Saab  * Move cursor left one character.
409a5f0fb15SPaul Saab  */
410d713e089SXin LI static int cmd_left(void)
411a5f0fb15SPaul Saab {
4126dcb072bSXin LI 	char *ncp;
413b2ea2440SXin LI 	int width = 0;
414b2ea2440SXin LI 	int bswidth = 0;
415a5f0fb15SPaul Saab 
416a5f0fb15SPaul Saab 	if (cp <= cmdbuf)
417a5f0fb15SPaul Saab 	{
418a5f0fb15SPaul Saab 		/* Already at the beginning of the line */
419a5f0fb15SPaul Saab 		return (CC_OK);
420a5f0fb15SPaul Saab 	}
4216dcb072bSXin LI 	ncp = cp;
4226dcb072bSXin LI 	while (ncp > cmdbuf)
4236dcb072bSXin LI 	{
4246dcb072bSXin LI 		cmd_step_left(&ncp, &width, &bswidth);
4256dcb072bSXin LI 		if (width > 0)
4266dcb072bSXin LI 			break;
4276dcb072bSXin LI 	}
4286dcb072bSXin LI 	if (cmd_col < prompt_col + width)
429a5f0fb15SPaul Saab 		cmd_rshift();
4306dcb072bSXin LI 	cp = ncp;
4316dcb072bSXin LI 	cmd_col -= width;
4326dcb072bSXin LI 	while (bswidth-- > 0)
433a5f0fb15SPaul Saab 		putbs();
434a5f0fb15SPaul Saab 	return (CC_OK);
435a5f0fb15SPaul Saab }
436a5f0fb15SPaul Saab 
437a5f0fb15SPaul Saab /*
438a5f0fb15SPaul Saab  * Insert a char into the command buffer, at the current position.
439a5f0fb15SPaul Saab  */
440*c77c4889SXin LI static int cmd_ichar(constant char *cs, size_t clen)
441a5f0fb15SPaul Saab {
442a5f0fb15SPaul Saab 	char *s;
443a5f0fb15SPaul Saab 
4446dcb072bSXin LI 	if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
445a5f0fb15SPaul Saab 	{
4466dcb072bSXin LI 		/* No room in the command buffer for another char. */
447a5f0fb15SPaul Saab 		bell();
448a5f0fb15SPaul Saab 		return (CC_ERROR);
449a5f0fb15SPaul Saab 	}
450a5f0fb15SPaul Saab 
451a5f0fb15SPaul Saab 	/*
4526dcb072bSXin LI 	 * Make room for the new character (shift the tail of the buffer right).
453a5f0fb15SPaul Saab 	 */
454a5f0fb15SPaul Saab 	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
4556dcb072bSXin LI 		s[clen] = s[0];
4566dcb072bSXin LI 	/*
4576dcb072bSXin LI 	 * Insert the character into the buffer.
4586dcb072bSXin LI 	 */
4596dcb072bSXin LI 	for (s = cp;  s < cp + clen;  s++)
4606dcb072bSXin LI 		*s = *cs++;
461a5f0fb15SPaul Saab 	/*
462a5f0fb15SPaul Saab 	 * Reprint the tail of the line from the inserted char.
463a5f0fb15SPaul Saab 	 */
464*c77c4889SXin LI 	have_updown_match = FALSE;
465a5f0fb15SPaul Saab 	cmd_repaint(cp);
466a5f0fb15SPaul Saab 	cmd_right();
467a5f0fb15SPaul Saab 	return (CC_OK);
468a5f0fb15SPaul Saab }
469a5f0fb15SPaul Saab 
470a5f0fb15SPaul Saab /*
471a5f0fb15SPaul Saab  * Backspace in the command buffer.
472a5f0fb15SPaul Saab  * Delete the char to the left of the cursor.
473a5f0fb15SPaul Saab  */
474d713e089SXin LI static int cmd_erase(void)
475a5f0fb15SPaul Saab {
4761ea31627SRobert Watson 	char *s;
4776dcb072bSXin LI 	int clen;
478a5f0fb15SPaul Saab 
479a5f0fb15SPaul Saab 	if (cp == cmdbuf)
480a5f0fb15SPaul Saab 	{
481a5f0fb15SPaul Saab 		/*
482a5f0fb15SPaul Saab 		 * Backspace past beginning of the buffer:
483a5f0fb15SPaul Saab 		 * this usually means abort the command.
484a5f0fb15SPaul Saab 		 */
485a5f0fb15SPaul Saab 		return (CC_QUIT);
486a5f0fb15SPaul Saab 	}
487a5f0fb15SPaul Saab 	/*
488a5f0fb15SPaul Saab 	 * Move cursor left (to the char being erased).
489a5f0fb15SPaul Saab 	 */
4906dcb072bSXin LI 	s = cp;
491a5f0fb15SPaul Saab 	cmd_left();
492a15691bfSXin LI 	clen = (int) (s - cp);
4936dcb072bSXin LI 
494a5f0fb15SPaul Saab 	/*
495a5f0fb15SPaul Saab 	 * Remove the char from the buffer (shift the buffer left).
496a5f0fb15SPaul Saab 	 */
4976dcb072bSXin LI 	for (s = cp;  ;  s++)
4986dcb072bSXin LI 	{
4996dcb072bSXin LI 		s[0] = s[clen];
5006dcb072bSXin LI 		if (s[0] == '\0')
5016dcb072bSXin LI 			break;
5026dcb072bSXin LI 	}
5036dcb072bSXin LI 
504a5f0fb15SPaul Saab 	/*
505a5f0fb15SPaul Saab 	 * Repaint the buffer after the erased char.
506a5f0fb15SPaul Saab 	 */
507*c77c4889SXin LI 	have_updown_match = FALSE;
508a5f0fb15SPaul Saab 	cmd_repaint(cp);
509a5f0fb15SPaul Saab 
510a5f0fb15SPaul Saab 	/*
511a5f0fb15SPaul Saab 	 * We say that erasing the entire command string causes us
512a5f0fb15SPaul Saab 	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
513a5f0fb15SPaul Saab 	 */
514a5f0fb15SPaul Saab 	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
515a5f0fb15SPaul Saab 		return (CC_QUIT);
516a5f0fb15SPaul Saab 	return (CC_OK);
517a5f0fb15SPaul Saab }
518a5f0fb15SPaul Saab 
519a5f0fb15SPaul Saab /*
520a5f0fb15SPaul Saab  * Delete the char under the cursor.
521a5f0fb15SPaul Saab  */
522d713e089SXin LI static int cmd_delete(void)
523a5f0fb15SPaul Saab {
524a5f0fb15SPaul Saab 	if (*cp == '\0')
525a5f0fb15SPaul Saab 	{
5266dcb072bSXin LI 		/* At end of string; there is no char under the cursor. */
527a5f0fb15SPaul Saab 		return (CC_OK);
528a5f0fb15SPaul Saab 	}
529a5f0fb15SPaul Saab 	/*
530a5f0fb15SPaul Saab 	 * Move right, then use cmd_erase.
531a5f0fb15SPaul Saab 	 */
532a5f0fb15SPaul Saab 	cmd_right();
533a5f0fb15SPaul Saab 	cmd_erase();
534a5f0fb15SPaul Saab 	return (CC_OK);
535a5f0fb15SPaul Saab }
536a5f0fb15SPaul Saab 
537a5f0fb15SPaul Saab /*
538a5f0fb15SPaul Saab  * Delete the "word" to the left of the cursor.
539a5f0fb15SPaul Saab  */
540d713e089SXin LI static int cmd_werase(void)
541a5f0fb15SPaul Saab {
542a5f0fb15SPaul Saab 	if (cp > cmdbuf && cp[-1] == ' ')
543a5f0fb15SPaul Saab 	{
544a5f0fb15SPaul Saab 		/*
545a5f0fb15SPaul Saab 		 * If the char left of cursor is a space,
546a5f0fb15SPaul Saab 		 * erase all the spaces left of cursor (to the first non-space).
547a5f0fb15SPaul Saab 		 */
548a5f0fb15SPaul Saab 		while (cp > cmdbuf && cp[-1] == ' ')
549a5f0fb15SPaul Saab 			(void) cmd_erase();
550a5f0fb15SPaul Saab 	} else
551a5f0fb15SPaul Saab 	{
552a5f0fb15SPaul Saab 		/*
553a5f0fb15SPaul Saab 		 * If the char left of cursor is not a space,
554a5f0fb15SPaul Saab 		 * erase all the nonspaces left of cursor (the whole "word").
555a5f0fb15SPaul Saab 		 */
556a5f0fb15SPaul Saab 		while (cp > cmdbuf && cp[-1] != ' ')
557a5f0fb15SPaul Saab 			(void) cmd_erase();
558a5f0fb15SPaul Saab 	}
559a5f0fb15SPaul Saab 	return (CC_OK);
560a5f0fb15SPaul Saab }
561a5f0fb15SPaul Saab 
562a5f0fb15SPaul Saab /*
563a5f0fb15SPaul Saab  * Delete the "word" under the cursor.
564a5f0fb15SPaul Saab  */
565d713e089SXin LI static int cmd_wdelete(void)
566a5f0fb15SPaul Saab {
567a5f0fb15SPaul Saab 	if (*cp == ' ')
568a5f0fb15SPaul Saab 	{
569a5f0fb15SPaul Saab 		/*
570a5f0fb15SPaul Saab 		 * If the char under the cursor is a space,
571a5f0fb15SPaul Saab 		 * delete it and all the spaces right of cursor.
572a5f0fb15SPaul Saab 		 */
573a5f0fb15SPaul Saab 		while (*cp == ' ')
574a5f0fb15SPaul Saab 			(void) cmd_delete();
575a5f0fb15SPaul Saab 	} else
576a5f0fb15SPaul Saab 	{
577a5f0fb15SPaul Saab 		/*
578a5f0fb15SPaul Saab 		 * If the char under the cursor is not a space,
579a5f0fb15SPaul Saab 		 * delete it and all nonspaces right of cursor (the whole word).
580a5f0fb15SPaul Saab 		 */
581a5f0fb15SPaul Saab 		while (*cp != ' ' && *cp != '\0')
582a5f0fb15SPaul Saab 			(void) cmd_delete();
583a5f0fb15SPaul Saab 	}
584a5f0fb15SPaul Saab 	return (CC_OK);
585a5f0fb15SPaul Saab }
586a5f0fb15SPaul Saab 
587a5f0fb15SPaul Saab /*
588a5f0fb15SPaul Saab  * Delete all chars in the command buffer.
589a5f0fb15SPaul Saab  */
590d713e089SXin LI static int cmd_kill(void)
591a5f0fb15SPaul Saab {
592a5f0fb15SPaul Saab 	if (cmdbuf[0] == '\0')
593a5f0fb15SPaul Saab 	{
5946dcb072bSXin LI 		/* Buffer is already empty; abort the current command. */
595a5f0fb15SPaul Saab 		return (CC_QUIT);
596a5f0fb15SPaul Saab 	}
597a5f0fb15SPaul Saab 	cmd_offset = 0;
598a5f0fb15SPaul Saab 	cmd_home();
599a5f0fb15SPaul Saab 	*cp = '\0';
600*c77c4889SXin LI 	have_updown_match = FALSE;
601a5f0fb15SPaul Saab 	cmd_repaint(cp);
602a5f0fb15SPaul Saab 
603a5f0fb15SPaul Saab 	/*
604a5f0fb15SPaul Saab 	 * We say that erasing the entire command string causes us
605a5f0fb15SPaul Saab 	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
606a5f0fb15SPaul Saab 	 */
607a5f0fb15SPaul Saab 	if (curr_cmdflags & CF_QUIT_ON_ERASE)
608a5f0fb15SPaul Saab 		return (CC_QUIT);
609a5f0fb15SPaul Saab 	return (CC_OK);
610a5f0fb15SPaul Saab }
611a5f0fb15SPaul Saab 
612a5f0fb15SPaul Saab /*
613a5f0fb15SPaul Saab  * Select an mlist structure to be the current command history.
614a5f0fb15SPaul Saab  */
615d713e089SXin LI public void set_mlist(void *mlist, int cmdflags)
616a5f0fb15SPaul Saab {
6177374caaaSXin LI #if CMD_HISTORY
618a5f0fb15SPaul Saab 	curr_mlist = (struct mlist *) mlist;
619a5f0fb15SPaul Saab 	curr_cmdflags = cmdflags;
6206dcb072bSXin LI 
6216dcb072bSXin LI 	/* Make sure the next up-arrow moves to the last string in the mlist. */
6226dcb072bSXin LI 	if (curr_mlist != NULL)
6236dcb072bSXin LI 		curr_mlist->curr_mp = curr_mlist;
6247374caaaSXin LI #endif
625a5f0fb15SPaul Saab }
626a5f0fb15SPaul Saab 
627a5f0fb15SPaul Saab #if CMD_HISTORY
628a5f0fb15SPaul Saab /*
629a5f0fb15SPaul Saab  * Move up or down in the currently selected command history list.
63096e55cc7SXin LI  * Only consider entries whose first updown_match chars are equal to
63196e55cc7SXin LI  * cmdbuf's corresponding chars.
632a5f0fb15SPaul Saab  */
633d713e089SXin LI static int cmd_updown(int action)
634a5f0fb15SPaul Saab {
635f6b74a7dSXin LI 	constant char *s;
63696e55cc7SXin LI 	struct mlist *ml;
637a5f0fb15SPaul Saab 
638a5f0fb15SPaul Saab 	if (curr_mlist == NULL)
639a5f0fb15SPaul Saab 	{
640a5f0fb15SPaul Saab 		/*
641a5f0fb15SPaul Saab 		 * The current command has no history list.
642a5f0fb15SPaul Saab 		 */
643a5f0fb15SPaul Saab 		bell();
644a5f0fb15SPaul Saab 		return (CC_OK);
645a5f0fb15SPaul Saab 	}
64696e55cc7SXin LI 
647*c77c4889SXin LI 	if (!have_updown_match)
64896e55cc7SXin LI 	{
649*c77c4889SXin LI 		updown_match = ptr_diff(cp, cmdbuf);
650*c77c4889SXin LI 		have_updown_match = TRUE;
65196e55cc7SXin LI 	}
65296e55cc7SXin LI 
653a5f0fb15SPaul Saab 	/*
65496e55cc7SXin LI 	 * Find the next history entry which matches.
655a5f0fb15SPaul Saab 	 */
65696e55cc7SXin LI 	for (ml = curr_mlist->curr_mp;;)
65796e55cc7SXin LI 	{
65896e55cc7SXin LI 		ml = (action == EC_UP) ? ml->prev : ml->next;
65996e55cc7SXin LI 		if (ml == curr_mlist)
66096e55cc7SXin LI 		{
661a5f0fb15SPaul Saab 			/*
66296e55cc7SXin LI 			 * We reached the end (or beginning) of the list.
66396e55cc7SXin LI 			 */
66496e55cc7SXin LI 			break;
66596e55cc7SXin LI 		}
66696e55cc7SXin LI 		if (strncmp(cmdbuf, ml->string, updown_match) == 0)
66796e55cc7SXin LI 		{
66896e55cc7SXin LI 			/*
66996e55cc7SXin LI 			 * This entry matches; stop here.
670a5f0fb15SPaul Saab 			 * Copy the entry into cmdbuf and echo it on the screen.
671a5f0fb15SPaul Saab 			 */
67296e55cc7SXin LI 			curr_mlist->curr_mp = ml;
67396e55cc7SXin LI 			s = ml->string;
674a5f0fb15SPaul Saab 			if (s == NULL)
675a5f0fb15SPaul Saab 				s = "";
676b7780dbeSXin LI 			cmd_offset = 0;
67796e55cc7SXin LI 			cmd_home();
67896e55cc7SXin LI 			clear_eol();
6797bd2567cSXin LI 			strcpy(cmdbuf, s);
6806dcb072bSXin LI 			for (cp = cmdbuf;  *cp != '\0';  )
681a5f0fb15SPaul Saab 				cmd_right();
682a5f0fb15SPaul Saab 			return (CC_OK);
683a5f0fb15SPaul Saab 		}
68496e55cc7SXin LI 	}
68596e55cc7SXin LI 	/*
68696e55cc7SXin LI 	 * We didn't find a history entry that matches.
68796e55cc7SXin LI 	 */
68896e55cc7SXin LI 	bell();
68996e55cc7SXin LI 	return (CC_OK);
69096e55cc7SXin LI }
691*c77c4889SXin LI 
692*c77c4889SXin LI /*
693*c77c4889SXin LI  * Yet another lesson in the evils of global variables.
694*c77c4889SXin LI  */
695*c77c4889SXin LI public ssize_t save_updown_match(void)
696*c77c4889SXin LI {
697*c77c4889SXin LI 	if (!have_updown_match)
698*c77c4889SXin LI 		return (ssize_t)(-1);
699*c77c4889SXin LI 	return (ssize_t) updown_match;
700*c77c4889SXin LI }
701*c77c4889SXin LI 
702*c77c4889SXin LI public void restore_updown_match(ssize_t udm)
703*c77c4889SXin LI {
704*c77c4889SXin LI 	updown_match = udm;
705*c77c4889SXin LI 	have_updown_match = (udm != (ssize_t)(-1));
706*c77c4889SXin LI }
707*c77c4889SXin LI #endif /* CMD_HISTORY */
708a5f0fb15SPaul Saab 
709a5f0fb15SPaul Saab /*
710b7780dbeSXin LI  *
711b7780dbeSXin LI  */
712d713e089SXin LI static void ml_link(struct mlist *mlist, struct mlist *ml)
713b7780dbeSXin LI {
714b7780dbeSXin LI 	ml->next = mlist;
715b7780dbeSXin LI 	ml->prev = mlist->prev;
716b7780dbeSXin LI 	mlist->prev->next = ml;
717b7780dbeSXin LI 	mlist->prev = ml;
718b7780dbeSXin LI }
719b7780dbeSXin LI 
720b7780dbeSXin LI /*
721b7780dbeSXin LI  *
722b7780dbeSXin LI  */
723d713e089SXin LI static void ml_unlink(struct mlist *ml)
724b7780dbeSXin LI {
725b7780dbeSXin LI 	ml->prev->next = ml->next;
726b7780dbeSXin LI 	ml->next->prev = ml->prev;
727b7780dbeSXin LI }
728b7780dbeSXin LI 
729b7780dbeSXin LI /*
730a15691bfSXin LI  * Add a string to an mlist.
731a5f0fb15SPaul Saab  */
732*c77c4889SXin LI public void cmd_addhist(struct mlist *mlist, constant char *cmd, lbool modified)
733a5f0fb15SPaul Saab {
734a5f0fb15SPaul Saab #if CMD_HISTORY
735a5f0fb15SPaul Saab 	struct mlist *ml;
736a5f0fb15SPaul Saab 
737a5f0fb15SPaul Saab 	/*
738a5f0fb15SPaul Saab 	 * Don't save a trivial command.
739a5f0fb15SPaul Saab 	 */
740a5f0fb15SPaul Saab 	if (strlen(cmd) == 0)
741a5f0fb15SPaul Saab 		return;
7426dcb072bSXin LI 
743b7780dbeSXin LI 	if (no_hist_dups)
744b7780dbeSXin LI 	{
745b7780dbeSXin LI 		struct mlist *next = NULL;
746b7780dbeSXin LI 		for (ml = mlist->next;  ml->string != NULL;  ml = next)
747b7780dbeSXin LI 		{
748b7780dbeSXin LI 			next = ml->next;
749b7780dbeSXin LI 			if (strcmp(ml->string, cmd) == 0)
750b7780dbeSXin LI 			{
751b7780dbeSXin LI 				ml_unlink(ml);
752b7780dbeSXin LI 				free(ml->string);
753b7780dbeSXin LI 				free(ml);
754b7780dbeSXin LI 			}
755b7780dbeSXin LI 		}
756b7780dbeSXin LI 	}
757b7780dbeSXin LI 
758a5f0fb15SPaul Saab 	/*
7596dcb072bSXin LI 	 * Save the command unless it's a duplicate of the
7606dcb072bSXin LI 	 * last command in the history.
761a5f0fb15SPaul Saab 	 */
7626dcb072bSXin LI 	ml = mlist->prev;
7636dcb072bSXin LI 	if (ml == mlist || strcmp(ml->string, cmd) != 0)
764a5f0fb15SPaul Saab 	{
765a5f0fb15SPaul Saab 		/*
766a5f0fb15SPaul Saab 		 * Did not find command in history.
767a5f0fb15SPaul Saab 		 * Save the command and put it at the end of the history list.
768a5f0fb15SPaul Saab 		 */
769a5f0fb15SPaul Saab 		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
770a5f0fb15SPaul Saab 		ml->string = save(cmd);
771a15691bfSXin LI 		ml->modified = modified;
772b7780dbeSXin LI 		ml_link(mlist, ml);
773a5f0fb15SPaul Saab 	}
774a5f0fb15SPaul Saab 	/*
775a5f0fb15SPaul Saab 	 * Point to the cmd just after the just-accepted command.
776a5f0fb15SPaul Saab 	 * Thus, an UPARROW will always retrieve the previous command.
777a5f0fb15SPaul Saab 	 */
778a5f0fb15SPaul Saab 	mlist->curr_mp = ml->next;
779a5f0fb15SPaul Saab #endif
780a5f0fb15SPaul Saab }
781a5f0fb15SPaul Saab 
782a5f0fb15SPaul Saab /*
783a5f0fb15SPaul Saab  * Accept the command in the command buffer.
784a5f0fb15SPaul Saab  * Add it to the currently selected history list.
785a5f0fb15SPaul Saab  */
786d713e089SXin LI public void cmd_accept(void)
787a5f0fb15SPaul Saab {
788a5f0fb15SPaul Saab #if CMD_HISTORY
789a5f0fb15SPaul Saab 	/*
790a5f0fb15SPaul Saab 	 * Nothing to do if there is no currently selected history list.
791a5f0fb15SPaul Saab 	 */
792b7780dbeSXin LI 	if (curr_mlist == NULL || curr_mlist == ml_examine)
793a5f0fb15SPaul Saab 		return;
794*c77c4889SXin LI 	cmd_addhist(curr_mlist, cmdbuf, TRUE);
795*c77c4889SXin LI 	curr_mlist->modified = TRUE;
796a5f0fb15SPaul Saab #endif
797a5f0fb15SPaul Saab }
798a5f0fb15SPaul Saab 
799a5f0fb15SPaul Saab /*
800a5f0fb15SPaul Saab  * Try to perform a line-edit function on the command buffer,
801a5f0fb15SPaul Saab  * using a specified char as a line-editing command.
802a5f0fb15SPaul Saab  * Returns:
803a5f0fb15SPaul Saab  *      CC_PASS The char does not invoke a line edit function.
804a5f0fb15SPaul Saab  *      CC_OK   Line edit function done.
805a5f0fb15SPaul Saab  *      CC_QUIT The char requests the current command to be aborted.
806a5f0fb15SPaul Saab  */
807*c77c4889SXin LI static int cmd_edit(char c)
808a5f0fb15SPaul Saab {
809a5f0fb15SPaul Saab 	int action;
810a5f0fb15SPaul Saab 	int flags;
811a5f0fb15SPaul Saab 
812a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME
813a5f0fb15SPaul Saab #define not_in_completion()     in_completion = 0
814a5f0fb15SPaul Saab #else
815d713e089SXin LI #define not_in_completion(void)
816a5f0fb15SPaul Saab #endif
817a5f0fb15SPaul Saab 
818a5f0fb15SPaul Saab 	/*
819a5f0fb15SPaul Saab 	 * See if the char is indeed a line-editing command.
820a5f0fb15SPaul Saab 	 */
821a5f0fb15SPaul Saab 	flags = 0;
822a5f0fb15SPaul Saab #if CMD_HISTORY
823a5f0fb15SPaul Saab 	if (curr_mlist == NULL)
824a5f0fb15SPaul Saab 		/*
825a5f0fb15SPaul Saab 		 * No current history; don't accept history manipulation cmds.
826a5f0fb15SPaul Saab 		 */
8272235c7feSXin LI 		flags |= ECF_NOHISTORY;
828a5f0fb15SPaul Saab #endif
829a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME
83095270f73SXin LI 	if (curr_mlist == ml_search || curr_mlist == NULL)
831a5f0fb15SPaul Saab 		/*
83295270f73SXin LI 		 * Don't accept file-completion cmds in contexts
83395270f73SXin LI 		 * such as search pattern, digits, long option name, etc.
834a5f0fb15SPaul Saab 		 */
8352235c7feSXin LI 		flags |= ECF_NOCOMPLETE;
836a5f0fb15SPaul Saab #endif
837a5f0fb15SPaul Saab 
838a5f0fb15SPaul Saab 	action = editchar(c, flags);
839a5f0fb15SPaul Saab 
840a5f0fb15SPaul Saab 	switch (action)
841a5f0fb15SPaul Saab 	{
8422235c7feSXin LI 	case A_NOACTION:
8432235c7feSXin LI 		return (CC_OK);
844a5f0fb15SPaul Saab 	case EC_RIGHT:
845a5f0fb15SPaul Saab 		not_in_completion();
846a5f0fb15SPaul Saab 		return (cmd_right());
847a5f0fb15SPaul Saab 	case EC_LEFT:
848a5f0fb15SPaul Saab 		not_in_completion();
849a5f0fb15SPaul Saab 		return (cmd_left());
850a5f0fb15SPaul Saab 	case EC_W_RIGHT:
851a5f0fb15SPaul Saab 		not_in_completion();
852a5f0fb15SPaul Saab 		while (*cp != '\0' && *cp != ' ')
853a5f0fb15SPaul Saab 			cmd_right();
854a5f0fb15SPaul Saab 		while (*cp == ' ')
855a5f0fb15SPaul Saab 			cmd_right();
856a5f0fb15SPaul Saab 		return (CC_OK);
857a5f0fb15SPaul Saab 	case EC_W_LEFT:
858a5f0fb15SPaul Saab 		not_in_completion();
859a5f0fb15SPaul Saab 		while (cp > cmdbuf && cp[-1] == ' ')
860a5f0fb15SPaul Saab 			cmd_left();
861a5f0fb15SPaul Saab 		while (cp > cmdbuf && cp[-1] != ' ')
862a5f0fb15SPaul Saab 			cmd_left();
863a5f0fb15SPaul Saab 		return (CC_OK);
864a5f0fb15SPaul Saab 	case EC_HOME:
865a5f0fb15SPaul Saab 		not_in_completion();
866a5f0fb15SPaul Saab 		cmd_offset = 0;
867a5f0fb15SPaul Saab 		cmd_home();
868a5f0fb15SPaul Saab 		cmd_repaint(cp);
869a5f0fb15SPaul Saab 		return (CC_OK);
870a5f0fb15SPaul Saab 	case EC_END:
871a5f0fb15SPaul Saab 		not_in_completion();
872a5f0fb15SPaul Saab 		while (*cp != '\0')
873a5f0fb15SPaul Saab 			cmd_right();
874a5f0fb15SPaul Saab 		return (CC_OK);
875a5f0fb15SPaul Saab 	case EC_INSERT:
876a5f0fb15SPaul Saab 		not_in_completion();
877a5f0fb15SPaul Saab 		return (CC_OK);
878a5f0fb15SPaul Saab 	case EC_BACKSPACE:
879a5f0fb15SPaul Saab 		not_in_completion();
880a5f0fb15SPaul Saab 		return (cmd_erase());
881a5f0fb15SPaul Saab 	case EC_LINEKILL:
882a5f0fb15SPaul Saab 		not_in_completion();
883a5f0fb15SPaul Saab 		return (cmd_kill());
88433096f16SXin LI 	case EC_ABORT:
88533096f16SXin LI 		not_in_completion();
88633096f16SXin LI 		(void) cmd_kill();
88733096f16SXin LI 		return (CC_QUIT);
888a5f0fb15SPaul Saab 	case EC_W_BACKSPACE:
889a5f0fb15SPaul Saab 		not_in_completion();
890a5f0fb15SPaul Saab 		return (cmd_werase());
891a5f0fb15SPaul Saab 	case EC_DELETE:
892a5f0fb15SPaul Saab 		not_in_completion();
893a5f0fb15SPaul Saab 		return (cmd_delete());
894a5f0fb15SPaul Saab 	case EC_W_DELETE:
895a5f0fb15SPaul Saab 		not_in_completion();
896a5f0fb15SPaul Saab 		return (cmd_wdelete());
897a5f0fb15SPaul Saab 	case EC_LITERAL:
898*c77c4889SXin LI 		literal = TRUE;
899a5f0fb15SPaul Saab 		return (CC_OK);
900a5f0fb15SPaul Saab #if CMD_HISTORY
901a5f0fb15SPaul Saab 	case EC_UP:
902a5f0fb15SPaul Saab 	case EC_DOWN:
903a5f0fb15SPaul Saab 		not_in_completion();
904a5f0fb15SPaul Saab 		return (cmd_updown(action));
905a5f0fb15SPaul Saab #endif
906a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME
907a5f0fb15SPaul Saab 	case EC_F_COMPLETE:
908a5f0fb15SPaul Saab 	case EC_B_COMPLETE:
909a5f0fb15SPaul Saab 	case EC_EXPAND:
910a5f0fb15SPaul Saab 		return (cmd_complete(action));
911a5f0fb15SPaul Saab #endif
912a5f0fb15SPaul Saab 	default:
913a5f0fb15SPaul Saab 		not_in_completion();
914a5f0fb15SPaul Saab 		return (CC_PASS);
915a5f0fb15SPaul Saab 	}
916a5f0fb15SPaul Saab }
917a5f0fb15SPaul Saab 
918a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME
919a5f0fb15SPaul Saab /*
920a5f0fb15SPaul Saab  * Insert a string into the command buffer, at the current position.
921a5f0fb15SPaul Saab  */
922*c77c4889SXin LI static int cmd_istr(constant char *str)
923a5f0fb15SPaul Saab {
924*c77c4889SXin LI 	constant char *endline = str + strlen(str);
925*c77c4889SXin LI 	constant char *s;
926a5f0fb15SPaul Saab 	int action;
927a5f0fb15SPaul Saab 
9286dcb072bSXin LI 	for (s = str;  *s != '\0';  )
929a5f0fb15SPaul Saab 	{
930*c77c4889SXin LI 		constant char *os = s;
931*c77c4889SXin LI 		step_charc(&s, +1, endline);
932*c77c4889SXin LI 		action = cmd_ichar(os, ptr_diff(s, os));
933a5f0fb15SPaul Saab 		if (action != CC_OK)
934a5f0fb15SPaul Saab 			return (action);
935a5f0fb15SPaul Saab 	}
936a5f0fb15SPaul Saab 	return (CC_OK);
937a5f0fb15SPaul Saab }
938a5f0fb15SPaul Saab 
939a5f0fb15SPaul Saab /*
940a5f0fb15SPaul Saab  * Find the beginning and end of the "current" word.
941a5f0fb15SPaul Saab  * This is the word which the cursor (cp) is inside or at the end of.
942a5f0fb15SPaul Saab  * Return pointer to the beginning of the word and put the
943a5f0fb15SPaul Saab  * cursor at the end of the word.
944a5f0fb15SPaul Saab  */
945d713e089SXin LI static char * delimit_word(void)
946a5f0fb15SPaul Saab {
947a5f0fb15SPaul Saab 	char *word;
948a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
949a5f0fb15SPaul Saab 	char *p;
950*c77c4889SXin LI 	int delim_quoted = FALSE;
951*c77c4889SXin LI 	int meta_quoted = FALSE;
952f6b74a7dSXin LI 	constant char *esc = get_meta_escape();
953*c77c4889SXin LI 	size_t esclen = strlen(esc);
954a5f0fb15SPaul Saab #endif
955a5f0fb15SPaul Saab 
956a5f0fb15SPaul Saab 	/*
957a5f0fb15SPaul Saab 	 * Move cursor to end of word.
958a5f0fb15SPaul Saab 	 */
959a5f0fb15SPaul Saab 	if (*cp != ' ' && *cp != '\0')
960a5f0fb15SPaul Saab 	{
961a5f0fb15SPaul Saab 		/*
962a5f0fb15SPaul Saab 		 * Cursor is on a nonspace.
963a5f0fb15SPaul Saab 		 * Move cursor right to the next space.
964a5f0fb15SPaul Saab 		 */
965a5f0fb15SPaul Saab 		while (*cp != ' ' && *cp != '\0')
966a5f0fb15SPaul Saab 			cmd_right();
967a5f0fb15SPaul Saab 	} else if (cp > cmdbuf && cp[-1] != ' ')
968a5f0fb15SPaul Saab 	{
969a5f0fb15SPaul Saab 		/*
970a5f0fb15SPaul Saab 		 * Cursor is on a space, and char to the left is a nonspace.
971a5f0fb15SPaul Saab 		 * We're already at the end of the word.
972a5f0fb15SPaul Saab 		 */
973a5f0fb15SPaul Saab 		;
974000ba3e8STim J. Robbins #if 0
975a5f0fb15SPaul Saab 	} else
976a5f0fb15SPaul Saab 	{
977a5f0fb15SPaul Saab 		/*
978a5f0fb15SPaul Saab 		 * Cursor is on a space and char to the left is a space.
979a5f0fb15SPaul Saab 		 * Huh? There's no word here.
980a5f0fb15SPaul Saab 		 */
981a5f0fb15SPaul Saab 		return (NULL);
982000ba3e8STim J. Robbins #endif
983a5f0fb15SPaul Saab 	}
984a5f0fb15SPaul Saab 	/*
985000ba3e8STim J. Robbins 	 * Find the beginning of the word which the cursor is in.
986a5f0fb15SPaul Saab 	 */
987a5f0fb15SPaul Saab 	if (cp == cmdbuf)
988a5f0fb15SPaul Saab 		return (NULL);
989a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
990a5f0fb15SPaul Saab 	/*
991a5f0fb15SPaul Saab 	 * If we have an unbalanced quote (that is, an open quote
992a5f0fb15SPaul Saab 	 * without a corresponding close quote), we return everything
993a5f0fb15SPaul Saab 	 * from the open quote, including spaces.
994a5f0fb15SPaul Saab 	 */
995000ba3e8STim J. Robbins 	for (word = cmdbuf;  word < cp;  word++)
996000ba3e8STim J. Robbins 		if (*word != ' ')
997000ba3e8STim J. Robbins 			break;
998000ba3e8STim J. Robbins 	if (word >= cp)
999000ba3e8STim J. Robbins 		return (cp);
1000a5f0fb15SPaul Saab 	for (p = cmdbuf;  p < cp;  p++)
1001a5f0fb15SPaul Saab 	{
1002000ba3e8STim J. Robbins 		if (meta_quoted)
1003a5f0fb15SPaul Saab 		{
1004*c77c4889SXin LI 			meta_quoted = FALSE;
1005000ba3e8STim J. Robbins 		} else if (esclen > 0 && p + esclen < cp &&
1006000ba3e8STim J. Robbins 		           strncmp(p, esc, esclen) == 0)
1007a5f0fb15SPaul Saab 		{
1008*c77c4889SXin LI 			meta_quoted = TRUE;
1009000ba3e8STim J. Robbins 			p += esclen - 1;
1010000ba3e8STim J. Robbins 		} else if (delim_quoted)
1011000ba3e8STim J. Robbins 		{
1012000ba3e8STim J. Robbins 			if (*p == closequote)
1013*c77c4889SXin LI 				delim_quoted = FALSE;
1014000ba3e8STim J. Robbins 		} else /* (!delim_quoted) */
1015000ba3e8STim J. Robbins 		{
1016000ba3e8STim J. Robbins 			if (*p == openquote)
1017*c77c4889SXin LI 				delim_quoted = TRUE;
1018000ba3e8STim J. Robbins 			else if (*p == ' ')
1019000ba3e8STim J. Robbins 				word = p+1;
1020a5f0fb15SPaul Saab 		}
1021a5f0fb15SPaul Saab 	}
1022a5f0fb15SPaul Saab #endif
1023a5f0fb15SPaul Saab 	return (word);
1024a5f0fb15SPaul Saab }
1025a5f0fb15SPaul Saab 
1026a5f0fb15SPaul Saab /*
1027a5f0fb15SPaul Saab  * Set things up to enter completion mode.
1028a5f0fb15SPaul Saab  * Expand the word under the cursor into a list of filenames
1029a5f0fb15SPaul Saab  * which start with that word, and set tk_text to that list.
1030a5f0fb15SPaul Saab  */
1031d713e089SXin LI static void init_compl(void)
1032a5f0fb15SPaul Saab {
1033a5f0fb15SPaul Saab 	char *word;
1034a5f0fb15SPaul Saab 	char c;
1035a5f0fb15SPaul Saab 
1036a5f0fb15SPaul Saab 	/*
1037a5f0fb15SPaul Saab 	 * Get rid of any previous tk_text.
1038a5f0fb15SPaul Saab 	 */
1039a5f0fb15SPaul Saab 	if (tk_text != NULL)
1040a5f0fb15SPaul Saab 	{
1041a5f0fb15SPaul Saab 		free(tk_text);
1042a5f0fb15SPaul Saab 		tk_text = NULL;
1043a5f0fb15SPaul Saab 	}
1044a5f0fb15SPaul Saab 	/*
1045a5f0fb15SPaul Saab 	 * Find the original (uncompleted) word in the command buffer.
1046a5f0fb15SPaul Saab 	 */
1047a5f0fb15SPaul Saab 	word = delimit_word();
1048a5f0fb15SPaul Saab 	if (word == NULL)
1049a5f0fb15SPaul Saab 		return;
1050a5f0fb15SPaul Saab 	/*
1051a5f0fb15SPaul Saab 	 * Set the insertion point to the point in the command buffer
1052a5f0fb15SPaul Saab 	 * where the original (uncompleted) word now sits.
1053a5f0fb15SPaul Saab 	 */
1054a5f0fb15SPaul Saab 	tk_ipoint = word;
1055a5f0fb15SPaul Saab 	/*
1056a5f0fb15SPaul Saab 	 * Save the original (uncompleted) word
1057a5f0fb15SPaul Saab 	 */
1058a5f0fb15SPaul Saab 	if (tk_original != NULL)
1059a5f0fb15SPaul Saab 		free(tk_original);
1060*c77c4889SXin LI 	tk_original = (char *) ecalloc(ptr_diff(cp,word)+1, sizeof(char));
1061*c77c4889SXin LI 	strncpy(tk_original, word, ptr_diff(cp,word));
1062a5f0fb15SPaul Saab 	/*
1063a5f0fb15SPaul Saab 	 * Get the expanded filename.
1064a5f0fb15SPaul Saab 	 * This may result in a single filename, or
1065a5f0fb15SPaul Saab 	 * a blank-separated list of filenames.
1066a5f0fb15SPaul Saab 	 */
1067a5f0fb15SPaul Saab 	c = *cp;
1068a5f0fb15SPaul Saab 	*cp = '\0';
1069000ba3e8STim J. Robbins 	if (*word != openquote)
1070000ba3e8STim J. Robbins 	{
1071a5f0fb15SPaul Saab 		tk_text = fcomplete(word);
1072000ba3e8STim J. Robbins 	} else
1073000ba3e8STim J. Robbins 	{
1074e2449719SXin LI #if MSDOS_COMPILER
1075e2449719SXin LI 		char *qword = NULL;
1076e2449719SXin LI #else
1077000ba3e8STim J. Robbins 		char *qword = shell_quote(word+1);
1078e2449719SXin LI #endif
1079000ba3e8STim J. Robbins 		if (qword == NULL)
1080000ba3e8STim J. Robbins 			tk_text = fcomplete(word+1);
1081000ba3e8STim J. Robbins 		else
1082000ba3e8STim J. Robbins 		{
1083000ba3e8STim J. Robbins 			tk_text = fcomplete(qword);
1084000ba3e8STim J. Robbins 			free(qword);
1085000ba3e8STim J. Robbins 		}
1086000ba3e8STim J. Robbins 	}
1087a5f0fb15SPaul Saab 	*cp = c;
1088a5f0fb15SPaul Saab }
1089a5f0fb15SPaul Saab 
1090a5f0fb15SPaul Saab /*
1091a5f0fb15SPaul Saab  * Return the next word in the current completion list.
1092a5f0fb15SPaul Saab  */
1093*c77c4889SXin LI static constant char * next_compl(int action, constant char *prev)
1094a5f0fb15SPaul Saab {
1095a5f0fb15SPaul Saab 	switch (action)
1096a5f0fb15SPaul Saab 	{
1097a5f0fb15SPaul Saab 	case EC_F_COMPLETE:
1098a5f0fb15SPaul Saab 		return (forw_textlist(&tk_tlist, prev));
1099a5f0fb15SPaul Saab 	case EC_B_COMPLETE:
1100a5f0fb15SPaul Saab 		return (back_textlist(&tk_tlist, prev));
1101a5f0fb15SPaul Saab 	}
1102a5f0fb15SPaul Saab 	/* Cannot happen */
1103a5f0fb15SPaul Saab 	return ("?");
1104a5f0fb15SPaul Saab }
1105a5f0fb15SPaul Saab 
1106a5f0fb15SPaul Saab /*
1107a5f0fb15SPaul Saab  * Complete the filename before (or under) the cursor.
1108a5f0fb15SPaul Saab  * cmd_complete may be called multiple times.  The global in_completion
1109a5f0fb15SPaul Saab  * remembers whether this call is the first time (create the list),
1110a5f0fb15SPaul Saab  * or a subsequent time (step thru the list).
1111a5f0fb15SPaul Saab  */
1112d713e089SXin LI static int cmd_complete(int action)
1113a5f0fb15SPaul Saab {
1114*c77c4889SXin LI 	constant char *s;
1115a5f0fb15SPaul Saab 
1116a5f0fb15SPaul Saab 	if (!in_completion || action == EC_EXPAND)
1117a5f0fb15SPaul Saab 	{
1118a5f0fb15SPaul Saab 		/*
1119a5f0fb15SPaul Saab 		 * Expand the word under the cursor and
1120a5f0fb15SPaul Saab 		 * use the first word in the expansion
1121a5f0fb15SPaul Saab 		 * (or the entire expansion if we're doing EC_EXPAND).
1122a5f0fb15SPaul Saab 		 */
1123a5f0fb15SPaul Saab 		init_compl();
1124a5f0fb15SPaul Saab 		if (tk_text == NULL)
1125a5f0fb15SPaul Saab 		{
1126a5f0fb15SPaul Saab 			bell();
1127a5f0fb15SPaul Saab 			return (CC_OK);
1128a5f0fb15SPaul Saab 		}
1129a5f0fb15SPaul Saab 		if (action == EC_EXPAND)
1130a5f0fb15SPaul Saab 		{
1131a5f0fb15SPaul Saab 			/*
1132a5f0fb15SPaul Saab 			 * Use the whole list.
1133a5f0fb15SPaul Saab 			 */
1134a5f0fb15SPaul Saab 			tk_trial = tk_text;
1135a5f0fb15SPaul Saab 		} else
1136a5f0fb15SPaul Saab 		{
1137a5f0fb15SPaul Saab 			/*
1138a5f0fb15SPaul Saab 			 * Use the first filename in the list.
1139a5f0fb15SPaul Saab 			 */
1140*c77c4889SXin LI 			in_completion = TRUE;
1141a5f0fb15SPaul Saab 			init_textlist(&tk_tlist, tk_text);
1142a5f0fb15SPaul Saab 			tk_trial = next_compl(action, (char*)NULL);
1143a5f0fb15SPaul Saab 		}
1144a5f0fb15SPaul Saab 	} else
1145a5f0fb15SPaul Saab 	{
1146a5f0fb15SPaul Saab 		/*
1147a5f0fb15SPaul Saab 		 * We already have a completion list.
1148a5f0fb15SPaul Saab 		 * Use the next/previous filename from the list.
1149a5f0fb15SPaul Saab 		 */
1150a5f0fb15SPaul Saab 		tk_trial = next_compl(action, tk_trial);
1151a5f0fb15SPaul Saab 	}
1152a5f0fb15SPaul Saab 
1153a5f0fb15SPaul Saab 	/*
1154a5f0fb15SPaul Saab 	 * Remove the original word, or the previous trial completion.
1155a5f0fb15SPaul Saab 	 */
1156a5f0fb15SPaul Saab 	while (cp > tk_ipoint)
1157a5f0fb15SPaul Saab 		(void) cmd_erase();
1158a5f0fb15SPaul Saab 
1159a5f0fb15SPaul Saab 	if (tk_trial == NULL)
1160a5f0fb15SPaul Saab 	{
1161a5f0fb15SPaul Saab 		/*
1162a5f0fb15SPaul Saab 		 * There are no more trial completions.
1163a5f0fb15SPaul Saab 		 * Insert the original (uncompleted) filename.
1164a5f0fb15SPaul Saab 		 */
1165*c77c4889SXin LI 		in_completion = FALSE;
1166a5f0fb15SPaul Saab 		if (cmd_istr(tk_original) != CC_OK)
1167a5f0fb15SPaul Saab 			goto fail;
1168a5f0fb15SPaul Saab 	} else
1169a5f0fb15SPaul Saab 	{
1170a5f0fb15SPaul Saab 		/*
1171a5f0fb15SPaul Saab 		 * Insert trial completion.
1172a5f0fb15SPaul Saab 		 */
1173a5f0fb15SPaul Saab 		if (cmd_istr(tk_trial) != CC_OK)
1174a5f0fb15SPaul Saab 			goto fail;
1175a5f0fb15SPaul Saab 		/*
1176a5f0fb15SPaul Saab 		 * If it is a directory, append a slash.
1177a5f0fb15SPaul Saab 		 */
1178a5f0fb15SPaul Saab 		if (is_dir(tk_trial))
1179a5f0fb15SPaul Saab 		{
1180a5f0fb15SPaul Saab 			if (cp > cmdbuf && cp[-1] == closequote)
1181a5f0fb15SPaul Saab 				(void) cmd_erase();
1182a5f0fb15SPaul Saab 			s = lgetenv("LESSSEPARATOR");
1183a5f0fb15SPaul Saab 			if (s == NULL)
1184a5f0fb15SPaul Saab 				s = PATHNAME_SEP;
1185a5f0fb15SPaul Saab 			if (cmd_istr(s) != CC_OK)
1186a5f0fb15SPaul Saab 				goto fail;
1187a5f0fb15SPaul Saab 		}
1188a5f0fb15SPaul Saab 	}
1189a5f0fb15SPaul Saab 
1190a5f0fb15SPaul Saab 	return (CC_OK);
1191a5f0fb15SPaul Saab 
1192a5f0fb15SPaul Saab fail:
1193*c77c4889SXin LI 	in_completion = FALSE;
1194a5f0fb15SPaul Saab 	bell();
1195a5f0fb15SPaul Saab 	return (CC_OK);
1196a5f0fb15SPaul Saab }
1197a5f0fb15SPaul Saab 
1198a5f0fb15SPaul Saab #endif /* TAB_COMPLETE_FILENAME */
1199a5f0fb15SPaul Saab 
1200a5f0fb15SPaul Saab /*
1201a5f0fb15SPaul Saab  * Process a single character of a multi-character command, such as
1202a5f0fb15SPaul Saab  * a number, or the pattern of a search command.
1203a5f0fb15SPaul Saab  * Returns:
1204a5f0fb15SPaul Saab  *      CC_OK           The char was accepted.
1205a5f0fb15SPaul Saab  *      CC_QUIT         The char requests the command to be aborted.
1206a5f0fb15SPaul Saab  *      CC_ERROR        The char could not be accepted due to an error.
1207a5f0fb15SPaul Saab  */
1208*c77c4889SXin LI public int cmd_char(char c)
1209a5f0fb15SPaul Saab {
1210a5f0fb15SPaul Saab 	int action;
1211*c77c4889SXin LI 	size_t len;
12126dcb072bSXin LI 
12136dcb072bSXin LI 	if (!utf_mode)
12146dcb072bSXin LI 	{
12156dcb072bSXin LI 		cmd_mbc_buf[0] = c;
12166dcb072bSXin LI 		len = 1;
12176dcb072bSXin LI 	} else
12186dcb072bSXin LI 	{
12196dcb072bSXin LI 		/* Perform strict validation in all possible cases.  */
12206dcb072bSXin LI 		if (cmd_mbc_buf_len == 0)
12216dcb072bSXin LI 		{
12226dcb072bSXin LI 		 retry:
12236dcb072bSXin LI 			cmd_mbc_buf_index = 1;
12246dcb072bSXin LI 			*cmd_mbc_buf = c;
12256dcb072bSXin LI 			if (IS_ASCII_OCTET(c))
12266dcb072bSXin LI 				cmd_mbc_buf_len = 1;
1227b2ea2440SXin LI #if MSDOS_COMPILER || OS2
1228*c77c4889SXin LI 			else if (c == '\340' && IS_ASCII_OCTET(peekcc()))
1229b2ea2440SXin LI 			{
1230b2ea2440SXin LI 				/* Assume a special key. */
1231b2ea2440SXin LI 				cmd_mbc_buf_len = 1;
1232b2ea2440SXin LI 			}
1233b2ea2440SXin LI #endif
12346dcb072bSXin LI 			else if (IS_UTF8_LEAD(c))
12356dcb072bSXin LI 			{
12366dcb072bSXin LI 				cmd_mbc_buf_len = utf_len(c);
12376dcb072bSXin LI 				return (CC_OK);
12386dcb072bSXin LI 			} else
12396dcb072bSXin LI 			{
12406dcb072bSXin LI 				/* UTF8_INVALID or stray UTF8_TRAIL */
12416dcb072bSXin LI 				bell();
12426dcb072bSXin LI 				return (CC_ERROR);
12436dcb072bSXin LI 			}
12446dcb072bSXin LI 		} else if (IS_UTF8_TRAIL(c))
12456dcb072bSXin LI 		{
12466dcb072bSXin LI 			cmd_mbc_buf[cmd_mbc_buf_index++] = c;
12476dcb072bSXin LI 			if (cmd_mbc_buf_index < cmd_mbc_buf_len)
12486dcb072bSXin LI 				return (CC_OK);
1249a15691bfSXin LI 			if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index))
12506dcb072bSXin LI 			{
12516dcb072bSXin LI 				/* complete, but not well formed (non-shortest form), sequence */
12526dcb072bSXin LI 				cmd_mbc_buf_len = 0;
12536dcb072bSXin LI 				bell();
12546dcb072bSXin LI 				return (CC_ERROR);
12556dcb072bSXin LI 			}
12566dcb072bSXin LI 		} else
12576dcb072bSXin LI 		{
12586dcb072bSXin LI 			/* Flush incomplete (truncated) sequence.  */
12596dcb072bSXin LI 			cmd_mbc_buf_len = 0;
12606dcb072bSXin LI 			bell();
12616dcb072bSXin LI 			/* Handle new char.  */
12626dcb072bSXin LI 			goto retry;
12636dcb072bSXin LI 		}
12646dcb072bSXin LI 
1265*c77c4889SXin LI 		len = (size_t) cmd_mbc_buf_len; /*{{type-issue}}*/
12666dcb072bSXin LI 		cmd_mbc_buf_len = 0;
12676dcb072bSXin LI 	}
1268a5f0fb15SPaul Saab 
1269a5f0fb15SPaul Saab 	if (literal)
1270a5f0fb15SPaul Saab 	{
1271a5f0fb15SPaul Saab 		/*
1272a5f0fb15SPaul Saab 		 * Insert the char, even if it is a line-editing char.
1273a5f0fb15SPaul Saab 		 */
1274*c77c4889SXin LI 		literal = FALSE;
12756dcb072bSXin LI 		return (cmd_ichar(cmd_mbc_buf, len));
1276a5f0fb15SPaul Saab 	}
1277a5f0fb15SPaul Saab 
1278a5f0fb15SPaul Saab 	/*
12796dcb072bSXin LI 	 * See if it is a line-editing character.
1280a5f0fb15SPaul Saab 	 */
12816dcb072bSXin LI 	if (in_mca() && len == 1)
1282a5f0fb15SPaul Saab 	{
1283a5f0fb15SPaul Saab 		action = cmd_edit(c);
1284a5f0fb15SPaul Saab 		switch (action)
1285a5f0fb15SPaul Saab 		{
1286a5f0fb15SPaul Saab 		case CC_OK:
1287a5f0fb15SPaul Saab 		case CC_QUIT:
1288a5f0fb15SPaul Saab 			return (action);
1289a5f0fb15SPaul Saab 		case CC_PASS:
1290a5f0fb15SPaul Saab 			break;
1291a5f0fb15SPaul Saab 		}
1292a5f0fb15SPaul Saab 	}
1293a5f0fb15SPaul Saab 
1294a5f0fb15SPaul Saab 	/*
1295a5f0fb15SPaul Saab 	 * Insert the char into the command buffer.
1296a5f0fb15SPaul Saab 	 */
12976dcb072bSXin LI 	return (cmd_ichar(cmd_mbc_buf, len));
1298a5f0fb15SPaul Saab }
1299a5f0fb15SPaul Saab 
1300a5f0fb15SPaul Saab /*
1301a5f0fb15SPaul Saab  * Return the number currently in the command buffer.
1302a5f0fb15SPaul Saab  */
1303*c77c4889SXin LI public LINENUM cmd_int(mutable long *frac)
1304a5f0fb15SPaul Saab {
1305*c77c4889SXin LI 	constant char *p;
1306000ba3e8STim J. Robbins 	LINENUM n = 0;
1307*c77c4889SXin LI 	lbool err;
1308000ba3e8STim J. Robbins 
13097f074f9cSXin LI 	for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
131095270f73SXin LI 	{
1311d713e089SXin LI 		if (ckd_mul(&n, n, 10) || ckd_add(&n, n, *p - '0'))
131295270f73SXin LI 		{
131395270f73SXin LI 			error("Integer is too big", NULL_PARG);
131495270f73SXin LI 			return (0);
131595270f73SXin LI 		}
131695270f73SXin LI 	}
13177f074f9cSXin LI 	*frac = 0;
13187f074f9cSXin LI 	if (*p++ == '.')
13197f074f9cSXin LI 	{
13207f074f9cSXin LI 		*frac = getfraction(&p, NULL, &err);
13217f074f9cSXin LI 		/* {{ do something if err is set? }} */
13227f074f9cSXin LI 	}
1323000ba3e8STim J. Robbins 	return (n);
1324a5f0fb15SPaul Saab }
1325a5f0fb15SPaul Saab 
1326a5f0fb15SPaul Saab /*
1327a5f0fb15SPaul Saab  * Return a pointer to the command buffer.
1328a5f0fb15SPaul Saab  */
1329*c77c4889SXin LI public constant char * get_cmdbuf(void)
1330a5f0fb15SPaul Saab {
133195270f73SXin LI 	if (cmd_mbc_buf_index < cmd_mbc_buf_len)
133295270f73SXin LI 		/* Don't return buffer containing an incomplete multibyte char. */
133395270f73SXin LI 		return (NULL);
1334a5f0fb15SPaul Saab 	return (cmdbuf);
1335a5f0fb15SPaul Saab }
13366dcb072bSXin LI 
13377374caaaSXin LI #if CMD_HISTORY
13387f074f9cSXin LI /*
13397f074f9cSXin LI  * Return the last (most recent) string in the current command history.
13407f074f9cSXin LI  */
1341*c77c4889SXin LI public constant char * cmd_lastpattern(void)
13427f074f9cSXin LI {
13437f074f9cSXin LI 	if (curr_mlist == NULL)
13447f074f9cSXin LI 		return (NULL);
13457f074f9cSXin LI 	return (curr_mlist->curr_mp->prev->string);
13467f074f9cSXin LI }
13477374caaaSXin LI #endif
13487f074f9cSXin LI 
13496dcb072bSXin LI #if CMD_HISTORY
13506dcb072bSXin LI /*
1351a15691bfSXin LI  */
1352d713e089SXin LI static int mlist_size(struct mlist *ml)
1353a15691bfSXin LI {
1354a15691bfSXin LI 	int size = 0;
1355a15691bfSXin LI 	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1356a15691bfSXin LI 		++size;
1357a15691bfSXin LI 	return size;
1358a15691bfSXin LI }
1359a15691bfSXin LI 
1360a15691bfSXin LI /*
13616dcb072bSXin LI  * Get the name of the history file.
13626dcb072bSXin LI  */
1363*c77c4889SXin LI static char * histfile_find(lbool must_exist)
136495270f73SXin LI {
1365*c77c4889SXin LI 	constant char *home = lgetenv("HOME");
136695270f73SXin LI 	char *name = NULL;
136795270f73SXin LI 
136895270f73SXin LI 	/* Try in $XDG_STATE_HOME, then in $HOME/.local/state, then in $XDG_DATA_HOME, then in $HOME. */
136995270f73SXin LI #if OS2
137095270f73SXin LI 	if (isnullenv(home))
137195270f73SXin LI 		home = lgetenv("INIT");
137295270f73SXin LI #endif
137395270f73SXin LI 	name = dirfile(lgetenv("XDG_STATE_HOME"), &LESSHISTFILE[1], must_exist);
137495270f73SXin LI 	if (name == NULL)
137595270f73SXin LI 	{
137695270f73SXin LI 		char *dir = dirfile(home, ".local/state", 1);
137795270f73SXin LI 		if (dir != NULL)
137895270f73SXin LI 		{
137995270f73SXin LI 			name = dirfile(dir, &LESSHISTFILE[1], must_exist);
138095270f73SXin LI 			free(dir);
138195270f73SXin LI 		}
138295270f73SXin LI 	}
138395270f73SXin LI 	if (name == NULL)
138495270f73SXin LI 		name = dirfile(lgetenv("XDG_DATA_HOME"), &LESSHISTFILE[1], must_exist);
138595270f73SXin LI 	if (name == NULL)
138695270f73SXin LI 		name = dirfile(home, LESSHISTFILE, must_exist);
138795270f73SXin LI 	return (name);
138895270f73SXin LI }
138995270f73SXin LI 
1390*c77c4889SXin LI static char * histfile_name(lbool must_exist)
13916dcb072bSXin LI {
1392*c77c4889SXin LI 	constant char *name;
1393*c77c4889SXin LI 	char *wname;
13946dcb072bSXin LI 
13956dcb072bSXin LI 	/* See if filename is explicitly specified by $LESSHISTFILE. */
13966dcb072bSXin LI 	name = lgetenv("LESSHISTFILE");
1397b7780dbeSXin LI 	if (!isnullenv(name))
13986dcb072bSXin LI 	{
13997f074f9cSXin LI 		if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
14006dcb072bSXin LI 			/* $LESSHISTFILE == "-" means don't use a history file. */
14016dcb072bSXin LI 			return (NULL);
14026dcb072bSXin LI 		return (save(name));
14036dcb072bSXin LI 	}
14046dcb072bSXin LI 
1405a15691bfSXin LI 	/* See if history file is disabled in the build. */
1406a15691bfSXin LI 	if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0)
1407a15691bfSXin LI 		return (NULL);
1408a15691bfSXin LI 
1409*c77c4889SXin LI 	wname = NULL;
141030a1828cSXin LI 	if (!must_exist)
141130a1828cSXin LI 	{
141230a1828cSXin LI 	 	/* If we're writing the file and the file already exists, use it. */
1413*c77c4889SXin LI 		wname = histfile_find(TRUE);
14146dcb072bSXin LI 	}
1415*c77c4889SXin LI 	if (wname == NULL)
1416*c77c4889SXin LI 		wname = histfile_find(must_exist);
1417*c77c4889SXin LI 	return (wname);
14186dcb072bSXin LI }
14196dcb072bSXin LI 
14206dcb072bSXin LI /*
1421a15691bfSXin LI  * Read a .lesshst file and call a callback for each line in the file.
14226dcb072bSXin LI  */
1423*c77c4889SXin LI static void read_cmdhist2(void (*action)(void*,struct mlist*,constant char*), void *uparam, int skip_search, int skip_shell)
14246dcb072bSXin LI {
14256dcb072bSXin LI 	struct mlist *ml = NULL;
14266dcb072bSXin LI 	char line[CMDBUF_SIZE];
14276dcb072bSXin LI 	char *filename;
14286dcb072bSXin LI 	FILE *f;
1429a15691bfSXin LI 	int *skip = NULL;
14306dcb072bSXin LI 
1431*c77c4889SXin LI 	filename = histfile_name(TRUE);
14326dcb072bSXin LI 	if (filename == NULL)
14336dcb072bSXin LI 		return;
14346dcb072bSXin LI 	f = fopen(filename, "r");
14356dcb072bSXin LI 	free(filename);
14366dcb072bSXin LI 	if (f == NULL)
14376dcb072bSXin LI 		return;
14386dcb072bSXin LI 	if (fgets(line, sizeof(line), f) == NULL ||
14396dcb072bSXin LI 	    strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
14406dcb072bSXin LI 	{
14416dcb072bSXin LI 		fclose(f);
14426dcb072bSXin LI 		return;
14436dcb072bSXin LI 	}
14446dcb072bSXin LI 	while (fgets(line, sizeof(line), f) != NULL)
14456dcb072bSXin LI 	{
1446*c77c4889SXin LI 		char *p;
14476dcb072bSXin LI 		for (p = line;  *p != '\0';  p++)
14486dcb072bSXin LI 		{
14496dcb072bSXin LI 			if (*p == '\n' || *p == '\r')
14506dcb072bSXin LI 			{
14516dcb072bSXin LI 				*p = '\0';
14526dcb072bSXin LI 				break;
14536dcb072bSXin LI 			}
14546dcb072bSXin LI 		}
14556dcb072bSXin LI 		if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1456a15691bfSXin LI 		{
14576dcb072bSXin LI 			ml = &mlist_search;
1458a15691bfSXin LI 			skip = &skip_search;
1459a15691bfSXin LI 		} else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
1460efd72c2eSXin LI 		{
1461efd72c2eSXin LI #if SHELL_ESCAPE || PIPEC
14626dcb072bSXin LI 			ml = &mlist_shell;
1463a15691bfSXin LI 			skip = &skip_shell;
1464efd72c2eSXin LI #else
1465efd72c2eSXin LI 			ml = NULL;
1466a15691bfSXin LI 			skip = NULL;
14676dcb072bSXin LI #endif
1468b7780dbeSXin LI 		} else if (strcmp(line, HISTFILE_MARK_SECTION) == 0)
1469b7780dbeSXin LI 		{
1470b7780dbeSXin LI 			ml = NULL;
1471efd72c2eSXin LI 		} else if (*line == '"')
14726dcb072bSXin LI 		{
14736dcb072bSXin LI 			if (ml != NULL)
1474a15691bfSXin LI 			{
1475a15691bfSXin LI 				if (skip != NULL && *skip > 0)
1476a15691bfSXin LI 					--(*skip);
1477a15691bfSXin LI 				else
1478a15691bfSXin LI 					(*action)(uparam, ml, line+1);
1479a15691bfSXin LI 			}
1480b7780dbeSXin LI 		} else if (*line == 'm')
1481b7780dbeSXin LI 		{
1482b7780dbeSXin LI 			(*action)(uparam, NULL, line);
14836dcb072bSXin LI 		}
14846dcb072bSXin LI 	}
14856dcb072bSXin LI 	fclose(f);
1486a15691bfSXin LI }
1487a15691bfSXin LI 
1488*c77c4889SXin LI static void read_cmdhist(void (*action)(void*,struct mlist*,constant char*), void *uparam, lbool skip_search, lbool skip_shell)
1489a15691bfSXin LI {
1490*c77c4889SXin LI 	if (!secure_allow(SF_HISTORY))
149195270f73SXin LI 		return;
1492a15691bfSXin LI 	read_cmdhist2(action, uparam, skip_search, skip_shell);
1493a15691bfSXin LI 	(*action)(uparam, NULL, NULL); /* signal end of file */
1494a15691bfSXin LI }
1495a15691bfSXin LI 
1496*c77c4889SXin LI static void addhist_init(void *uparam, struct mlist *ml, constant char *string)
1497a15691bfSXin LI {
1498*c77c4889SXin LI 	(void) uparam;
1499b7780dbeSXin LI 	if (ml != NULL)
1500a15691bfSXin LI 		cmd_addhist(ml, string, 0);
1501b7780dbeSXin LI 	else if (string != NULL)
1502*c77c4889SXin LI 		restore_mark(string);
1503a15691bfSXin LI }
1504a15691bfSXin LI #endif /* CMD_HISTORY */
1505a15691bfSXin LI 
1506a15691bfSXin LI /*
1507a15691bfSXin LI  * Initialize history from a .lesshist file.
1508a15691bfSXin LI  */
1509d713e089SXin LI public void init_cmdhist(void)
1510a15691bfSXin LI {
1511a15691bfSXin LI #if CMD_HISTORY
1512a15691bfSXin LI 	read_cmdhist(&addhist_init, NULL, 0, 0);
15136dcb072bSXin LI #endif /* CMD_HISTORY */
15146dcb072bSXin LI }
15156dcb072bSXin LI 
15166dcb072bSXin LI /*
1517a15691bfSXin LI  * Write the header for a section of the history file.
15186dcb072bSXin LI  */
15196dcb072bSXin LI #if CMD_HISTORY
1520d713e089SXin LI static void write_mlist_header(struct mlist *ml, FILE *f)
15216dcb072bSXin LI {
1522a15691bfSXin LI 	if (ml == &mlist_search)
1523a15691bfSXin LI 		fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1524a15691bfSXin LI #if SHELL_ESCAPE || PIPEC
1525a15691bfSXin LI 	else if (ml == &mlist_shell)
1526a15691bfSXin LI 		fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1527a15691bfSXin LI #endif
15286dcb072bSXin LI }
1529a15691bfSXin LI 
1530a15691bfSXin LI /*
1531a15691bfSXin LI  * Write all modified entries in an mlist to the history file.
1532a15691bfSXin LI  */
1533d713e089SXin LI static void write_mlist(struct mlist *ml, FILE *f)
1534a15691bfSXin LI {
15356dcb072bSXin LI 	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1536a15691bfSXin LI 	{
1537a15691bfSXin LI 		if (!ml->modified)
1538a15691bfSXin LI 			continue;
15396dcb072bSXin LI 		fprintf(f, "\"%s\n", ml->string);
1540*c77c4889SXin LI 		ml->modified = FALSE;
1541a15691bfSXin LI 	}
1542*c77c4889SXin LI 	ml->modified = FALSE; /* entire mlist is now unmodified */
1543a15691bfSXin LI }
1544a15691bfSXin LI 
1545a15691bfSXin LI /*
1546a15691bfSXin LI  * Make a temp name in the same directory as filename.
1547a15691bfSXin LI  */
1548*c77c4889SXin LI static char * make_tempname(constant char *filename)
1549a15691bfSXin LI {
1550a15691bfSXin LI 	char lastch;
1551a15691bfSXin LI 	char *tempname = ecalloc(1, strlen(filename)+1);
1552a15691bfSXin LI 	strcpy(tempname, filename);
1553a15691bfSXin LI 	lastch = tempname[strlen(tempname)-1];
1554a15691bfSXin LI 	tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q';
1555a15691bfSXin LI 	return tempname;
1556a15691bfSXin LI }
1557a15691bfSXin LI 
1558a15691bfSXin LI struct save_ctx
1559a15691bfSXin LI {
1560a15691bfSXin LI 	struct mlist *mlist;
1561a15691bfSXin LI 	FILE *fout;
1562a15691bfSXin LI };
1563a15691bfSXin LI 
1564a15691bfSXin LI /*
1565a15691bfSXin LI  * Copy entries from the saved history file to a new file.
1566a15691bfSXin LI  * At the end of each mlist, append any new entries
1567a15691bfSXin LI  * created during this session.
1568a15691bfSXin LI  */
1569*c77c4889SXin LI static void copy_hist(void *uparam, struct mlist *ml, constant char *string)
1570a15691bfSXin LI {
1571a15691bfSXin LI 	struct save_ctx *ctx = (struct save_ctx *) uparam;
1572a15691bfSXin LI 
1573b7780dbeSXin LI 	if (ml != NULL && ml != ctx->mlist) {
1574a15691bfSXin LI 		/* We're changing mlists. */
1575a15691bfSXin LI 		if (ctx->mlist)
1576a15691bfSXin LI 			/* Append any new entries to the end of the current mlist. */
1577a15691bfSXin LI 			write_mlist(ctx->mlist, ctx->fout);
1578a15691bfSXin LI 		/* Write the header for the new mlist. */
1579a15691bfSXin LI 		ctx->mlist = ml;
1580a15691bfSXin LI 		write_mlist_header(ctx->mlist, ctx->fout);
1581a15691bfSXin LI 	}
1582b7780dbeSXin LI 
1583b7780dbeSXin LI 	if (string == NULL) /* End of file */
1584a15691bfSXin LI 	{
1585a15691bfSXin LI 		/* Write any sections that were not in the original file. */
1586a15691bfSXin LI 		if (mlist_search.modified)
1587a15691bfSXin LI 		{
1588a15691bfSXin LI 			write_mlist_header(&mlist_search, ctx->fout);
1589a15691bfSXin LI 			write_mlist(&mlist_search, ctx->fout);
1590a15691bfSXin LI 		}
1591a15691bfSXin LI #if SHELL_ESCAPE || PIPEC
1592a15691bfSXin LI 		if (mlist_shell.modified)
1593a15691bfSXin LI 		{
1594a15691bfSXin LI 			write_mlist_header(&mlist_shell, ctx->fout);
1595a15691bfSXin LI 			write_mlist(&mlist_shell, ctx->fout);
1596a15691bfSXin LI 		}
1597a15691bfSXin LI #endif
1598b7780dbeSXin LI 	} else if (ml != NULL)
1599b7780dbeSXin LI 	{
1600b7780dbeSXin LI 		/* Copy mlist entry. */
1601b7780dbeSXin LI 		fprintf(ctx->fout, "\"%s\n", string);
1602a15691bfSXin LI 	}
1603b7780dbeSXin LI 	/* Skip marks */
16046dcb072bSXin LI }
16056dcb072bSXin LI #endif /* CMD_HISTORY */
16066dcb072bSXin LI 
16076dcb072bSXin LI /*
1608a15691bfSXin LI  * Make a file readable only by its owner.
16096dcb072bSXin LI  */
1610d713e089SXin LI static void make_file_private(FILE *f)
16117374caaaSXin LI {
1612a15691bfSXin LI #if HAVE_FCHMOD
1613*c77c4889SXin LI 	lbool do_chmod = TRUE;
16147374caaaSXin LI #if HAVE_STAT
16157374caaaSXin LI 	struct stat statbuf;
16167374caaaSXin LI 	int r = fstat(fileno(f), &statbuf);
16177374caaaSXin LI 	if (r < 0 || !S_ISREG(statbuf.st_mode))
16187374caaaSXin LI 		/* Don't chmod if not a regular file. */
1619*c77c4889SXin LI 		do_chmod = FALSE;
16207374caaaSXin LI #endif
16217374caaaSXin LI 	if (do_chmod)
16226dcb072bSXin LI 		fchmod(fileno(f), 0600);
1623a15691bfSXin LI #endif
16247374caaaSXin LI }
16256dcb072bSXin LI 
1626a15691bfSXin LI /*
1627a15691bfSXin LI  * Does the history file need to be updated?
1628a15691bfSXin LI  */
162995270f73SXin LI #if CMD_HISTORY
1630*c77c4889SXin LI static lbool histfile_modified(void)
1631a15691bfSXin LI {
1632a15691bfSXin LI 	if (mlist_search.modified)
1633*c77c4889SXin LI 		return TRUE;
16346dcb072bSXin LI #if SHELL_ESCAPE || PIPEC
1635a15691bfSXin LI 	if (mlist_shell.modified)
1636*c77c4889SXin LI 		return TRUE;
16376dcb072bSXin LI #endif
1638b7780dbeSXin LI 	if (marks_modified)
1639*c77c4889SXin LI 		return TRUE;
1640*c77c4889SXin LI 	return FALSE;
1641a15691bfSXin LI }
164295270f73SXin LI #endif
16436dcb072bSXin LI 
1644a15691bfSXin LI /*
1645a15691bfSXin LI  * Update the .lesshst file.
1646a15691bfSXin LI  */
1647d713e089SXin LI public void save_cmdhist(void)
1648a15691bfSXin LI {
1649a15691bfSXin LI #if CMD_HISTORY
1650a15691bfSXin LI 	char *histname;
1651a15691bfSXin LI 	char *tempname;
1652a15691bfSXin LI 	int skip_search;
1653a15691bfSXin LI 	int skip_shell;
1654a15691bfSXin LI 	struct save_ctx ctx;
1655*c77c4889SXin LI 	constant char *s;
1656a15691bfSXin LI 	FILE *fout = NULL;
1657a15691bfSXin LI 	int histsize = 0;
1658a15691bfSXin LI 
1659*c77c4889SXin LI 	if (!secure_allow(SF_HISTORY) || !histfile_modified())
1660a15691bfSXin LI 		return;
166130a1828cSXin LI 	histname = histfile_name(0);
1662a15691bfSXin LI 	if (histname == NULL)
1663a15691bfSXin LI 		return;
1664a15691bfSXin LI 	tempname = make_tempname(histname);
1665a15691bfSXin LI 	fout = fopen(tempname, "w");
1666a15691bfSXin LI 	if (fout != NULL)
1667a15691bfSXin LI 	{
1668a15691bfSXin LI 		make_file_private(fout);
1669a15691bfSXin LI 		s = lgetenv("LESSHISTSIZE");
1670a15691bfSXin LI 		if (s != NULL)
1671a15691bfSXin LI 			histsize = atoi(s);
1672a15691bfSXin LI 		if (histsize <= 0)
1673a15691bfSXin LI 			histsize = 100;
1674a15691bfSXin LI 		skip_search = mlist_size(&mlist_search) - histsize;
1675a15691bfSXin LI #if SHELL_ESCAPE || PIPEC
1676a15691bfSXin LI 		skip_shell = mlist_size(&mlist_shell) - histsize;
1677a15691bfSXin LI #endif
1678a15691bfSXin LI 		fprintf(fout, "%s\n", HISTFILE_FIRST_LINE);
1679a15691bfSXin LI 		ctx.fout = fout;
1680a15691bfSXin LI 		ctx.mlist = NULL;
1681b7780dbeSXin LI 		read_cmdhist(&copy_hist, &ctx, skip_search, skip_shell);
1682b7780dbeSXin LI 		save_marks(fout, HISTFILE_MARK_SECTION);
1683a15691bfSXin LI 		fclose(fout);
1684a15691bfSXin LI #if MSDOS_COMPILER==WIN32C
1685a15691bfSXin LI 		/*
1686a15691bfSXin LI 		 * Windows rename doesn't remove an existing file,
1687a15691bfSXin LI 		 * making it useless for atomic operations. Sigh.
1688a15691bfSXin LI 		 */
1689a15691bfSXin LI 		remove(histname);
1690a15691bfSXin LI #endif
1691a15691bfSXin LI 		rename(tempname, histname);
1692a15691bfSXin LI 	}
1693a15691bfSXin LI 	free(tempname);
1694a15691bfSXin LI 	free(histname);
16956dcb072bSXin LI #endif /* CMD_HISTORY */
16966dcb072bSXin LI }
1697