xref: /dflybsd-src/contrib/less/cmdbuf.c (revision e0f238eda64c20d98364903e0c003825fd0b66dd)
11133e27eSPeter Avalos /*
2*e433da38SAaron LI  * Copyright (C) 1984-2024  Mark Nudelman
31133e27eSPeter Avalos  *
41133e27eSPeter Avalos  * You may distribute under the terms of either the GNU General Public
51133e27eSPeter Avalos  * License or the Less License, as specified in the README file.
61133e27eSPeter Avalos  *
7e639dc31SJohn Marino  * For more information, see the README file.
81133e27eSPeter Avalos  */
91133e27eSPeter Avalos 
101133e27eSPeter Avalos 
111133e27eSPeter Avalos /*
121133e27eSPeter Avalos  * Functions which manipulate the command buffer.
131133e27eSPeter Avalos  * Used only by command() and related functions.
141133e27eSPeter Avalos  */
151133e27eSPeter Avalos 
161133e27eSPeter Avalos #include "less.h"
171133e27eSPeter Avalos #include "cmd.h"
181133e27eSPeter Avalos #include "charset.h"
191133e27eSPeter Avalos #if HAVE_STAT
201133e27eSPeter Avalos #include <sys/stat.h>
211133e27eSPeter Avalos #endif
221133e27eSPeter Avalos 
231133e27eSPeter Avalos extern int sc_width;
241133e27eSPeter Avalos extern int utf_mode;
2502d62a0fSDaniel Fojt extern int no_hist_dups;
2602d62a0fSDaniel Fojt extern int marks_modified;
271133e27eSPeter Avalos 
281133e27eSPeter Avalos static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
291133e27eSPeter Avalos static int cmd_col;              /* Current column of the cursor */
301133e27eSPeter Avalos static int prompt_col;           /* Column of cursor just after prompt */
311133e27eSPeter Avalos static char *cp;                 /* Pointer into cmdbuf */
321133e27eSPeter Avalos static int cmd_offset;           /* Index into cmdbuf of first displayed char */
33*e433da38SAaron LI static lbool literal;            /* Next input char should not be interpreted */
34*e433da38SAaron LI static size_t updown_match;      /* Prefix length in up/down movement */
35*e433da38SAaron LI static lbool have_updown_match = FALSE;
361133e27eSPeter Avalos 
371133e27eSPeter Avalos #if TAB_COMPLETE_FILENAME
38320d7c8aSAaron LI static int cmd_complete(int action);
391133e27eSPeter Avalos /*
401133e27eSPeter Avalos  * These variables are statics used by cmd_complete.
411133e27eSPeter Avalos  */
42*e433da38SAaron LI static lbool in_completion = FALSE;
431133e27eSPeter Avalos static char *tk_text;
441133e27eSPeter Avalos static char *tk_original;
45*e433da38SAaron LI static constant char *tk_ipoint;
46*e433da38SAaron LI static constant char *tk_trial = NULL;
471133e27eSPeter Avalos static struct textlist tk_tlist;
481133e27eSPeter Avalos #endif
491133e27eSPeter Avalos 
501133e27eSPeter Avalos static int cmd_left();
511133e27eSPeter Avalos static int cmd_right();
521133e27eSPeter Avalos 
531133e27eSPeter Avalos #if SPACES_IN_FILENAMES
541133e27eSPeter Avalos public char openquote = '"';
551133e27eSPeter Avalos public char closequote = '"';
561133e27eSPeter Avalos #endif
571133e27eSPeter Avalos 
581133e27eSPeter Avalos #if CMD_HISTORY
591133e27eSPeter Avalos 
601133e27eSPeter Avalos /* History file */
611133e27eSPeter Avalos #define HISTFILE_FIRST_LINE      ".less-history-file:"
621133e27eSPeter Avalos #define HISTFILE_SEARCH_SECTION  ".search"
631133e27eSPeter Avalos #define HISTFILE_SHELL_SECTION   ".shell"
6402d62a0fSDaniel Fojt #define HISTFILE_MARK_SECTION    ".mark"
651133e27eSPeter Avalos 
661133e27eSPeter Avalos /*
671133e27eSPeter Avalos  * A mlist structure represents a command history.
681133e27eSPeter Avalos  */
691133e27eSPeter Avalos struct mlist
701133e27eSPeter Avalos {
711133e27eSPeter Avalos 	struct mlist *next;
721133e27eSPeter Avalos 	struct mlist *prev;
731133e27eSPeter Avalos 	struct mlist *curr_mp;
741133e27eSPeter Avalos 	char *string;
75*e433da38SAaron LI 	lbool modified;
761133e27eSPeter Avalos };
771133e27eSPeter Avalos 
781133e27eSPeter Avalos /*
791133e27eSPeter Avalos  * These are the various command histories that exist.
801133e27eSPeter Avalos  */
811133e27eSPeter Avalos struct mlist mlist_search =
821133e27eSPeter Avalos 	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
8302d62a0fSDaniel Fojt public void *ml_search = (void *) &mlist_search;
841133e27eSPeter Avalos 
851133e27eSPeter Avalos struct mlist mlist_examine =
861133e27eSPeter Avalos 	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
8702d62a0fSDaniel Fojt public void *ml_examine = (void *) &mlist_examine;
881133e27eSPeter Avalos 
891133e27eSPeter Avalos #if SHELL_ESCAPE || PIPEC
901133e27eSPeter Avalos struct mlist mlist_shell =
911133e27eSPeter Avalos 	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
9202d62a0fSDaniel Fojt public void *ml_shell = (void *) &mlist_shell;
931133e27eSPeter Avalos #endif
941133e27eSPeter Avalos 
951133e27eSPeter Avalos #else /* CMD_HISTORY */
961133e27eSPeter Avalos 
971133e27eSPeter Avalos /* If CMD_HISTORY is off, these are just flags. */
9802d62a0fSDaniel Fojt public void *ml_search = (void *)1;
9902d62a0fSDaniel Fojt public void *ml_examine = (void *)2;
1001133e27eSPeter Avalos #if SHELL_ESCAPE || PIPEC
10102d62a0fSDaniel Fojt public void *ml_shell = (void *)3;
1021133e27eSPeter Avalos #endif
1031133e27eSPeter Avalos 
1041133e27eSPeter Avalos #endif /* CMD_HISTORY */
1051133e27eSPeter Avalos 
1061133e27eSPeter Avalos /*
1071133e27eSPeter Avalos  * History for the current command.
1081133e27eSPeter Avalos  */
1091133e27eSPeter Avalos static struct mlist *curr_mlist = NULL;
1101133e27eSPeter Avalos static int curr_cmdflags;
1111133e27eSPeter Avalos 
1121133e27eSPeter Avalos static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
1131133e27eSPeter Avalos static int cmd_mbc_buf_len;
1141133e27eSPeter Avalos static int cmd_mbc_buf_index;
1151133e27eSPeter Avalos 
1161133e27eSPeter Avalos 
1171133e27eSPeter Avalos /*
1181133e27eSPeter Avalos  * Reset command buffer (to empty).
1191133e27eSPeter Avalos  */
cmd_reset(void)120320d7c8aSAaron LI public void cmd_reset(void)
1211133e27eSPeter Avalos {
1221133e27eSPeter Avalos 	cp = cmdbuf;
1231133e27eSPeter Avalos 	*cp = '\0';
1241133e27eSPeter Avalos 	cmd_col = 0;
1251133e27eSPeter Avalos 	cmd_offset = 0;
126*e433da38SAaron LI 	literal = FALSE;
1271133e27eSPeter Avalos 	cmd_mbc_buf_len = 0;
128*e433da38SAaron LI 	have_updown_match = FALSE;
1291133e27eSPeter Avalos }
1301133e27eSPeter Avalos 
1311133e27eSPeter Avalos /*
1321133e27eSPeter Avalos  * Clear command line.
1331133e27eSPeter Avalos  */
clear_cmd(void)134320d7c8aSAaron LI public void clear_cmd(void)
1351133e27eSPeter Avalos {
1361133e27eSPeter Avalos 	cmd_col = prompt_col = 0;
1371133e27eSPeter Avalos 	cmd_mbc_buf_len = 0;
138*e433da38SAaron LI 	have_updown_match = FALSE;
1391133e27eSPeter Avalos }
1401133e27eSPeter Avalos 
1411133e27eSPeter Avalos /*
1421133e27eSPeter Avalos  * Display a string, usually as a prompt for input into the command buffer.
1431133e27eSPeter Avalos  */
cmd_putstr(constant char * s)144320d7c8aSAaron LI public void cmd_putstr(constant char *s)
1451133e27eSPeter Avalos {
1461133e27eSPeter Avalos 	LWCHAR prev_ch = 0;
1471133e27eSPeter Avalos 	LWCHAR ch;
14802d62a0fSDaniel Fojt 	constant char *endline = s + strlen(s);
1491133e27eSPeter Avalos 	while (*s != '\0')
1501133e27eSPeter Avalos 	{
151*e433da38SAaron LI 		constant char *os = s;
15202d62a0fSDaniel Fojt 		int width;
153*e433da38SAaron LI 		ch = step_charc(&s, +1, endline);
154*e433da38SAaron LI 		while (os < s)
155*e433da38SAaron LI 			putchr(*os++);
1561133e27eSPeter Avalos 		if (!utf_mode)
15702d62a0fSDaniel Fojt 			width = 1;
15802d62a0fSDaniel Fojt 		else if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
15902d62a0fSDaniel Fojt 			width = 0;
16002d62a0fSDaniel Fojt 		else
16102d62a0fSDaniel Fojt 			width = is_wide_char(ch) ? 2 : 1;
1621133e27eSPeter Avalos 		cmd_col += width;
1631133e27eSPeter Avalos 		prompt_col += width;
1641133e27eSPeter Avalos 		prev_ch = ch;
1651133e27eSPeter Avalos 	}
1661133e27eSPeter Avalos }
1671133e27eSPeter Avalos 
1681133e27eSPeter Avalos /*
1691133e27eSPeter Avalos  * How many characters are in the command buffer?
1701133e27eSPeter Avalos  */
len_cmdbuf(void)171320d7c8aSAaron LI public int len_cmdbuf(void)
1721133e27eSPeter Avalos {
173*e433da38SAaron LI 	constant char *s = cmdbuf;
174*e433da38SAaron LI 	constant char *endline = s + strlen(s);
1751133e27eSPeter Avalos 	int len = 0;
1761133e27eSPeter Avalos 
1771133e27eSPeter Avalos 	while (*s != '\0')
1781133e27eSPeter Avalos 	{
179*e433da38SAaron LI 		step_charc(&s, +1, endline);
1801133e27eSPeter Avalos 		len++;
1811133e27eSPeter Avalos 	}
1821133e27eSPeter Avalos 	return (len);
1831133e27eSPeter Avalos }
1841133e27eSPeter Avalos 
1851133e27eSPeter Avalos /*
1861133e27eSPeter Avalos  * Common part of cmd_step_right() and cmd_step_left().
18702d62a0fSDaniel Fojt  * {{ Returning pwidth and bswidth separately is a historical artifact
18802d62a0fSDaniel Fojt  *    since they're always the same. Maybe clean this up someday. }}
1891133e27eSPeter Avalos  */
cmd_step_common(char * p,LWCHAR ch,size_t len,int * pwidth,int * bswidth)190*e433da38SAaron LI static constant char * cmd_step_common(char *p, LWCHAR ch, size_t len, int *pwidth, int *bswidth)
1911133e27eSPeter Avalos {
192*e433da38SAaron LI 	constant char *pr;
19302d62a0fSDaniel Fojt 	int width;
1941133e27eSPeter Avalos 
1951133e27eSPeter Avalos 	if (len == 1)
1961133e27eSPeter Avalos 	{
197*e433da38SAaron LI 		pr = prchar(ch);
19802d62a0fSDaniel Fojt 		width = (int) strlen(pr);
1991133e27eSPeter Avalos 	} else
2001133e27eSPeter Avalos 	{
2011133e27eSPeter Avalos 		pr = prutfchar(ch);
2021133e27eSPeter Avalos 		if (is_composing_char(ch))
20302d62a0fSDaniel Fojt 			width = 0;
20402d62a0fSDaniel Fojt 		else if (is_ubin_char(ch))
20502d62a0fSDaniel Fojt 			width = (int) strlen(pr);
20602d62a0fSDaniel Fojt 		else
2071133e27eSPeter Avalos 		{
2081133e27eSPeter Avalos 			LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
2091133e27eSPeter Avalos 			if (is_combining_char(prev_ch, ch))
21002d62a0fSDaniel Fojt 				width = 0;
21102d62a0fSDaniel Fojt 			else
21202d62a0fSDaniel Fojt 				width = is_wide_char(ch) ? 2 : 1;
21302d62a0fSDaniel Fojt 		}
21402d62a0fSDaniel Fojt 	}
2151133e27eSPeter Avalos 	if (pwidth != NULL)
21602d62a0fSDaniel Fojt 		*pwidth = width;
2171133e27eSPeter Avalos 	if (bswidth != NULL)
21802d62a0fSDaniel Fojt 		*bswidth = width;
2191133e27eSPeter Avalos 	return (pr);
2201133e27eSPeter Avalos }
2211133e27eSPeter Avalos 
2221133e27eSPeter Avalos /*
2231133e27eSPeter Avalos  * Step a pointer one character right in the command buffer.
2241133e27eSPeter Avalos  */
cmd_step_right(char ** pp,int * pwidth,int * bswidth)225*e433da38SAaron LI static constant char * cmd_step_right(char **pp, int *pwidth, int *bswidth)
2261133e27eSPeter Avalos {
2271133e27eSPeter Avalos 	char *p = *pp;
2281133e27eSPeter Avalos 	LWCHAR ch = step_char(pp, +1, p + strlen(p));
2291133e27eSPeter Avalos 
230*e433da38SAaron LI 	return cmd_step_common(p, ch, ptr_diff(*pp, p), pwidth, bswidth);
2311133e27eSPeter Avalos }
2321133e27eSPeter Avalos 
2331133e27eSPeter Avalos /*
2341133e27eSPeter Avalos  * Step a pointer one character left in the command buffer.
2351133e27eSPeter Avalos  */
cmd_step_left(char ** pp,int * pwidth,int * bswidth)236*e433da38SAaron LI static constant char * cmd_step_left(char **pp, int *pwidth, int *bswidth)
2371133e27eSPeter Avalos {
2381133e27eSPeter Avalos 	char *p = *pp;
2391133e27eSPeter Avalos 	LWCHAR ch = step_char(pp, -1, cmdbuf);
2401133e27eSPeter Avalos 
241*e433da38SAaron LI 	return cmd_step_common(*pp, ch, ptr_diff(p, *pp), pwidth, bswidth);
2421133e27eSPeter Avalos }
2431133e27eSPeter Avalos 
2441133e27eSPeter Avalos /*
2450c7ad07eSAntonio Huete Jimenez  * Put the cursor at "home" (just after the prompt),
2460c7ad07eSAntonio Huete Jimenez  * and set cp to the corresponding char in cmdbuf.
2470c7ad07eSAntonio Huete Jimenez  */
cmd_home(void)248320d7c8aSAaron LI static void cmd_home(void)
2490c7ad07eSAntonio Huete Jimenez {
2500c7ad07eSAntonio Huete Jimenez 	while (cmd_col > prompt_col)
2510c7ad07eSAntonio Huete Jimenez 	{
2520c7ad07eSAntonio Huete Jimenez 		int width, bswidth;
2530c7ad07eSAntonio Huete Jimenez 
2540c7ad07eSAntonio Huete Jimenez 		cmd_step_left(&cp, &width, &bswidth);
2550c7ad07eSAntonio Huete Jimenez 		while (bswidth-- > 0)
2560c7ad07eSAntonio Huete Jimenez 			putbs();
2570c7ad07eSAntonio Huete Jimenez 		cmd_col -= width;
2580c7ad07eSAntonio Huete Jimenez 	}
2590c7ad07eSAntonio Huete Jimenez 
2600c7ad07eSAntonio Huete Jimenez 	cp = &cmdbuf[cmd_offset];
2610c7ad07eSAntonio Huete Jimenez }
2620c7ad07eSAntonio Huete Jimenez 
2630c7ad07eSAntonio Huete Jimenez /*
2641133e27eSPeter Avalos  * Repaint the line from cp onwards.
2651133e27eSPeter Avalos  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
2661133e27eSPeter Avalos  */
cmd_repaint(constant char * old_cp)267320d7c8aSAaron LI public void cmd_repaint(constant char *old_cp)
2681133e27eSPeter Avalos {
2691133e27eSPeter Avalos 	/*
2701133e27eSPeter Avalos 	 * Repaint the line from the current position.
2711133e27eSPeter Avalos 	 */
2720c7ad07eSAntonio Huete Jimenez 	if (old_cp == NULL)
2730c7ad07eSAntonio Huete Jimenez 	{
2740c7ad07eSAntonio Huete Jimenez 		old_cp = cp;
2750c7ad07eSAntonio Huete Jimenez 		cmd_home();
2760c7ad07eSAntonio Huete Jimenez 	}
2771133e27eSPeter Avalos 	clear_eol();
2781133e27eSPeter Avalos 	while (*cp != '\0')
2791133e27eSPeter Avalos 	{
2801133e27eSPeter Avalos 		char *np = cp;
2811133e27eSPeter Avalos 		int width;
282*e433da38SAaron LI 		constant char *pr = cmd_step_right(&np, &width, NULL);
2831133e27eSPeter Avalos 		if (cmd_col + width >= sc_width)
2841133e27eSPeter Avalos 			break;
2851133e27eSPeter Avalos 		cp = np;
2861133e27eSPeter Avalos 		putstr(pr);
2871133e27eSPeter Avalos 		cmd_col += width;
2881133e27eSPeter Avalos 	}
2891133e27eSPeter Avalos 	while (*cp != '\0')
2901133e27eSPeter Avalos 	{
2911133e27eSPeter Avalos 		char *np = cp;
2921133e27eSPeter Avalos 		int width;
293*e433da38SAaron LI 		constant char *pr = cmd_step_right(&np, &width, NULL);
2941133e27eSPeter Avalos 		if (width > 0)
2951133e27eSPeter Avalos 			break;
2961133e27eSPeter Avalos 		cp = np;
2971133e27eSPeter Avalos 		putstr(pr);
2981133e27eSPeter Avalos 	}
2991133e27eSPeter Avalos 
3001133e27eSPeter Avalos 	/*
3011133e27eSPeter Avalos 	 * Back up the cursor to the correct position.
3021133e27eSPeter Avalos 	 */
3031133e27eSPeter Avalos 	while (cp > old_cp)
3041133e27eSPeter Avalos 		cmd_left();
3051133e27eSPeter Avalos }
3061133e27eSPeter Avalos 
3071133e27eSPeter Avalos /*
3081133e27eSPeter Avalos  * Shift the cmdbuf display left a half-screen.
3091133e27eSPeter Avalos  */
cmd_lshift(void)310320d7c8aSAaron LI static void cmd_lshift(void)
3111133e27eSPeter Avalos {
3121133e27eSPeter Avalos 	char *s;
3131133e27eSPeter Avalos 	char *save_cp;
3141133e27eSPeter Avalos 	int cols;
3151133e27eSPeter Avalos 
3161133e27eSPeter Avalos 	/*
3171133e27eSPeter Avalos 	 * Start at the first displayed char, count how far to the
3181133e27eSPeter Avalos 	 * right we'd have to move to reach the center of the screen.
3191133e27eSPeter Avalos 	 */
3201133e27eSPeter Avalos 	s = cmdbuf + cmd_offset;
3211133e27eSPeter Avalos 	cols = 0;
3221133e27eSPeter Avalos 	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
3231133e27eSPeter Avalos 	{
3241133e27eSPeter Avalos 		int width;
3251133e27eSPeter Avalos 		cmd_step_right(&s, &width, NULL);
3261133e27eSPeter Avalos 		cols += width;
3271133e27eSPeter Avalos 	}
3281133e27eSPeter Avalos 	while (*s != '\0')
3291133e27eSPeter Avalos 	{
3301133e27eSPeter Avalos 		int width;
3311133e27eSPeter Avalos 		char *ns = s;
3321133e27eSPeter Avalos 		cmd_step_right(&ns, &width, NULL);
3331133e27eSPeter Avalos 		if (width > 0)
3341133e27eSPeter Avalos 			break;
3351133e27eSPeter Avalos 		s = ns;
3361133e27eSPeter Avalos 	}
3371133e27eSPeter Avalos 
338fa0be7c5SJohn Marino 	cmd_offset = (int) (s - cmdbuf);
3391133e27eSPeter Avalos 	save_cp = cp;
3401133e27eSPeter Avalos 	cmd_home();
3411133e27eSPeter Avalos 	cmd_repaint(save_cp);
3421133e27eSPeter Avalos }
3431133e27eSPeter Avalos 
3441133e27eSPeter Avalos /*
3451133e27eSPeter Avalos  * Shift the cmdbuf display right a half-screen.
3461133e27eSPeter Avalos  */
cmd_rshift(void)347320d7c8aSAaron LI static void cmd_rshift(void)
3481133e27eSPeter Avalos {
3491133e27eSPeter Avalos 	char *s;
3501133e27eSPeter Avalos 	char *save_cp;
3511133e27eSPeter Avalos 	int cols;
3521133e27eSPeter Avalos 
3531133e27eSPeter Avalos 	/*
3541133e27eSPeter Avalos 	 * Start at the first displayed char, count how far to the
3551133e27eSPeter Avalos 	 * left we'd have to move to traverse a half-screen width
3561133e27eSPeter Avalos 	 * of displayed characters.
3571133e27eSPeter Avalos 	 */
3581133e27eSPeter Avalos 	s = cmdbuf + cmd_offset;
3591133e27eSPeter Avalos 	cols = 0;
3601133e27eSPeter Avalos 	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
3611133e27eSPeter Avalos 	{
3621133e27eSPeter Avalos 		int width;
3631133e27eSPeter Avalos 		cmd_step_left(&s, &width, NULL);
3641133e27eSPeter Avalos 		cols += width;
3651133e27eSPeter Avalos 	}
3661133e27eSPeter Avalos 
367fa0be7c5SJohn Marino 	cmd_offset = (int) (s - cmdbuf);
3681133e27eSPeter Avalos 	save_cp = cp;
3691133e27eSPeter Avalos 	cmd_home();
3701133e27eSPeter Avalos 	cmd_repaint(save_cp);
3711133e27eSPeter Avalos }
3721133e27eSPeter Avalos 
3731133e27eSPeter Avalos /*
3741133e27eSPeter Avalos  * Move cursor right one character.
3751133e27eSPeter Avalos  */
cmd_right(void)376320d7c8aSAaron LI static int cmd_right(void)
3771133e27eSPeter Avalos {
378*e433da38SAaron LI 	constant char *pr;
3791133e27eSPeter Avalos 	char *ncp;
3801133e27eSPeter Avalos 	int width;
3811133e27eSPeter Avalos 
3821133e27eSPeter Avalos 	if (*cp == '\0')
3831133e27eSPeter Avalos 	{
3841133e27eSPeter Avalos 		/* Already at the end of the line. */
3851133e27eSPeter Avalos 		return (CC_OK);
3861133e27eSPeter Avalos 	}
3871133e27eSPeter Avalos 	ncp = cp;
3881133e27eSPeter Avalos 	pr = cmd_step_right(&ncp, &width, NULL);
3891133e27eSPeter Avalos 	if (cmd_col + width >= sc_width)
3901133e27eSPeter Avalos 		cmd_lshift();
3911133e27eSPeter Avalos 	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
3921133e27eSPeter Avalos 		cmd_lshift();
3931133e27eSPeter Avalos 	cp = ncp;
3941133e27eSPeter Avalos 	cmd_col += width;
3951133e27eSPeter Avalos 	putstr(pr);
3961133e27eSPeter Avalos 	while (*cp != '\0')
3971133e27eSPeter Avalos 	{
3981133e27eSPeter Avalos 		pr = cmd_step_right(&ncp, &width, NULL);
3991133e27eSPeter Avalos 		if (width > 0)
4001133e27eSPeter Avalos 			break;
4011133e27eSPeter Avalos 		putstr(pr);
4021133e27eSPeter Avalos 		cp = ncp;
4031133e27eSPeter Avalos 	}
4041133e27eSPeter Avalos 	return (CC_OK);
4051133e27eSPeter Avalos }
4061133e27eSPeter Avalos 
4071133e27eSPeter Avalos /*
4081133e27eSPeter Avalos  * Move cursor left one character.
4091133e27eSPeter Avalos  */
cmd_left(void)410320d7c8aSAaron LI static int cmd_left(void)
4111133e27eSPeter Avalos {
4121133e27eSPeter Avalos 	char *ncp;
41302d62a0fSDaniel Fojt 	int width = 0;
41402d62a0fSDaniel Fojt 	int bswidth = 0;
4151133e27eSPeter Avalos 
4161133e27eSPeter Avalos 	if (cp <= cmdbuf)
4171133e27eSPeter Avalos 	{
4181133e27eSPeter Avalos 		/* Already at the beginning of the line */
4191133e27eSPeter Avalos 		return (CC_OK);
4201133e27eSPeter Avalos 	}
4211133e27eSPeter Avalos 	ncp = cp;
4221133e27eSPeter Avalos 	while (ncp > cmdbuf)
4231133e27eSPeter Avalos 	{
4241133e27eSPeter Avalos 		cmd_step_left(&ncp, &width, &bswidth);
4251133e27eSPeter Avalos 		if (width > 0)
4261133e27eSPeter Avalos 			break;
4271133e27eSPeter Avalos 	}
4281133e27eSPeter Avalos 	if (cmd_col < prompt_col + width)
4291133e27eSPeter Avalos 		cmd_rshift();
4301133e27eSPeter Avalos 	cp = ncp;
4311133e27eSPeter Avalos 	cmd_col -= width;
4321133e27eSPeter Avalos 	while (bswidth-- > 0)
4331133e27eSPeter Avalos 		putbs();
4341133e27eSPeter Avalos 	return (CC_OK);
4351133e27eSPeter Avalos }
4361133e27eSPeter Avalos 
4371133e27eSPeter Avalos /*
4381133e27eSPeter Avalos  * Insert a char into the command buffer, at the current position.
4391133e27eSPeter Avalos  */
cmd_ichar(constant char * cs,size_t clen)440*e433da38SAaron LI static int cmd_ichar(constant char *cs, size_t clen)
4411133e27eSPeter Avalos {
4421133e27eSPeter Avalos 	char *s;
4431133e27eSPeter Avalos 
4441133e27eSPeter Avalos 	if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
4451133e27eSPeter Avalos 	{
4461133e27eSPeter Avalos 		/* No room in the command buffer for another char. */
4471133e27eSPeter Avalos 		bell();
4481133e27eSPeter Avalos 		return (CC_ERROR);
4491133e27eSPeter Avalos 	}
4501133e27eSPeter Avalos 
4511133e27eSPeter Avalos 	/*
4521133e27eSPeter Avalos 	 * Make room for the new character (shift the tail of the buffer right).
4531133e27eSPeter Avalos 	 */
4541133e27eSPeter Avalos 	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
4551133e27eSPeter Avalos 		s[clen] = s[0];
4561133e27eSPeter Avalos 	/*
4571133e27eSPeter Avalos 	 * Insert the character into the buffer.
4581133e27eSPeter Avalos 	 */
4591133e27eSPeter Avalos 	for (s = cp;  s < cp + clen;  s++)
4601133e27eSPeter Avalos 		*s = *cs++;
4611133e27eSPeter Avalos 	/*
4621133e27eSPeter Avalos 	 * Reprint the tail of the line from the inserted char.
4631133e27eSPeter Avalos 	 */
464*e433da38SAaron LI 	have_updown_match = FALSE;
4651133e27eSPeter Avalos 	cmd_repaint(cp);
4661133e27eSPeter Avalos 	cmd_right();
4671133e27eSPeter Avalos 	return (CC_OK);
4681133e27eSPeter Avalos }
4691133e27eSPeter Avalos 
4701133e27eSPeter Avalos /*
4711133e27eSPeter Avalos  * Backspace in the command buffer.
4721133e27eSPeter Avalos  * Delete the char to the left of the cursor.
4731133e27eSPeter Avalos  */
cmd_erase(void)474320d7c8aSAaron LI static int cmd_erase(void)
4751133e27eSPeter Avalos {
47602d62a0fSDaniel Fojt 	char *s;
4771133e27eSPeter Avalos 	int clen;
4781133e27eSPeter Avalos 
4791133e27eSPeter Avalos 	if (cp == cmdbuf)
4801133e27eSPeter Avalos 	{
4811133e27eSPeter Avalos 		/*
4821133e27eSPeter Avalos 		 * Backspace past beginning of the buffer:
4831133e27eSPeter Avalos 		 * this usually means abort the command.
4841133e27eSPeter Avalos 		 */
4851133e27eSPeter Avalos 		return (CC_QUIT);
4861133e27eSPeter Avalos 	}
4871133e27eSPeter Avalos 	/*
4881133e27eSPeter Avalos 	 * Move cursor left (to the char being erased).
4891133e27eSPeter Avalos 	 */
4901133e27eSPeter Avalos 	s = cp;
4911133e27eSPeter Avalos 	cmd_left();
492fa0be7c5SJohn Marino 	clen = (int) (s - cp);
4931133e27eSPeter Avalos 
4941133e27eSPeter Avalos 	/*
4951133e27eSPeter Avalos 	 * Remove the char from the buffer (shift the buffer left).
4961133e27eSPeter Avalos 	 */
4971133e27eSPeter Avalos 	for (s = cp;  ;  s++)
4981133e27eSPeter Avalos 	{
4991133e27eSPeter Avalos 		s[0] = s[clen];
5001133e27eSPeter Avalos 		if (s[0] == '\0')
5011133e27eSPeter Avalos 			break;
5021133e27eSPeter Avalos 	}
5031133e27eSPeter Avalos 
5041133e27eSPeter Avalos 	/*
5051133e27eSPeter Avalos 	 * Repaint the buffer after the erased char.
5061133e27eSPeter Avalos 	 */
507*e433da38SAaron LI 	have_updown_match = FALSE;
5081133e27eSPeter Avalos 	cmd_repaint(cp);
5091133e27eSPeter Avalos 
5101133e27eSPeter Avalos 	/*
5111133e27eSPeter Avalos 	 * We say that erasing the entire command string causes us
5121133e27eSPeter Avalos 	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
5131133e27eSPeter Avalos 	 */
5141133e27eSPeter Avalos 	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
5151133e27eSPeter Avalos 		return (CC_QUIT);
5161133e27eSPeter Avalos 	return (CC_OK);
5171133e27eSPeter Avalos }
5181133e27eSPeter Avalos 
5191133e27eSPeter Avalos /*
5201133e27eSPeter Avalos  * Delete the char under the cursor.
5211133e27eSPeter Avalos  */
cmd_delete(void)522320d7c8aSAaron LI static int cmd_delete(void)
5231133e27eSPeter Avalos {
5241133e27eSPeter Avalos 	if (*cp == '\0')
5251133e27eSPeter Avalos 	{
5261133e27eSPeter Avalos 		/* At end of string; there is no char under the cursor. */
5271133e27eSPeter Avalos 		return (CC_OK);
5281133e27eSPeter Avalos 	}
5291133e27eSPeter Avalos 	/*
5301133e27eSPeter Avalos 	 * Move right, then use cmd_erase.
5311133e27eSPeter Avalos 	 */
5321133e27eSPeter Avalos 	cmd_right();
5331133e27eSPeter Avalos 	cmd_erase();
5341133e27eSPeter Avalos 	return (CC_OK);
5351133e27eSPeter Avalos }
5361133e27eSPeter Avalos 
5371133e27eSPeter Avalos /*
5381133e27eSPeter Avalos  * Delete the "word" to the left of the cursor.
5391133e27eSPeter Avalos  */
cmd_werase(void)540320d7c8aSAaron LI static int cmd_werase(void)
5411133e27eSPeter Avalos {
5421133e27eSPeter Avalos 	if (cp > cmdbuf && cp[-1] == ' ')
5431133e27eSPeter Avalos 	{
5441133e27eSPeter Avalos 		/*
5451133e27eSPeter Avalos 		 * If the char left of cursor is a space,
5461133e27eSPeter Avalos 		 * erase all the spaces left of cursor (to the first non-space).
5471133e27eSPeter Avalos 		 */
5481133e27eSPeter Avalos 		while (cp > cmdbuf && cp[-1] == ' ')
5491133e27eSPeter Avalos 			(void) cmd_erase();
5501133e27eSPeter Avalos 	} else
5511133e27eSPeter Avalos 	{
5521133e27eSPeter Avalos 		/*
5531133e27eSPeter Avalos 		 * If the char left of cursor is not a space,
5541133e27eSPeter Avalos 		 * erase all the nonspaces left of cursor (the whole "word").
5551133e27eSPeter Avalos 		 */
5561133e27eSPeter Avalos 		while (cp > cmdbuf && cp[-1] != ' ')
5571133e27eSPeter Avalos 			(void) cmd_erase();
5581133e27eSPeter Avalos 	}
5591133e27eSPeter Avalos 	return (CC_OK);
5601133e27eSPeter Avalos }
5611133e27eSPeter Avalos 
5621133e27eSPeter Avalos /*
5631133e27eSPeter Avalos  * Delete the "word" under the cursor.
5641133e27eSPeter Avalos  */
cmd_wdelete(void)565320d7c8aSAaron LI static int cmd_wdelete(void)
5661133e27eSPeter Avalos {
5671133e27eSPeter Avalos 	if (*cp == ' ')
5681133e27eSPeter Avalos 	{
5691133e27eSPeter Avalos 		/*
5701133e27eSPeter Avalos 		 * If the char under the cursor is a space,
5711133e27eSPeter Avalos 		 * delete it and all the spaces right of cursor.
5721133e27eSPeter Avalos 		 */
5731133e27eSPeter Avalos 		while (*cp == ' ')
5741133e27eSPeter Avalos 			(void) cmd_delete();
5751133e27eSPeter Avalos 	} else
5761133e27eSPeter Avalos 	{
5771133e27eSPeter Avalos 		/*
5781133e27eSPeter Avalos 		 * If the char under the cursor is not a space,
5791133e27eSPeter Avalos 		 * delete it and all nonspaces right of cursor (the whole word).
5801133e27eSPeter Avalos 		 */
5811133e27eSPeter Avalos 		while (*cp != ' ' && *cp != '\0')
5821133e27eSPeter Avalos 			(void) cmd_delete();
5831133e27eSPeter Avalos 	}
5841133e27eSPeter Avalos 	return (CC_OK);
5851133e27eSPeter Avalos }
5861133e27eSPeter Avalos 
5871133e27eSPeter Avalos /*
5881133e27eSPeter Avalos  * Delete all chars in the command buffer.
5891133e27eSPeter Avalos  */
cmd_kill(void)590320d7c8aSAaron LI static int cmd_kill(void)
5911133e27eSPeter Avalos {
5921133e27eSPeter Avalos 	if (cmdbuf[0] == '\0')
5931133e27eSPeter Avalos 	{
5941133e27eSPeter Avalos 		/* Buffer is already empty; abort the current command. */
5951133e27eSPeter Avalos 		return (CC_QUIT);
5961133e27eSPeter Avalos 	}
5971133e27eSPeter Avalos 	cmd_offset = 0;
5981133e27eSPeter Avalos 	cmd_home();
5991133e27eSPeter Avalos 	*cp = '\0';
600*e433da38SAaron LI 	have_updown_match = FALSE;
6011133e27eSPeter Avalos 	cmd_repaint(cp);
6021133e27eSPeter Avalos 
6031133e27eSPeter Avalos 	/*
6041133e27eSPeter Avalos 	 * We say that erasing the entire command string causes us
6051133e27eSPeter Avalos 	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
6061133e27eSPeter Avalos 	 */
6071133e27eSPeter Avalos 	if (curr_cmdflags & CF_QUIT_ON_ERASE)
6081133e27eSPeter Avalos 		return (CC_QUIT);
6091133e27eSPeter Avalos 	return (CC_OK);
6101133e27eSPeter Avalos }
6111133e27eSPeter Avalos 
6121133e27eSPeter Avalos /*
6131133e27eSPeter Avalos  * Select an mlist structure to be the current command history.
6141133e27eSPeter Avalos  */
set_mlist(void * mlist,int cmdflags)615320d7c8aSAaron LI public void set_mlist(void *mlist, int cmdflags)
6161133e27eSPeter Avalos {
6178be36e5bSPeter Avalos #if CMD_HISTORY
6181133e27eSPeter Avalos 	curr_mlist = (struct mlist *) mlist;
6191133e27eSPeter Avalos 	curr_cmdflags = cmdflags;
6201133e27eSPeter Avalos 
6211133e27eSPeter Avalos 	/* Make sure the next up-arrow moves to the last string in the mlist. */
6221133e27eSPeter Avalos 	if (curr_mlist != NULL)
6231133e27eSPeter Avalos 		curr_mlist->curr_mp = curr_mlist;
6248be36e5bSPeter Avalos #endif
6251133e27eSPeter Avalos }
6261133e27eSPeter Avalos 
6271133e27eSPeter Avalos #if CMD_HISTORY
6281133e27eSPeter Avalos /*
6291133e27eSPeter Avalos  * Move up or down in the currently selected command history list.
630e639dc31SJohn Marino  * Only consider entries whose first updown_match chars are equal to
631e639dc31SJohn Marino  * cmdbuf's corresponding chars.
6321133e27eSPeter Avalos  */
cmd_updown(int action)633320d7c8aSAaron LI static int cmd_updown(int action)
6341133e27eSPeter Avalos {
63502d62a0fSDaniel Fojt 	constant char *s;
636e639dc31SJohn Marino 	struct mlist *ml;
6371133e27eSPeter Avalos 
6381133e27eSPeter Avalos 	if (curr_mlist == NULL)
6391133e27eSPeter Avalos 	{
6401133e27eSPeter Avalos 		/*
6411133e27eSPeter Avalos 		 * The current command has no history list.
6421133e27eSPeter Avalos 		 */
6431133e27eSPeter Avalos 		bell();
6441133e27eSPeter Avalos 		return (CC_OK);
6451133e27eSPeter Avalos 	}
646e639dc31SJohn Marino 
647*e433da38SAaron LI 	if (!have_updown_match)
648e639dc31SJohn Marino 	{
649*e433da38SAaron LI 		updown_match = ptr_diff(cp, cmdbuf);
650*e433da38SAaron LI 		have_updown_match = TRUE;
651e639dc31SJohn Marino 	}
652e639dc31SJohn Marino 
6531133e27eSPeter Avalos 	/*
654e639dc31SJohn Marino 	 * Find the next history entry which matches.
6551133e27eSPeter Avalos 	 */
656e639dc31SJohn Marino 	for (ml = curr_mlist->curr_mp;;)
657e639dc31SJohn Marino 	{
658e639dc31SJohn Marino 		ml = (action == EC_UP) ? ml->prev : ml->next;
659e639dc31SJohn Marino 		if (ml == curr_mlist)
660e639dc31SJohn Marino 		{
6611133e27eSPeter Avalos 			/*
662e639dc31SJohn Marino 			 * We reached the end (or beginning) of the list.
663e639dc31SJohn Marino 			 */
664e639dc31SJohn Marino 			break;
665e639dc31SJohn Marino 		}
666e639dc31SJohn Marino 		if (strncmp(cmdbuf, ml->string, updown_match) == 0)
667e639dc31SJohn Marino 		{
668e639dc31SJohn Marino 			/*
669e639dc31SJohn Marino 			 * This entry matches; stop here.
6701133e27eSPeter Avalos 			 * Copy the entry into cmdbuf and echo it on the screen.
6711133e27eSPeter Avalos 			 */
672e639dc31SJohn Marino 			curr_mlist->curr_mp = ml;
673e639dc31SJohn Marino 			s = ml->string;
6741133e27eSPeter Avalos 			if (s == NULL)
6751133e27eSPeter Avalos 				s = "";
67602d62a0fSDaniel Fojt 			cmd_offset = 0;
677e639dc31SJohn Marino 			cmd_home();
678e639dc31SJohn Marino 			clear_eol();
6791133e27eSPeter Avalos 			strcpy(cmdbuf, s);
6801133e27eSPeter Avalos 			for (cp = cmdbuf;  *cp != '\0';  )
6811133e27eSPeter Avalos 				cmd_right();
6821133e27eSPeter Avalos 			return (CC_OK);
6831133e27eSPeter Avalos 		}
684e639dc31SJohn Marino 	}
685e639dc31SJohn Marino 	/*
686e639dc31SJohn Marino 	 * We didn't find a history entry that matches.
687e639dc31SJohn Marino 	 */
688e639dc31SJohn Marino 	bell();
689e639dc31SJohn Marino 	return (CC_OK);
690e639dc31SJohn Marino }
691*e433da38SAaron LI 
692*e433da38SAaron LI /*
693*e433da38SAaron LI  * Yet another lesson in the evils of global variables.
694*e433da38SAaron LI  */
save_updown_match(void)695*e433da38SAaron LI public ssize_t save_updown_match(void)
696*e433da38SAaron LI {
697*e433da38SAaron LI 	if (!have_updown_match)
698*e433da38SAaron LI 		return (ssize_t)(-1);
699*e433da38SAaron LI 	return (ssize_t) updown_match;
700*e433da38SAaron LI }
701*e433da38SAaron LI 
restore_updown_match(ssize_t udm)702*e433da38SAaron LI public void restore_updown_match(ssize_t udm)
703*e433da38SAaron LI {
704*e433da38SAaron LI 	updown_match = udm;
705*e433da38SAaron LI 	have_updown_match = (udm != (ssize_t)(-1));
706*e433da38SAaron LI }
707*e433da38SAaron LI #endif /* CMD_HISTORY */
7081133e27eSPeter Avalos 
7091133e27eSPeter Avalos /*
71002d62a0fSDaniel Fojt  *
71102d62a0fSDaniel Fojt  */
ml_link(struct mlist * mlist,struct mlist * ml)712320d7c8aSAaron LI static void ml_link(struct mlist *mlist, struct mlist *ml)
71302d62a0fSDaniel Fojt {
71402d62a0fSDaniel Fojt 	ml->next = mlist;
71502d62a0fSDaniel Fojt 	ml->prev = mlist->prev;
71602d62a0fSDaniel Fojt 	mlist->prev->next = ml;
71702d62a0fSDaniel Fojt 	mlist->prev = ml;
71802d62a0fSDaniel Fojt }
71902d62a0fSDaniel Fojt 
72002d62a0fSDaniel Fojt /*
72102d62a0fSDaniel Fojt  *
72202d62a0fSDaniel Fojt  */
ml_unlink(struct mlist * ml)723320d7c8aSAaron LI static void ml_unlink(struct mlist *ml)
72402d62a0fSDaniel Fojt {
72502d62a0fSDaniel Fojt 	ml->prev->next = ml->next;
72602d62a0fSDaniel Fojt 	ml->next->prev = ml->prev;
72702d62a0fSDaniel Fojt }
72802d62a0fSDaniel Fojt 
72902d62a0fSDaniel Fojt /*
730fa0be7c5SJohn Marino  * Add a string to an mlist.
7311133e27eSPeter Avalos  */
cmd_addhist(struct mlist * mlist,constant char * cmd,lbool modified)732*e433da38SAaron LI public void cmd_addhist(struct mlist *mlist, constant char *cmd, lbool modified)
7331133e27eSPeter Avalos {
7341133e27eSPeter Avalos #if CMD_HISTORY
7351133e27eSPeter Avalos 	struct mlist *ml;
7361133e27eSPeter Avalos 
7371133e27eSPeter Avalos 	/*
7381133e27eSPeter Avalos 	 * Don't save a trivial command.
7391133e27eSPeter Avalos 	 */
7401133e27eSPeter Avalos 	if (strlen(cmd) == 0)
7411133e27eSPeter Avalos 		return;
7421133e27eSPeter Avalos 
74302d62a0fSDaniel Fojt 	if (no_hist_dups)
74402d62a0fSDaniel Fojt 	{
74502d62a0fSDaniel Fojt 		struct mlist *next = NULL;
74602d62a0fSDaniel Fojt 		for (ml = mlist->next;  ml->string != NULL;  ml = next)
74702d62a0fSDaniel Fojt 		{
74802d62a0fSDaniel Fojt 			next = ml->next;
74902d62a0fSDaniel Fojt 			if (strcmp(ml->string, cmd) == 0)
75002d62a0fSDaniel Fojt 			{
75102d62a0fSDaniel Fojt 				ml_unlink(ml);
75202d62a0fSDaniel Fojt 				free(ml->string);
75302d62a0fSDaniel Fojt 				free(ml);
75402d62a0fSDaniel Fojt 			}
75502d62a0fSDaniel Fojt 		}
75602d62a0fSDaniel Fojt 	}
75702d62a0fSDaniel Fojt 
7581133e27eSPeter Avalos 	/*
7591133e27eSPeter Avalos 	 * Save the command unless it's a duplicate of the
7601133e27eSPeter Avalos 	 * last command in the history.
7611133e27eSPeter Avalos 	 */
7621133e27eSPeter Avalos 	ml = mlist->prev;
7631133e27eSPeter Avalos 	if (ml == mlist || strcmp(ml->string, cmd) != 0)
7641133e27eSPeter Avalos 	{
7651133e27eSPeter Avalos 		/*
7661133e27eSPeter Avalos 		 * Did not find command in history.
7671133e27eSPeter Avalos 		 * Save the command and put it at the end of the history list.
7681133e27eSPeter Avalos 		 */
7691133e27eSPeter Avalos 		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
7701133e27eSPeter Avalos 		ml->string = save(cmd);
771fa0be7c5SJohn Marino 		ml->modified = modified;
77202d62a0fSDaniel Fojt 		ml_link(mlist, ml);
7731133e27eSPeter Avalos 	}
7741133e27eSPeter Avalos 	/*
7751133e27eSPeter Avalos 	 * Point to the cmd just after the just-accepted command.
7761133e27eSPeter Avalos 	 * Thus, an UPARROW will always retrieve the previous command.
7771133e27eSPeter Avalos 	 */
7781133e27eSPeter Avalos 	mlist->curr_mp = ml->next;
7791133e27eSPeter Avalos #endif
7801133e27eSPeter Avalos }
7811133e27eSPeter Avalos 
7821133e27eSPeter Avalos /*
7831133e27eSPeter Avalos  * Accept the command in the command buffer.
7841133e27eSPeter Avalos  * Add it to the currently selected history list.
7851133e27eSPeter Avalos  */
cmd_accept(void)786320d7c8aSAaron LI public void cmd_accept(void)
7871133e27eSPeter Avalos {
7881133e27eSPeter Avalos #if CMD_HISTORY
7891133e27eSPeter Avalos 	/*
7901133e27eSPeter Avalos 	 * Nothing to do if there is no currently selected history list.
7911133e27eSPeter Avalos 	 */
79202d62a0fSDaniel Fojt 	if (curr_mlist == NULL || curr_mlist == ml_examine)
7931133e27eSPeter Avalos 		return;
794*e433da38SAaron LI 	cmd_addhist(curr_mlist, cmdbuf, TRUE);
795*e433da38SAaron LI 	curr_mlist->modified = TRUE;
7961133e27eSPeter Avalos #endif
7971133e27eSPeter Avalos }
7981133e27eSPeter Avalos 
7991133e27eSPeter Avalos /*
8001133e27eSPeter Avalos  * Try to perform a line-edit function on the command buffer,
8011133e27eSPeter Avalos  * using a specified char as a line-editing command.
8021133e27eSPeter Avalos  * Returns:
8031133e27eSPeter Avalos  *      CC_PASS The char does not invoke a line edit function.
8041133e27eSPeter Avalos  *      CC_OK   Line edit function done.
8051133e27eSPeter Avalos  *      CC_QUIT The char requests the current command to be aborted.
8061133e27eSPeter Avalos  */
cmd_edit(char c)807*e433da38SAaron LI static int cmd_edit(char c)
8081133e27eSPeter Avalos {
8091133e27eSPeter Avalos 	int action;
8101133e27eSPeter Avalos 	int flags;
8111133e27eSPeter Avalos 
8121133e27eSPeter Avalos #if TAB_COMPLETE_FILENAME
8131133e27eSPeter Avalos #define not_in_completion()     in_completion = 0
8141133e27eSPeter Avalos #else
815320d7c8aSAaron LI #define not_in_completion(void)
8161133e27eSPeter Avalos #endif
8171133e27eSPeter Avalos 
8181133e27eSPeter Avalos 	/*
8191133e27eSPeter Avalos 	 * See if the char is indeed a line-editing command.
8201133e27eSPeter Avalos 	 */
8211133e27eSPeter Avalos 	flags = 0;
8221133e27eSPeter Avalos #if CMD_HISTORY
8231133e27eSPeter Avalos 	if (curr_mlist == NULL)
8241133e27eSPeter Avalos 		/*
8251133e27eSPeter Avalos 		 * No current history; don't accept history manipulation cmds.
8261133e27eSPeter Avalos 		 */
8270c7ad07eSAntonio Huete Jimenez 		flags |= ECF_NOHISTORY;
8281133e27eSPeter Avalos #endif
8291133e27eSPeter Avalos #if TAB_COMPLETE_FILENAME
8300c7ad07eSAntonio Huete Jimenez 	if (curr_mlist == ml_search || curr_mlist == NULL)
8311133e27eSPeter Avalos 		/*
8320c7ad07eSAntonio Huete Jimenez 		 * Don't accept file-completion cmds in contexts
8330c7ad07eSAntonio Huete Jimenez 		 * such as search pattern, digits, long option name, etc.
8341133e27eSPeter Avalos 		 */
8350c7ad07eSAntonio Huete Jimenez 		flags |= ECF_NOCOMPLETE;
8361133e27eSPeter Avalos #endif
8371133e27eSPeter Avalos 
8381133e27eSPeter Avalos 	action = editchar(c, flags);
8391133e27eSPeter Avalos 
8401133e27eSPeter Avalos 	switch (action)
8411133e27eSPeter Avalos 	{
8420c7ad07eSAntonio Huete Jimenez 	case A_NOACTION:
8430c7ad07eSAntonio Huete Jimenez 		return (CC_OK);
8441133e27eSPeter Avalos 	case EC_RIGHT:
8451133e27eSPeter Avalos 		not_in_completion();
8461133e27eSPeter Avalos 		return (cmd_right());
8471133e27eSPeter Avalos 	case EC_LEFT:
8481133e27eSPeter Avalos 		not_in_completion();
8491133e27eSPeter Avalos 		return (cmd_left());
8501133e27eSPeter Avalos 	case EC_W_RIGHT:
8511133e27eSPeter Avalos 		not_in_completion();
8521133e27eSPeter Avalos 		while (*cp != '\0' && *cp != ' ')
8531133e27eSPeter Avalos 			cmd_right();
8541133e27eSPeter Avalos 		while (*cp == ' ')
8551133e27eSPeter Avalos 			cmd_right();
8561133e27eSPeter Avalos 		return (CC_OK);
8571133e27eSPeter Avalos 	case EC_W_LEFT:
8581133e27eSPeter Avalos 		not_in_completion();
8591133e27eSPeter Avalos 		while (cp > cmdbuf && cp[-1] == ' ')
8601133e27eSPeter Avalos 			cmd_left();
8611133e27eSPeter Avalos 		while (cp > cmdbuf && cp[-1] != ' ')
8621133e27eSPeter Avalos 			cmd_left();
8631133e27eSPeter Avalos 		return (CC_OK);
8641133e27eSPeter Avalos 	case EC_HOME:
8651133e27eSPeter Avalos 		not_in_completion();
8661133e27eSPeter Avalos 		cmd_offset = 0;
8671133e27eSPeter Avalos 		cmd_home();
8681133e27eSPeter Avalos 		cmd_repaint(cp);
8691133e27eSPeter Avalos 		return (CC_OK);
8701133e27eSPeter Avalos 	case EC_END:
8711133e27eSPeter Avalos 		not_in_completion();
8721133e27eSPeter Avalos 		while (*cp != '\0')
8731133e27eSPeter Avalos 			cmd_right();
8741133e27eSPeter Avalos 		return (CC_OK);
8751133e27eSPeter Avalos 	case EC_INSERT:
8761133e27eSPeter Avalos 		not_in_completion();
8771133e27eSPeter Avalos 		return (CC_OK);
8781133e27eSPeter Avalos 	case EC_BACKSPACE:
8791133e27eSPeter Avalos 		not_in_completion();
8801133e27eSPeter Avalos 		return (cmd_erase());
8811133e27eSPeter Avalos 	case EC_LINEKILL:
8821133e27eSPeter Avalos 		not_in_completion();
8831133e27eSPeter Avalos 		return (cmd_kill());
88425ce721eSPeter Avalos 	case EC_ABORT:
88525ce721eSPeter Avalos 		not_in_completion();
88625ce721eSPeter Avalos 		(void) cmd_kill();
88725ce721eSPeter Avalos 		return (CC_QUIT);
8881133e27eSPeter Avalos 	case EC_W_BACKSPACE:
8891133e27eSPeter Avalos 		not_in_completion();
8901133e27eSPeter Avalos 		return (cmd_werase());
8911133e27eSPeter Avalos 	case EC_DELETE:
8921133e27eSPeter Avalos 		not_in_completion();
8931133e27eSPeter Avalos 		return (cmd_delete());
8941133e27eSPeter Avalos 	case EC_W_DELETE:
8951133e27eSPeter Avalos 		not_in_completion();
8961133e27eSPeter Avalos 		return (cmd_wdelete());
8971133e27eSPeter Avalos 	case EC_LITERAL:
898*e433da38SAaron LI 		literal = TRUE;
8991133e27eSPeter Avalos 		return (CC_OK);
9001133e27eSPeter Avalos #if CMD_HISTORY
9011133e27eSPeter Avalos 	case EC_UP:
9021133e27eSPeter Avalos 	case EC_DOWN:
9031133e27eSPeter Avalos 		not_in_completion();
9041133e27eSPeter Avalos 		return (cmd_updown(action));
9051133e27eSPeter Avalos #endif
9061133e27eSPeter Avalos #if TAB_COMPLETE_FILENAME
9071133e27eSPeter Avalos 	case EC_F_COMPLETE:
9081133e27eSPeter Avalos 	case EC_B_COMPLETE:
9091133e27eSPeter Avalos 	case EC_EXPAND:
9101133e27eSPeter Avalos 		return (cmd_complete(action));
9111133e27eSPeter Avalos #endif
9121133e27eSPeter Avalos 	default:
9131133e27eSPeter Avalos 		not_in_completion();
9141133e27eSPeter Avalos 		return (CC_PASS);
9151133e27eSPeter Avalos 	}
9161133e27eSPeter Avalos }
9171133e27eSPeter Avalos 
9181133e27eSPeter Avalos #if TAB_COMPLETE_FILENAME
9191133e27eSPeter Avalos /*
9201133e27eSPeter Avalos  * Insert a string into the command buffer, at the current position.
9211133e27eSPeter Avalos  */
cmd_istr(constant char * str)922*e433da38SAaron LI static int cmd_istr(constant char *str)
9231133e27eSPeter Avalos {
924*e433da38SAaron LI 	constant char *endline = str + strlen(str);
925*e433da38SAaron LI 	constant char *s;
9261133e27eSPeter Avalos 	int action;
9271133e27eSPeter Avalos 
9281133e27eSPeter Avalos 	for (s = str;  *s != '\0';  )
9291133e27eSPeter Avalos 	{
930*e433da38SAaron LI 		constant char *os = s;
931*e433da38SAaron LI 		step_charc(&s, +1, endline);
932*e433da38SAaron LI 		action = cmd_ichar(os, ptr_diff(s, os));
9331133e27eSPeter Avalos 		if (action != CC_OK)
9341133e27eSPeter Avalos 			return (action);
9351133e27eSPeter Avalos 	}
9361133e27eSPeter Avalos 	return (CC_OK);
9371133e27eSPeter Avalos }
9381133e27eSPeter Avalos 
9391133e27eSPeter Avalos /*
9401133e27eSPeter Avalos  * Find the beginning and end of the "current" word.
9411133e27eSPeter Avalos  * This is the word which the cursor (cp) is inside or at the end of.
9421133e27eSPeter Avalos  * Return pointer to the beginning of the word and put the
9431133e27eSPeter Avalos  * cursor at the end of the word.
9441133e27eSPeter Avalos  */
delimit_word(void)945320d7c8aSAaron LI static char * delimit_word(void)
9461133e27eSPeter Avalos {
9471133e27eSPeter Avalos 	char *word;
9481133e27eSPeter Avalos #if SPACES_IN_FILENAMES
9491133e27eSPeter Avalos 	char *p;
950*e433da38SAaron LI 	int delim_quoted = FALSE;
951*e433da38SAaron LI 	int meta_quoted = FALSE;
95202d62a0fSDaniel Fojt 	constant char *esc = get_meta_escape();
953*e433da38SAaron LI 	size_t esclen = strlen(esc);
9541133e27eSPeter Avalos #endif
9551133e27eSPeter Avalos 
9561133e27eSPeter Avalos 	/*
9571133e27eSPeter Avalos 	 * Move cursor to end of word.
9581133e27eSPeter Avalos 	 */
9591133e27eSPeter Avalos 	if (*cp != ' ' && *cp != '\0')
9601133e27eSPeter Avalos 	{
9611133e27eSPeter Avalos 		/*
9621133e27eSPeter Avalos 		 * Cursor is on a nonspace.
9631133e27eSPeter Avalos 		 * Move cursor right to the next space.
9641133e27eSPeter Avalos 		 */
9651133e27eSPeter Avalos 		while (*cp != ' ' && *cp != '\0')
9661133e27eSPeter Avalos 			cmd_right();
9671133e27eSPeter Avalos 	} else if (cp > cmdbuf && cp[-1] != ' ')
9681133e27eSPeter Avalos 	{
9691133e27eSPeter Avalos 		/*
9701133e27eSPeter Avalos 		 * Cursor is on a space, and char to the left is a nonspace.
9711133e27eSPeter Avalos 		 * We're already at the end of the word.
9721133e27eSPeter Avalos 		 */
9731133e27eSPeter Avalos 		;
9741133e27eSPeter Avalos #if 0
9751133e27eSPeter Avalos 	} else
9761133e27eSPeter Avalos 	{
9771133e27eSPeter Avalos 		/*
9781133e27eSPeter Avalos 		 * Cursor is on a space and char to the left is a space.
9791133e27eSPeter Avalos 		 * Huh? There's no word here.
9801133e27eSPeter Avalos 		 */
9811133e27eSPeter Avalos 		return (NULL);
9821133e27eSPeter Avalos #endif
9831133e27eSPeter Avalos 	}
9841133e27eSPeter Avalos 	/*
9851133e27eSPeter Avalos 	 * Find the beginning of the word which the cursor is in.
9861133e27eSPeter Avalos 	 */
9871133e27eSPeter Avalos 	if (cp == cmdbuf)
9881133e27eSPeter Avalos 		return (NULL);
9891133e27eSPeter Avalos #if SPACES_IN_FILENAMES
9901133e27eSPeter Avalos 	/*
9911133e27eSPeter Avalos 	 * If we have an unbalanced quote (that is, an open quote
9921133e27eSPeter Avalos 	 * without a corresponding close quote), we return everything
9931133e27eSPeter Avalos 	 * from the open quote, including spaces.
9941133e27eSPeter Avalos 	 */
9951133e27eSPeter Avalos 	for (word = cmdbuf;  word < cp;  word++)
9961133e27eSPeter Avalos 		if (*word != ' ')
9971133e27eSPeter Avalos 			break;
9981133e27eSPeter Avalos 	if (word >= cp)
9991133e27eSPeter Avalos 		return (cp);
10001133e27eSPeter Avalos 	for (p = cmdbuf;  p < cp;  p++)
10011133e27eSPeter Avalos 	{
10021133e27eSPeter Avalos 		if (meta_quoted)
10031133e27eSPeter Avalos 		{
1004*e433da38SAaron LI 			meta_quoted = FALSE;
10051133e27eSPeter Avalos 		} else if (esclen > 0 && p + esclen < cp &&
10061133e27eSPeter Avalos 		           strncmp(p, esc, esclen) == 0)
10071133e27eSPeter Avalos 		{
1008*e433da38SAaron LI 			meta_quoted = TRUE;
10091133e27eSPeter Avalos 			p += esclen - 1;
10101133e27eSPeter Avalos 		} else if (delim_quoted)
10111133e27eSPeter Avalos 		{
10121133e27eSPeter Avalos 			if (*p == closequote)
1013*e433da38SAaron LI 				delim_quoted = FALSE;
10141133e27eSPeter Avalos 		} else /* (!delim_quoted) */
10151133e27eSPeter Avalos 		{
10161133e27eSPeter Avalos 			if (*p == openquote)
1017*e433da38SAaron LI 				delim_quoted = TRUE;
10181133e27eSPeter Avalos 			else if (*p == ' ')
10191133e27eSPeter Avalos 				word = p+1;
10201133e27eSPeter Avalos 		}
10211133e27eSPeter Avalos 	}
10221133e27eSPeter Avalos #endif
10231133e27eSPeter Avalos 	return (word);
10241133e27eSPeter Avalos }
10251133e27eSPeter Avalos 
10261133e27eSPeter Avalos /*
10271133e27eSPeter Avalos  * Set things up to enter completion mode.
10281133e27eSPeter Avalos  * Expand the word under the cursor into a list of filenames
10291133e27eSPeter Avalos  * which start with that word, and set tk_text to that list.
10301133e27eSPeter Avalos  */
init_compl(void)1031320d7c8aSAaron LI static void init_compl(void)
10321133e27eSPeter Avalos {
10331133e27eSPeter Avalos 	char *word;
10341133e27eSPeter Avalos 	char c;
10351133e27eSPeter Avalos 
10361133e27eSPeter Avalos 	/*
10371133e27eSPeter Avalos 	 * Get rid of any previous tk_text.
10381133e27eSPeter Avalos 	 */
10391133e27eSPeter Avalos 	if (tk_text != NULL)
10401133e27eSPeter Avalos 	{
10411133e27eSPeter Avalos 		free(tk_text);
10421133e27eSPeter Avalos 		tk_text = NULL;
10431133e27eSPeter Avalos 	}
10441133e27eSPeter Avalos 	/*
10451133e27eSPeter Avalos 	 * Find the original (uncompleted) word in the command buffer.
10461133e27eSPeter Avalos 	 */
10471133e27eSPeter Avalos 	word = delimit_word();
10481133e27eSPeter Avalos 	if (word == NULL)
10491133e27eSPeter Avalos 		return;
10501133e27eSPeter Avalos 	/*
10511133e27eSPeter Avalos 	 * Set the insertion point to the point in the command buffer
10521133e27eSPeter Avalos 	 * where the original (uncompleted) word now sits.
10531133e27eSPeter Avalos 	 */
10541133e27eSPeter Avalos 	tk_ipoint = word;
10551133e27eSPeter Avalos 	/*
10561133e27eSPeter Avalos 	 * Save the original (uncompleted) word
10571133e27eSPeter Avalos 	 */
10581133e27eSPeter Avalos 	if (tk_original != NULL)
10591133e27eSPeter Avalos 		free(tk_original);
1060*e433da38SAaron LI 	tk_original = (char *) ecalloc(ptr_diff(cp,word)+1, sizeof(char));
1061*e433da38SAaron LI 	strncpy(tk_original, word, ptr_diff(cp,word));
10621133e27eSPeter Avalos 	/*
10631133e27eSPeter Avalos 	 * Get the expanded filename.
10641133e27eSPeter Avalos 	 * This may result in a single filename, or
10651133e27eSPeter Avalos 	 * a blank-separated list of filenames.
10661133e27eSPeter Avalos 	 */
10671133e27eSPeter Avalos 	c = *cp;
10681133e27eSPeter Avalos 	*cp = '\0';
10691133e27eSPeter Avalos 	if (*word != openquote)
10701133e27eSPeter Avalos 	{
10711133e27eSPeter Avalos 		tk_text = fcomplete(word);
10721133e27eSPeter Avalos 	} else
10731133e27eSPeter Avalos 	{
1074e639dc31SJohn Marino #if MSDOS_COMPILER
1075e639dc31SJohn Marino 		char *qword = NULL;
1076e639dc31SJohn Marino #else
10771133e27eSPeter Avalos 		char *qword = shell_quote(word+1);
1078e639dc31SJohn Marino #endif
10791133e27eSPeter Avalos 		if (qword == NULL)
10801133e27eSPeter Avalos 			tk_text = fcomplete(word+1);
10811133e27eSPeter Avalos 		else
10821133e27eSPeter Avalos 		{
10831133e27eSPeter Avalos 			tk_text = fcomplete(qword);
10841133e27eSPeter Avalos 			free(qword);
10851133e27eSPeter Avalos 		}
10861133e27eSPeter Avalos 	}
10871133e27eSPeter Avalos 	*cp = c;
10881133e27eSPeter Avalos }
10891133e27eSPeter Avalos 
10901133e27eSPeter Avalos /*
10911133e27eSPeter Avalos  * Return the next word in the current completion list.
10921133e27eSPeter Avalos  */
next_compl(int action,constant char * prev)1093*e433da38SAaron LI static constant char * next_compl(int action, constant char *prev)
10941133e27eSPeter Avalos {
10951133e27eSPeter Avalos 	switch (action)
10961133e27eSPeter Avalos 	{
10971133e27eSPeter Avalos 	case EC_F_COMPLETE:
10981133e27eSPeter Avalos 		return (forw_textlist(&tk_tlist, prev));
10991133e27eSPeter Avalos 	case EC_B_COMPLETE:
11001133e27eSPeter Avalos 		return (back_textlist(&tk_tlist, prev));
11011133e27eSPeter Avalos 	}
11021133e27eSPeter Avalos 	/* Cannot happen */
11031133e27eSPeter Avalos 	return ("?");
11041133e27eSPeter Avalos }
11051133e27eSPeter Avalos 
11061133e27eSPeter Avalos /*
11071133e27eSPeter Avalos  * Complete the filename before (or under) the cursor.
11081133e27eSPeter Avalos  * cmd_complete may be called multiple times.  The global in_completion
11091133e27eSPeter Avalos  * remembers whether this call is the first time (create the list),
11101133e27eSPeter Avalos  * or a subsequent time (step thru the list).
11111133e27eSPeter Avalos  */
cmd_complete(int action)1112320d7c8aSAaron LI static int cmd_complete(int action)
11131133e27eSPeter Avalos {
1114*e433da38SAaron LI 	constant char *s;
11151133e27eSPeter Avalos 
11161133e27eSPeter Avalos 	if (!in_completion || action == EC_EXPAND)
11171133e27eSPeter Avalos 	{
11181133e27eSPeter Avalos 		/*
11191133e27eSPeter Avalos 		 * Expand the word under the cursor and
11201133e27eSPeter Avalos 		 * use the first word in the expansion
11211133e27eSPeter Avalos 		 * (or the entire expansion if we're doing EC_EXPAND).
11221133e27eSPeter Avalos 		 */
11231133e27eSPeter Avalos 		init_compl();
11241133e27eSPeter Avalos 		if (tk_text == NULL)
11251133e27eSPeter Avalos 		{
11261133e27eSPeter Avalos 			bell();
11271133e27eSPeter Avalos 			return (CC_OK);
11281133e27eSPeter Avalos 		}
11291133e27eSPeter Avalos 		if (action == EC_EXPAND)
11301133e27eSPeter Avalos 		{
11311133e27eSPeter Avalos 			/*
11321133e27eSPeter Avalos 			 * Use the whole list.
11331133e27eSPeter Avalos 			 */
11341133e27eSPeter Avalos 			tk_trial = tk_text;
11351133e27eSPeter Avalos 		} else
11361133e27eSPeter Avalos 		{
11371133e27eSPeter Avalos 			/*
11381133e27eSPeter Avalos 			 * Use the first filename in the list.
11391133e27eSPeter Avalos 			 */
1140*e433da38SAaron LI 			in_completion = TRUE;
11411133e27eSPeter Avalos 			init_textlist(&tk_tlist, tk_text);
11421133e27eSPeter Avalos 			tk_trial = next_compl(action, (char*)NULL);
11431133e27eSPeter Avalos 		}
11441133e27eSPeter Avalos 	} else
11451133e27eSPeter Avalos 	{
11461133e27eSPeter Avalos 		/*
11471133e27eSPeter Avalos 		 * We already have a completion list.
11481133e27eSPeter Avalos 		 * Use the next/previous filename from the list.
11491133e27eSPeter Avalos 		 */
11501133e27eSPeter Avalos 		tk_trial = next_compl(action, tk_trial);
11511133e27eSPeter Avalos 	}
11521133e27eSPeter Avalos 
11531133e27eSPeter Avalos 	/*
11541133e27eSPeter Avalos 	 * Remove the original word, or the previous trial completion.
11551133e27eSPeter Avalos 	 */
11561133e27eSPeter Avalos 	while (cp > tk_ipoint)
11571133e27eSPeter Avalos 		(void) cmd_erase();
11581133e27eSPeter Avalos 
11591133e27eSPeter Avalos 	if (tk_trial == NULL)
11601133e27eSPeter Avalos 	{
11611133e27eSPeter Avalos 		/*
11621133e27eSPeter Avalos 		 * There are no more trial completions.
11631133e27eSPeter Avalos 		 * Insert the original (uncompleted) filename.
11641133e27eSPeter Avalos 		 */
1165*e433da38SAaron LI 		in_completion = FALSE;
11661133e27eSPeter Avalos 		if (cmd_istr(tk_original) != CC_OK)
11671133e27eSPeter Avalos 			goto fail;
11681133e27eSPeter Avalos 	} else
11691133e27eSPeter Avalos 	{
11701133e27eSPeter Avalos 		/*
11711133e27eSPeter Avalos 		 * Insert trial completion.
11721133e27eSPeter Avalos 		 */
11731133e27eSPeter Avalos 		if (cmd_istr(tk_trial) != CC_OK)
11741133e27eSPeter Avalos 			goto fail;
11751133e27eSPeter Avalos 		/*
11761133e27eSPeter Avalos 		 * If it is a directory, append a slash.
11771133e27eSPeter Avalos 		 */
11781133e27eSPeter Avalos 		if (is_dir(tk_trial))
11791133e27eSPeter Avalos 		{
11801133e27eSPeter Avalos 			if (cp > cmdbuf && cp[-1] == closequote)
11811133e27eSPeter Avalos 				(void) cmd_erase();
11821133e27eSPeter Avalos 			s = lgetenv("LESSSEPARATOR");
11831133e27eSPeter Avalos 			if (s == NULL)
11841133e27eSPeter Avalos 				s = PATHNAME_SEP;
11851133e27eSPeter Avalos 			if (cmd_istr(s) != CC_OK)
11861133e27eSPeter Avalos 				goto fail;
11871133e27eSPeter Avalos 		}
11881133e27eSPeter Avalos 	}
11891133e27eSPeter Avalos 
11901133e27eSPeter Avalos 	return (CC_OK);
11911133e27eSPeter Avalos 
11921133e27eSPeter Avalos fail:
1193*e433da38SAaron LI 	in_completion = FALSE;
11941133e27eSPeter Avalos 	bell();
11951133e27eSPeter Avalos 	return (CC_OK);
11961133e27eSPeter Avalos }
11971133e27eSPeter Avalos 
11981133e27eSPeter Avalos #endif /* TAB_COMPLETE_FILENAME */
11991133e27eSPeter Avalos 
12001133e27eSPeter Avalos /*
12011133e27eSPeter Avalos  * Process a single character of a multi-character command, such as
12021133e27eSPeter Avalos  * a number, or the pattern of a search command.
12031133e27eSPeter Avalos  * Returns:
12041133e27eSPeter Avalos  *      CC_OK           The char was accepted.
12051133e27eSPeter Avalos  *      CC_QUIT         The char requests the command to be aborted.
12061133e27eSPeter Avalos  *      CC_ERROR        The char could not be accepted due to an error.
12071133e27eSPeter Avalos  */
cmd_char(char c)1208*e433da38SAaron LI public int cmd_char(char c)
12091133e27eSPeter Avalos {
12101133e27eSPeter Avalos 	int action;
1211*e433da38SAaron LI 	size_t len;
12121133e27eSPeter Avalos 
12131133e27eSPeter Avalos 	if (!utf_mode)
12141133e27eSPeter Avalos 	{
12151133e27eSPeter Avalos 		cmd_mbc_buf[0] = c;
12161133e27eSPeter Avalos 		len = 1;
12171133e27eSPeter Avalos 	} else
12181133e27eSPeter Avalos 	{
12191133e27eSPeter Avalos 		/* Perform strict validation in all possible cases.  */
12201133e27eSPeter Avalos 		if (cmd_mbc_buf_len == 0)
12211133e27eSPeter Avalos 		{
12221133e27eSPeter Avalos 		 retry:
12231133e27eSPeter Avalos 			cmd_mbc_buf_index = 1;
12241133e27eSPeter Avalos 			*cmd_mbc_buf = c;
12251133e27eSPeter Avalos 			if (IS_ASCII_OCTET(c))
12261133e27eSPeter Avalos 				cmd_mbc_buf_len = 1;
122702d62a0fSDaniel Fojt #if MSDOS_COMPILER || OS2
1228*e433da38SAaron LI 			else if (c == '\340' && IS_ASCII_OCTET(peekcc()))
122902d62a0fSDaniel Fojt 			{
123002d62a0fSDaniel Fojt 				/* Assume a special key. */
123102d62a0fSDaniel Fojt 				cmd_mbc_buf_len = 1;
123202d62a0fSDaniel Fojt 			}
123302d62a0fSDaniel Fojt #endif
12341133e27eSPeter Avalos 			else if (IS_UTF8_LEAD(c))
12351133e27eSPeter Avalos 			{
12361133e27eSPeter Avalos 				cmd_mbc_buf_len = utf_len(c);
12371133e27eSPeter Avalos 				return (CC_OK);
12381133e27eSPeter Avalos 			} else
12391133e27eSPeter Avalos 			{
12401133e27eSPeter Avalos 				/* UTF8_INVALID or stray UTF8_TRAIL */
12411133e27eSPeter Avalos 				bell();
12421133e27eSPeter Avalos 				return (CC_ERROR);
12431133e27eSPeter Avalos 			}
12441133e27eSPeter Avalos 		} else if (IS_UTF8_TRAIL(c))
12451133e27eSPeter Avalos 		{
12461133e27eSPeter Avalos 			cmd_mbc_buf[cmd_mbc_buf_index++] = c;
12471133e27eSPeter Avalos 			if (cmd_mbc_buf_index < cmd_mbc_buf_len)
12481133e27eSPeter Avalos 				return (CC_OK);
12499b760066SJohn Marino 			if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index))
12501133e27eSPeter Avalos 			{
12511133e27eSPeter Avalos 				/* complete, but not well formed (non-shortest form), sequence */
12521133e27eSPeter Avalos 				cmd_mbc_buf_len = 0;
12531133e27eSPeter Avalos 				bell();
12541133e27eSPeter Avalos 				return (CC_ERROR);
12551133e27eSPeter Avalos 			}
12561133e27eSPeter Avalos 		} else
12571133e27eSPeter Avalos 		{
12581133e27eSPeter Avalos 			/* Flush incomplete (truncated) sequence.  */
12591133e27eSPeter Avalos 			cmd_mbc_buf_len = 0;
12601133e27eSPeter Avalos 			bell();
12611133e27eSPeter Avalos 			/* Handle new char.  */
12621133e27eSPeter Avalos 			goto retry;
12631133e27eSPeter Avalos 		}
12641133e27eSPeter Avalos 
1265*e433da38SAaron LI 		len = (size_t) cmd_mbc_buf_len; /*{{type-issue}}*/
12661133e27eSPeter Avalos 		cmd_mbc_buf_len = 0;
12671133e27eSPeter Avalos 	}
12681133e27eSPeter Avalos 
12691133e27eSPeter Avalos 	if (literal)
12701133e27eSPeter Avalos 	{
12711133e27eSPeter Avalos 		/*
12721133e27eSPeter Avalos 		 * Insert the char, even if it is a line-editing char.
12731133e27eSPeter Avalos 		 */
1274*e433da38SAaron LI 		literal = FALSE;
12751133e27eSPeter Avalos 		return (cmd_ichar(cmd_mbc_buf, len));
12761133e27eSPeter Avalos 	}
12771133e27eSPeter Avalos 
12781133e27eSPeter Avalos 	/*
12791133e27eSPeter Avalos 	 * See if it is a line-editing character.
12801133e27eSPeter Avalos 	 */
12811133e27eSPeter Avalos 	if (in_mca() && len == 1)
12821133e27eSPeter Avalos 	{
12831133e27eSPeter Avalos 		action = cmd_edit(c);
12841133e27eSPeter Avalos 		switch (action)
12851133e27eSPeter Avalos 		{
12861133e27eSPeter Avalos 		case CC_OK:
12871133e27eSPeter Avalos 		case CC_QUIT:
12881133e27eSPeter Avalos 			return (action);
12891133e27eSPeter Avalos 		case CC_PASS:
12901133e27eSPeter Avalos 			break;
12911133e27eSPeter Avalos 		}
12921133e27eSPeter Avalos 	}
12931133e27eSPeter Avalos 
12941133e27eSPeter Avalos 	/*
12951133e27eSPeter Avalos 	 * Insert the char into the command buffer.
12961133e27eSPeter Avalos 	 */
12971133e27eSPeter Avalos 	return (cmd_ichar(cmd_mbc_buf, len));
12981133e27eSPeter Avalos }
12991133e27eSPeter Avalos 
13001133e27eSPeter Avalos /*
13011133e27eSPeter Avalos  * Return the number currently in the command buffer.
13021133e27eSPeter Avalos  */
cmd_int(mutable long * frac)1303*e433da38SAaron LI public LINENUM cmd_int(mutable long *frac)
13041133e27eSPeter Avalos {
1305*e433da38SAaron LI 	constant char *p;
13061133e27eSPeter Avalos 	LINENUM n = 0;
1307*e433da38SAaron LI 	lbool err;
13081133e27eSPeter Avalos 
13091133e27eSPeter Avalos 	for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
13100c7ad07eSAntonio Huete Jimenez 	{
1311320d7c8aSAaron LI 		if (ckd_mul(&n, n, 10) || ckd_add(&n, n, *p - '0'))
13120c7ad07eSAntonio Huete Jimenez 		{
13130c7ad07eSAntonio Huete Jimenez 			error("Integer is too big", NULL_PARG);
13140c7ad07eSAntonio Huete Jimenez 			return (0);
13150c7ad07eSAntonio Huete Jimenez 		}
13160c7ad07eSAntonio Huete Jimenez 	}
13171133e27eSPeter Avalos 	*frac = 0;
13181133e27eSPeter Avalos 	if (*p++ == '.')
13191133e27eSPeter Avalos 	{
13201133e27eSPeter Avalos 		*frac = getfraction(&p, NULL, &err);
13211133e27eSPeter Avalos 		/* {{ do something if err is set? }} */
13221133e27eSPeter Avalos 	}
13231133e27eSPeter Avalos 	return (n);
13241133e27eSPeter Avalos }
13251133e27eSPeter Avalos 
13261133e27eSPeter Avalos /*
13271133e27eSPeter Avalos  * Return a pointer to the command buffer.
13281133e27eSPeter Avalos  */
get_cmdbuf(void)1329*e433da38SAaron LI public constant char * get_cmdbuf(void)
13301133e27eSPeter Avalos {
13310c7ad07eSAntonio Huete Jimenez 	if (cmd_mbc_buf_index < cmd_mbc_buf_len)
13320c7ad07eSAntonio Huete Jimenez 		/* Don't return buffer containing an incomplete multibyte char. */
13330c7ad07eSAntonio Huete Jimenez 		return (NULL);
13341133e27eSPeter Avalos 	return (cmdbuf);
13351133e27eSPeter Avalos }
13361133e27eSPeter Avalos 
13378be36e5bSPeter Avalos #if CMD_HISTORY
13381133e27eSPeter Avalos /*
13391133e27eSPeter Avalos  * Return the last (most recent) string in the current command history.
13401133e27eSPeter Avalos  */
cmd_lastpattern(void)1341*e433da38SAaron LI public constant char * cmd_lastpattern(void)
13421133e27eSPeter Avalos {
13431133e27eSPeter Avalos 	if (curr_mlist == NULL)
13441133e27eSPeter Avalos 		return (NULL);
13451133e27eSPeter Avalos 	return (curr_mlist->curr_mp->prev->string);
13461133e27eSPeter Avalos }
13478be36e5bSPeter Avalos #endif
13481133e27eSPeter Avalos 
13491133e27eSPeter Avalos #if CMD_HISTORY
13501133e27eSPeter Avalos /*
1351fa0be7c5SJohn Marino  */
mlist_size(struct mlist * ml)1352320d7c8aSAaron LI static int mlist_size(struct mlist *ml)
1353fa0be7c5SJohn Marino {
1354fa0be7c5SJohn Marino 	int size = 0;
1355fa0be7c5SJohn Marino 	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1356fa0be7c5SJohn Marino 		++size;
1357fa0be7c5SJohn Marino 	return size;
1358fa0be7c5SJohn Marino }
1359fa0be7c5SJohn Marino 
1360fa0be7c5SJohn Marino /*
13611133e27eSPeter Avalos  * Get the name of the history file.
13621133e27eSPeter Avalos  */
histfile_find(lbool must_exist)1363*e433da38SAaron LI static char * histfile_find(lbool must_exist)
13641133e27eSPeter Avalos {
1365*e433da38SAaron LI 	constant char *home = lgetenv("HOME");
13660c7ad07eSAntonio Huete Jimenez 	char *name = NULL;
13670c7ad07eSAntonio Huete Jimenez 
13680c7ad07eSAntonio Huete Jimenez 	/* Try in $XDG_STATE_HOME, then in $HOME/.local/state, then in $XDG_DATA_HOME, then in $HOME. */
13690c7ad07eSAntonio Huete Jimenez #if OS2
13700c7ad07eSAntonio Huete Jimenez 	if (isnullenv(home))
13710c7ad07eSAntonio Huete Jimenez 		home = lgetenv("INIT");
13720c7ad07eSAntonio Huete Jimenez #endif
13730c7ad07eSAntonio Huete Jimenez 	name = dirfile(lgetenv("XDG_STATE_HOME"), &LESSHISTFILE[1], must_exist);
13740c7ad07eSAntonio Huete Jimenez 	if (name == NULL)
13750c7ad07eSAntonio Huete Jimenez 	{
13760c7ad07eSAntonio Huete Jimenez 		char *dir = dirfile(home, ".local/state", 1);
13770c7ad07eSAntonio Huete Jimenez 		if (dir != NULL)
13780c7ad07eSAntonio Huete Jimenez 		{
13790c7ad07eSAntonio Huete Jimenez 			name = dirfile(dir, &LESSHISTFILE[1], must_exist);
13800c7ad07eSAntonio Huete Jimenez 			free(dir);
13810c7ad07eSAntonio Huete Jimenez 		}
13820c7ad07eSAntonio Huete Jimenez 	}
13830c7ad07eSAntonio Huete Jimenez 	if (name == NULL)
13840c7ad07eSAntonio Huete Jimenez 		name = dirfile(lgetenv("XDG_DATA_HOME"), &LESSHISTFILE[1], must_exist);
13850c7ad07eSAntonio Huete Jimenez 	if (name == NULL)
13860c7ad07eSAntonio Huete Jimenez 		name = dirfile(home, LESSHISTFILE, must_exist);
13870c7ad07eSAntonio Huete Jimenez 	return (name);
13880c7ad07eSAntonio Huete Jimenez }
13890c7ad07eSAntonio Huete Jimenez 
histfile_name(lbool must_exist)1390*e433da38SAaron LI static char * histfile_name(lbool must_exist)
13910c7ad07eSAntonio Huete Jimenez {
1392*e433da38SAaron LI 	constant char *name;
1393*e433da38SAaron LI 	char *wname;
13941133e27eSPeter Avalos 
13951133e27eSPeter Avalos 	/* See if filename is explicitly specified by $LESSHISTFILE. */
13961133e27eSPeter Avalos 	name = lgetenv("LESSHISTFILE");
139702d62a0fSDaniel Fojt 	if (!isnullenv(name))
13981133e27eSPeter Avalos 	{
13991133e27eSPeter Avalos 		if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
14001133e27eSPeter Avalos 			/* $LESSHISTFILE == "-" means don't use a history file. */
14011133e27eSPeter Avalos 			return (NULL);
14021133e27eSPeter Avalos 		return (save(name));
14031133e27eSPeter Avalos 	}
14041133e27eSPeter Avalos 
1405fa0be7c5SJohn Marino 	/* See if history file is disabled in the build. */
1406fa0be7c5SJohn Marino 	if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0)
1407fa0be7c5SJohn Marino 		return (NULL);
1408fa0be7c5SJohn Marino 
1409*e433da38SAaron LI 	wname = NULL;
14100c7ad07eSAntonio Huete Jimenez 	if (!must_exist)
14111133e27eSPeter Avalos 	{
14120c7ad07eSAntonio Huete Jimenez 	 	/* If we're writing the file and the file already exists, use it. */
1413*e433da38SAaron LI 		wname = histfile_find(TRUE);
14141133e27eSPeter Avalos 	}
1415*e433da38SAaron LI 	if (wname == NULL)
1416*e433da38SAaron LI 		wname = histfile_find(must_exist);
1417*e433da38SAaron LI 	return (wname);
14181133e27eSPeter Avalos }
14191133e27eSPeter Avalos 
14201133e27eSPeter Avalos /*
1421fa0be7c5SJohn Marino  * Read a .lesshst file and call a callback for each line in the file.
14221133e27eSPeter Avalos  */
read_cmdhist2(void (* action)(void *,struct mlist *,constant char *),void * uparam,int skip_search,int skip_shell)1423*e433da38SAaron LI static void read_cmdhist2(void (*action)(void*,struct mlist*,constant char*), void *uparam, int skip_search, int skip_shell)
14241133e27eSPeter Avalos {
14251133e27eSPeter Avalos 	struct mlist *ml = NULL;
14261133e27eSPeter Avalos 	char line[CMDBUF_SIZE];
14271133e27eSPeter Avalos 	char *filename;
14281133e27eSPeter Avalos 	FILE *f;
1429fa0be7c5SJohn Marino 	int *skip = NULL;
14301133e27eSPeter Avalos 
1431*e433da38SAaron LI 	filename = histfile_name(TRUE);
14321133e27eSPeter Avalos 	if (filename == NULL)
14331133e27eSPeter Avalos 		return;
14341133e27eSPeter Avalos 	f = fopen(filename, "r");
14351133e27eSPeter Avalos 	free(filename);
14361133e27eSPeter Avalos 	if (f == NULL)
14371133e27eSPeter Avalos 		return;
14381133e27eSPeter Avalos 	if (fgets(line, sizeof(line), f) == NULL ||
14391133e27eSPeter Avalos 	    strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
14401133e27eSPeter Avalos 	{
14411133e27eSPeter Avalos 		fclose(f);
14421133e27eSPeter Avalos 		return;
14431133e27eSPeter Avalos 	}
14441133e27eSPeter Avalos 	while (fgets(line, sizeof(line), f) != NULL)
14451133e27eSPeter Avalos 	{
1446*e433da38SAaron LI 		char *p;
14471133e27eSPeter Avalos 		for (p = line;  *p != '\0';  p++)
14481133e27eSPeter Avalos 		{
14491133e27eSPeter Avalos 			if (*p == '\n' || *p == '\r')
14501133e27eSPeter Avalos 			{
14511133e27eSPeter Avalos 				*p = '\0';
14521133e27eSPeter Avalos 				break;
14531133e27eSPeter Avalos 			}
14541133e27eSPeter Avalos 		}
14551133e27eSPeter Avalos 		if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1456fa0be7c5SJohn Marino 		{
14571133e27eSPeter Avalos 			ml = &mlist_search;
1458fa0be7c5SJohn Marino 			skip = &skip_search;
1459fa0be7c5SJohn Marino 		} else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
14601133e27eSPeter Avalos 		{
14611133e27eSPeter Avalos #if SHELL_ESCAPE || PIPEC
14621133e27eSPeter Avalos 			ml = &mlist_shell;
1463fa0be7c5SJohn Marino 			skip = &skip_shell;
14641133e27eSPeter Avalos #else
14651133e27eSPeter Avalos 			ml = NULL;
1466fa0be7c5SJohn Marino 			skip = NULL;
14671133e27eSPeter Avalos #endif
146802d62a0fSDaniel Fojt 		} else if (strcmp(line, HISTFILE_MARK_SECTION) == 0)
146902d62a0fSDaniel Fojt 		{
147002d62a0fSDaniel Fojt 			ml = NULL;
14711133e27eSPeter Avalos 		} else if (*line == '"')
14721133e27eSPeter Avalos 		{
14731133e27eSPeter Avalos 			if (ml != NULL)
1474fa0be7c5SJohn Marino 			{
1475fa0be7c5SJohn Marino 				if (skip != NULL && *skip > 0)
1476fa0be7c5SJohn Marino 					--(*skip);
1477fa0be7c5SJohn Marino 				else
1478fa0be7c5SJohn Marino 					(*action)(uparam, ml, line+1);
1479fa0be7c5SJohn Marino 			}
148002d62a0fSDaniel Fojt 		} else if (*line == 'm')
148102d62a0fSDaniel Fojt 		{
148202d62a0fSDaniel Fojt 			(*action)(uparam, NULL, line);
14831133e27eSPeter Avalos 		}
14841133e27eSPeter Avalos 	}
14851133e27eSPeter Avalos 	fclose(f);
1486fa0be7c5SJohn Marino }
1487fa0be7c5SJohn Marino 
read_cmdhist(void (* action)(void *,struct mlist *,constant char *),void * uparam,lbool skip_search,lbool skip_shell)1488*e433da38SAaron LI static void read_cmdhist(void (*action)(void*,struct mlist*,constant char*), void *uparam, lbool skip_search, lbool skip_shell)
1489fa0be7c5SJohn Marino {
1490*e433da38SAaron LI 	if (!secure_allow(SF_HISTORY))
14910c7ad07eSAntonio Huete Jimenez 		return;
1492fa0be7c5SJohn Marino 	read_cmdhist2(action, uparam, skip_search, skip_shell);
1493fa0be7c5SJohn Marino 	(*action)(uparam, NULL, NULL); /* signal end of file */
1494fa0be7c5SJohn Marino }
1495fa0be7c5SJohn Marino 
addhist_init(void * uparam,struct mlist * ml,constant char * string)1496*e433da38SAaron LI static void addhist_init(void *uparam, struct mlist *ml, constant char *string)
1497fa0be7c5SJohn Marino {
1498*e433da38SAaron LI 	(void) uparam;
149902d62a0fSDaniel Fojt 	if (ml != NULL)
1500fa0be7c5SJohn Marino 		cmd_addhist(ml, string, 0);
150102d62a0fSDaniel Fojt 	else if (string != NULL)
1502*e433da38SAaron LI 		restore_mark(string);
1503fa0be7c5SJohn Marino }
1504fa0be7c5SJohn Marino #endif /* CMD_HISTORY */
1505fa0be7c5SJohn Marino 
1506fa0be7c5SJohn Marino /*
1507fa0be7c5SJohn Marino  * Initialize history from a .lesshist file.
1508fa0be7c5SJohn Marino  */
init_cmdhist(void)1509320d7c8aSAaron LI public void init_cmdhist(void)
1510fa0be7c5SJohn Marino {
1511fa0be7c5SJohn Marino #if CMD_HISTORY
1512fa0be7c5SJohn Marino 	read_cmdhist(&addhist_init, NULL, 0, 0);
15131133e27eSPeter Avalos #endif /* CMD_HISTORY */
15141133e27eSPeter Avalos }
15151133e27eSPeter Avalos 
15161133e27eSPeter Avalos /*
1517fa0be7c5SJohn Marino  * Write the header for a section of the history file.
15181133e27eSPeter Avalos  */
15191133e27eSPeter Avalos #if CMD_HISTORY
write_mlist_header(struct mlist * ml,FILE * f)1520320d7c8aSAaron LI static void write_mlist_header(struct mlist *ml, FILE *f)
15211133e27eSPeter Avalos {
1522fa0be7c5SJohn Marino 	if (ml == &mlist_search)
1523fa0be7c5SJohn Marino 		fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1524fa0be7c5SJohn Marino #if SHELL_ESCAPE || PIPEC
1525fa0be7c5SJohn Marino 	else if (ml == &mlist_shell)
1526fa0be7c5SJohn Marino 		fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1527fa0be7c5SJohn Marino #endif
15281133e27eSPeter Avalos }
1529fa0be7c5SJohn Marino 
1530fa0be7c5SJohn Marino /*
1531fa0be7c5SJohn Marino  * Write all modified entries in an mlist to the history file.
1532fa0be7c5SJohn Marino  */
write_mlist(struct mlist * ml,FILE * f)1533320d7c8aSAaron LI static void write_mlist(struct mlist *ml, FILE *f)
1534fa0be7c5SJohn Marino {
15351133e27eSPeter Avalos 	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1536fa0be7c5SJohn Marino 	{
1537fa0be7c5SJohn Marino 		if (!ml->modified)
1538fa0be7c5SJohn Marino 			continue;
15391133e27eSPeter Avalos 		fprintf(f, "\"%s\n", ml->string);
1540*e433da38SAaron LI 		ml->modified = FALSE;
1541fa0be7c5SJohn Marino 	}
1542*e433da38SAaron LI 	ml->modified = FALSE; /* entire mlist is now unmodified */
1543fa0be7c5SJohn Marino }
1544fa0be7c5SJohn Marino 
1545fa0be7c5SJohn Marino /*
1546fa0be7c5SJohn Marino  * Make a temp name in the same directory as filename.
1547fa0be7c5SJohn Marino  */
make_tempname(constant char * filename)1548*e433da38SAaron LI static char * make_tempname(constant char *filename)
1549fa0be7c5SJohn Marino {
1550fa0be7c5SJohn Marino 	char lastch;
1551fa0be7c5SJohn Marino 	char *tempname = ecalloc(1, strlen(filename)+1);
1552fa0be7c5SJohn Marino 	strcpy(tempname, filename);
1553fa0be7c5SJohn Marino 	lastch = tempname[strlen(tempname)-1];
1554fa0be7c5SJohn Marino 	tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q';
1555fa0be7c5SJohn Marino 	return tempname;
1556fa0be7c5SJohn Marino }
1557fa0be7c5SJohn Marino 
1558fa0be7c5SJohn Marino struct save_ctx
1559fa0be7c5SJohn Marino {
1560fa0be7c5SJohn Marino 	struct mlist *mlist;
1561fa0be7c5SJohn Marino 	FILE *fout;
1562fa0be7c5SJohn Marino };
1563fa0be7c5SJohn Marino 
1564fa0be7c5SJohn Marino /*
1565fa0be7c5SJohn Marino  * Copy entries from the saved history file to a new file.
1566fa0be7c5SJohn Marino  * At the end of each mlist, append any new entries
1567fa0be7c5SJohn Marino  * created during this session.
1568fa0be7c5SJohn Marino  */
copy_hist(void * uparam,struct mlist * ml,constant char * string)1569*e433da38SAaron LI static void copy_hist(void *uparam, struct mlist *ml, constant char *string)
1570fa0be7c5SJohn Marino {
1571fa0be7c5SJohn Marino 	struct save_ctx *ctx = (struct save_ctx *) uparam;
1572fa0be7c5SJohn Marino 
157302d62a0fSDaniel Fojt 	if (ml != NULL && ml != ctx->mlist) {
1574fa0be7c5SJohn Marino 		/* We're changing mlists. */
1575fa0be7c5SJohn Marino 		if (ctx->mlist)
1576fa0be7c5SJohn Marino 			/* Append any new entries to the end of the current mlist. */
1577fa0be7c5SJohn Marino 			write_mlist(ctx->mlist, ctx->fout);
1578fa0be7c5SJohn Marino 		/* Write the header for the new mlist. */
1579fa0be7c5SJohn Marino 		ctx->mlist = ml;
1580fa0be7c5SJohn Marino 		write_mlist_header(ctx->mlist, ctx->fout);
1581fa0be7c5SJohn Marino 	}
158202d62a0fSDaniel Fojt 
158302d62a0fSDaniel Fojt 	if (string == NULL) /* End of file */
1584fa0be7c5SJohn Marino 	{
1585fa0be7c5SJohn Marino 		/* Write any sections that were not in the original file. */
1586fa0be7c5SJohn Marino 		if (mlist_search.modified)
1587fa0be7c5SJohn Marino 		{
1588fa0be7c5SJohn Marino 			write_mlist_header(&mlist_search, ctx->fout);
1589fa0be7c5SJohn Marino 			write_mlist(&mlist_search, ctx->fout);
1590fa0be7c5SJohn Marino 		}
1591fa0be7c5SJohn Marino #if SHELL_ESCAPE || PIPEC
1592fa0be7c5SJohn Marino 		if (mlist_shell.modified)
1593fa0be7c5SJohn Marino 		{
1594fa0be7c5SJohn Marino 			write_mlist_header(&mlist_shell, ctx->fout);
1595fa0be7c5SJohn Marino 			write_mlist(&mlist_shell, ctx->fout);
1596fa0be7c5SJohn Marino 		}
1597fa0be7c5SJohn Marino #endif
159802d62a0fSDaniel Fojt 	} else if (ml != NULL)
159902d62a0fSDaniel Fojt 	{
160002d62a0fSDaniel Fojt 		/* Copy mlist entry. */
160102d62a0fSDaniel Fojt 		fprintf(ctx->fout, "\"%s\n", string);
1602fa0be7c5SJohn Marino 	}
160302d62a0fSDaniel Fojt 	/* Skip marks */
16041133e27eSPeter Avalos }
16051133e27eSPeter Avalos #endif /* CMD_HISTORY */
16061133e27eSPeter Avalos 
16071133e27eSPeter Avalos /*
1608fa0be7c5SJohn Marino  * Make a file readable only by its owner.
16091133e27eSPeter Avalos  */
make_file_private(FILE * f)1610320d7c8aSAaron LI static void make_file_private(FILE *f)
16118be36e5bSPeter Avalos {
1612fa0be7c5SJohn Marino #if HAVE_FCHMOD
1613*e433da38SAaron LI 	lbool do_chmod = TRUE;
16148be36e5bSPeter Avalos #if HAVE_STAT
16158be36e5bSPeter Avalos 	struct stat statbuf;
16168be36e5bSPeter Avalos 	int r = fstat(fileno(f), &statbuf);
16178be36e5bSPeter Avalos 	if (r < 0 || !S_ISREG(statbuf.st_mode))
16188be36e5bSPeter Avalos 		/* Don't chmod if not a regular file. */
1619*e433da38SAaron LI 		do_chmod = FALSE;
16208be36e5bSPeter Avalos #endif
16218be36e5bSPeter Avalos 	if (do_chmod)
16221133e27eSPeter Avalos 		fchmod(fileno(f), 0600);
1623fa0be7c5SJohn Marino #endif
16248be36e5bSPeter Avalos }
16251133e27eSPeter Avalos 
1626fa0be7c5SJohn Marino /*
1627fa0be7c5SJohn Marino  * Does the history file need to be updated?
1628fa0be7c5SJohn Marino  */
16290c7ad07eSAntonio Huete Jimenez #if CMD_HISTORY
histfile_modified(void)1630*e433da38SAaron LI static lbool histfile_modified(void)
1631fa0be7c5SJohn Marino {
1632fa0be7c5SJohn Marino 	if (mlist_search.modified)
1633*e433da38SAaron LI 		return TRUE;
16341133e27eSPeter Avalos #if SHELL_ESCAPE || PIPEC
1635fa0be7c5SJohn Marino 	if (mlist_shell.modified)
1636*e433da38SAaron LI 		return TRUE;
16371133e27eSPeter Avalos #endif
163802d62a0fSDaniel Fojt 	if (marks_modified)
1639*e433da38SAaron LI 		return TRUE;
1640*e433da38SAaron LI 	return FALSE;
1641fa0be7c5SJohn Marino }
16420c7ad07eSAntonio Huete Jimenez #endif
16431133e27eSPeter Avalos 
1644fa0be7c5SJohn Marino /*
1645fa0be7c5SJohn Marino  * Update the .lesshst file.
1646fa0be7c5SJohn Marino  */
save_cmdhist(void)1647320d7c8aSAaron LI public void save_cmdhist(void)
1648fa0be7c5SJohn Marino {
1649fa0be7c5SJohn Marino #if CMD_HISTORY
1650fa0be7c5SJohn Marino 	char *histname;
1651fa0be7c5SJohn Marino 	char *tempname;
1652fa0be7c5SJohn Marino 	int skip_search;
1653fa0be7c5SJohn Marino 	int skip_shell;
1654fa0be7c5SJohn Marino 	struct save_ctx ctx;
1655*e433da38SAaron LI 	constant char *s;
1656fa0be7c5SJohn Marino 	FILE *fout = NULL;
1657fa0be7c5SJohn Marino 	int histsize = 0;
1658fa0be7c5SJohn Marino 
1659*e433da38SAaron LI 	if (!secure_allow(SF_HISTORY) || !histfile_modified())
1660fa0be7c5SJohn Marino 		return;
16610c7ad07eSAntonio Huete Jimenez 	histname = histfile_name(0);
1662fa0be7c5SJohn Marino 	if (histname == NULL)
1663fa0be7c5SJohn Marino 		return;
1664fa0be7c5SJohn Marino 	tempname = make_tempname(histname);
1665fa0be7c5SJohn Marino 	fout = fopen(tempname, "w");
1666fa0be7c5SJohn Marino 	if (fout != NULL)
1667fa0be7c5SJohn Marino 	{
1668fa0be7c5SJohn Marino 		make_file_private(fout);
1669fa0be7c5SJohn Marino 		s = lgetenv("LESSHISTSIZE");
1670fa0be7c5SJohn Marino 		if (s != NULL)
1671fa0be7c5SJohn Marino 			histsize = atoi(s);
1672fa0be7c5SJohn Marino 		if (histsize <= 0)
1673fa0be7c5SJohn Marino 			histsize = 100;
1674fa0be7c5SJohn Marino 		skip_search = mlist_size(&mlist_search) - histsize;
1675fa0be7c5SJohn Marino #if SHELL_ESCAPE || PIPEC
1676fa0be7c5SJohn Marino 		skip_shell = mlist_size(&mlist_shell) - histsize;
1677fa0be7c5SJohn Marino #endif
1678fa0be7c5SJohn Marino 		fprintf(fout, "%s\n", HISTFILE_FIRST_LINE);
1679fa0be7c5SJohn Marino 		ctx.fout = fout;
1680fa0be7c5SJohn Marino 		ctx.mlist = NULL;
168102d62a0fSDaniel Fojt 		read_cmdhist(&copy_hist, &ctx, skip_search, skip_shell);
168202d62a0fSDaniel Fojt 		save_marks(fout, HISTFILE_MARK_SECTION);
1683fa0be7c5SJohn Marino 		fclose(fout);
1684fa0be7c5SJohn Marino #if MSDOS_COMPILER==WIN32C
1685fa0be7c5SJohn Marino 		/*
1686fa0be7c5SJohn Marino 		 * Windows rename doesn't remove an existing file,
1687fa0be7c5SJohn Marino 		 * making it useless for atomic operations. Sigh.
1688fa0be7c5SJohn Marino 		 */
1689fa0be7c5SJohn Marino 		remove(histname);
1690fa0be7c5SJohn Marino #endif
1691fa0be7c5SJohn Marino 		rename(tempname, histname);
1692fa0be7c5SJohn Marino 	}
1693fa0be7c5SJohn Marino 	free(tempname);
1694fa0be7c5SJohn Marino 	free(histname);
16951133e27eSPeter Avalos #endif /* CMD_HISTORY */
16961133e27eSPeter Avalos }
1697