cmdbuf.c revision 240121
1130803Smarcel/*
2130803Smarcel * Copyright (C) 1984-2012  Mark Nudelman
3130803Smarcel *
4130803Smarcel * You may distribute under the terms of either the GNU General Public
5130803Smarcel * License or the Less License, as specified in the README file.
6130803Smarcel *
7130803Smarcel * For more information, see the README file.
8130803Smarcel */
9130803Smarcel
10130803Smarcel
11130803Smarcel/*
12130803Smarcel * Functions which manipulate the command buffer.
13130803Smarcel * Used only by command() and related functions.
14130803Smarcel */
15130803Smarcel
16130803Smarcel#include "less.h"
17130803Smarcel#include "cmd.h"
18130803Smarcel#include "charset.h"
19130803Smarcel#if HAVE_STAT
20130803Smarcel#include <sys/stat.h>
21130803Smarcel#endif
22130803Smarcel
23130803Smarcelextern int sc_width;
24130803Smarcelextern int utf_mode;
25130803Smarcel
26130803Smarcelstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
27130803Smarcelstatic int cmd_col;		/* Current column of the cursor */
28130803Smarcelstatic int prompt_col;		/* Column of cursor just after prompt */
29130803Smarcelstatic char *cp;		/* Pointer into cmdbuf */
30130803Smarcelstatic int cmd_offset;		/* Index into cmdbuf of first displayed char */
31130803Smarcelstatic int literal;		/* Next input char should not be interpreted */
32130803Smarcelstatic int updown_match = -1;	/* Prefix length in up/down movement */
33130803Smarcel
34130803Smarcel#if TAB_COMPLETE_FILENAME
35130803Smarcelstatic int cmd_complete();
36130803Smarcel/*
37130803Smarcel * These variables are statics used by cmd_complete.
38130803Smarcel */
39130803Smarcelstatic int in_completion = 0;
40130803Smarcelstatic char *tk_text;
41130803Smarcelstatic char *tk_original;
42130803Smarcelstatic char *tk_ipoint;
43130803Smarcelstatic char *tk_trial;
44130803Smarcelstatic struct textlist tk_tlist;
45130803Smarcel#endif
46130803Smarcel
47130803Smarcelstatic int cmd_left();
48130803Smarcelstatic int cmd_right();
49130803Smarcel
50130803Smarcel#if SPACES_IN_FILENAMES
51130803Smarcelpublic char openquote = '"';
52130803Smarcelpublic char closequote = '"';
53130803Smarcel#endif
54130803Smarcel
55130803Smarcel#if CMD_HISTORY
56130803Smarcel
57130803Smarcel/* History file */
58130803Smarcel#define HISTFILE_FIRST_LINE      ".less-history-file:"
59130803Smarcel#define HISTFILE_SEARCH_SECTION  ".search"
60130803Smarcel#define HISTFILE_SHELL_SECTION   ".shell"
61130803Smarcel
62130803Smarcel/*
63130803Smarcel * A mlist structure represents a command history.
64130803Smarcel */
65130803Smarcelstruct mlist
66130803Smarcel{
67130803Smarcel	struct mlist *next;
68130803Smarcel	struct mlist *prev;
69130803Smarcel	struct mlist *curr_mp;
70130803Smarcel	char *string;
71130803Smarcel	int modified;
72130803Smarcel};
73130803Smarcel
74130803Smarcel/*
75130803Smarcel * These are the various command histories that exist.
76130803Smarcel */
77130803Smarcelstruct mlist mlist_search =
78130803Smarcel	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
79130803Smarcelpublic void * constant ml_search = (void *) &mlist_search;
80130803Smarcel
81130803Smarcelstruct mlist mlist_examine =
82130803Smarcel	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
83130803Smarcelpublic void * constant ml_examine = (void *) &mlist_examine;
84130803Smarcel
85130803Smarcel#if SHELL_ESCAPE || PIPEC
86130803Smarcelstruct mlist mlist_shell =
87130803Smarcel	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
88130803Smarcelpublic void * constant ml_shell = (void *) &mlist_shell;
89130803Smarcel#endif
90130803Smarcel
91130803Smarcel#else /* CMD_HISTORY */
92130803Smarcel
93130803Smarcel/* If CMD_HISTORY is off, these are just flags. */
94130803Smarcelpublic void * constant ml_search = (void *)1;
95130803Smarcelpublic void * constant ml_examine = (void *)2;
96130803Smarcel#if SHELL_ESCAPE || PIPEC
97130803Smarcelpublic void * constant ml_shell = (void *)3;
98130803Smarcel#endif
99130803Smarcel
100130803Smarcel#endif /* CMD_HISTORY */
101130803Smarcel
102130803Smarcel/*
103130803Smarcel * History for the current command.
104130803Smarcel */
105130803Smarcelstatic struct mlist *curr_mlist = NULL;
106130803Smarcelstatic int curr_cmdflags;
107130803Smarcel
108130803Smarcelstatic char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
109130803Smarcelstatic int cmd_mbc_buf_len;
110130803Smarcelstatic int cmd_mbc_buf_index;
111130803Smarcel
112130803Smarcel
113130803Smarcel/*
114130803Smarcel * Reset command buffer (to empty).
115130803Smarcel */
116130803Smarcel	public void
117130803Smarcelcmd_reset()
118130803Smarcel{
119130803Smarcel	cp = cmdbuf;
120130803Smarcel	*cp = '\0';
121130803Smarcel	cmd_col = 0;
122130803Smarcel	cmd_offset = 0;
123130803Smarcel	literal = 0;
124130803Smarcel	cmd_mbc_buf_len = 0;
125130803Smarcel	updown_match = -1;
126130803Smarcel}
127130803Smarcel
128130803Smarcel/*
129130803Smarcel * Clear command line.
130130803Smarcel */
131130803Smarcel	public void
132130803Smarcelclear_cmd()
133130803Smarcel{
134130803Smarcel	cmd_col = prompt_col = 0;
135130803Smarcel	cmd_mbc_buf_len = 0;
136130803Smarcel	updown_match = -1;
137130803Smarcel}
138130803Smarcel
139130803Smarcel/*
140130803Smarcel * Display a string, usually as a prompt for input into the command buffer.
141130803Smarcel */
142130803Smarcel	public void
143130803Smarcelcmd_putstr(s)
144130803Smarcel	char *s;
145130803Smarcel{
146130803Smarcel	LWCHAR prev_ch = 0;
147130803Smarcel	LWCHAR ch;
148130803Smarcel	char *endline = s + strlen(s);
149130803Smarcel	while (*s != '\0')
150130803Smarcel	{
151130803Smarcel		char *ns = s;
152130803Smarcel		ch = step_char(&ns, +1, endline);
153130803Smarcel		while (s < ns)
154130803Smarcel			putchr(*s++);
155130803Smarcel		if (!utf_mode)
156130803Smarcel		{
157130803Smarcel			cmd_col++;
158130803Smarcel			prompt_col++;
159130803Smarcel		} else if (!is_composing_char(ch) &&
160130803Smarcel		           !is_combining_char(prev_ch, ch))
161130803Smarcel		{
162130803Smarcel			int width = is_wide_char(ch) ? 2 : 1;
163130803Smarcel			cmd_col += width;
164130803Smarcel			prompt_col += width;
165130803Smarcel		}
166130803Smarcel		prev_ch = ch;
167130803Smarcel	}
168130803Smarcel}
169130803Smarcel
170130803Smarcel/*
171130803Smarcel * How many characters are in the command buffer?
172130803Smarcel */
173130803Smarcel	public int
174130803Smarcellen_cmdbuf()
175130803Smarcel{
176130803Smarcel	char *s = cmdbuf;
177130803Smarcel	char *endline = s + strlen(s);
178130803Smarcel	int len = 0;
179130803Smarcel
180130803Smarcel	while (*s != '\0')
181130803Smarcel	{
182130803Smarcel		step_char(&s, +1, endline);
183130803Smarcel		len++;
184130803Smarcel	}
185130803Smarcel	return (len);
186130803Smarcel}
187130803Smarcel
188130803Smarcel/*
189130803Smarcel * Common part of cmd_step_right() and cmd_step_left().
190130803Smarcel */
191130803Smarcel	static char *
192130803Smarcelcmd_step_common(p, ch, len, pwidth, bswidth)
193130803Smarcel	char *p;
194130803Smarcel	LWCHAR ch;
195130803Smarcel	int len;
196130803Smarcel	int *pwidth;
197130803Smarcel	int *bswidth;
198130803Smarcel{
199130803Smarcel	char *pr;
200130803Smarcel
201130803Smarcel	if (len == 1)
202130803Smarcel	{
203130803Smarcel		pr = prchar((int) ch);
204130803Smarcel		if (pwidth != NULL || bswidth != NULL)
205130803Smarcel		{
206130803Smarcel			int len = strlen(pr);
207130803Smarcel			if (pwidth != NULL)
208130803Smarcel				*pwidth = len;
209130803Smarcel			if (bswidth != NULL)
210130803Smarcel				*bswidth = len;
211130803Smarcel		}
212130803Smarcel	} else
213130803Smarcel	{
214130803Smarcel		pr = prutfchar(ch);
215130803Smarcel		if (pwidth != NULL || bswidth != NULL)
216130803Smarcel		{
217130803Smarcel			if (is_composing_char(ch))
218130803Smarcel			{
219130803Smarcel				if (pwidth != NULL)
220130803Smarcel					*pwidth = 0;
221130803Smarcel				if (bswidth != NULL)
222130803Smarcel					*bswidth = 0;
223130803Smarcel			} else if (is_ubin_char(ch))
224130803Smarcel			{
225130803Smarcel				int len = strlen(pr);
226130803Smarcel				if (pwidth != NULL)
227130803Smarcel					*pwidth = len;
228130803Smarcel				if (bswidth != NULL)
229130803Smarcel					*bswidth = len;
230130803Smarcel			} else
231130803Smarcel			{
232130803Smarcel				LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
233130803Smarcel				if (is_combining_char(prev_ch, ch))
234130803Smarcel				{
235130803Smarcel					if (pwidth != NULL)
236130803Smarcel						*pwidth = 0;
237130803Smarcel					if (bswidth != NULL)
238130803Smarcel						*bswidth = 0;
239130803Smarcel				} else
240130803Smarcel				{
241130803Smarcel					if (pwidth != NULL)
242130803Smarcel						*pwidth	= is_wide_char(ch)
243130803Smarcel							?	2
244130803Smarcel							:	1;
245130803Smarcel					if (bswidth != NULL)
246130803Smarcel						*bswidth = 1;
247130803Smarcel				}
248130803Smarcel			}
249130803Smarcel		}
250130803Smarcel	}
251130803Smarcel
252130803Smarcel	return (pr);
253130803Smarcel}
254130803Smarcel
255130803Smarcel/*
256130803Smarcel * Step a pointer one character right in the command buffer.
257130803Smarcel */
258130803Smarcel	static char *
259130803Smarcelcmd_step_right(pp, pwidth, bswidth)
260130803Smarcel	char **pp;
261130803Smarcel	int *pwidth;
262130803Smarcel	int *bswidth;
263130803Smarcel{
264130803Smarcel	char *p = *pp;
265130803Smarcel	LWCHAR ch = step_char(pp, +1, p + strlen(p));
266130803Smarcel
267130803Smarcel	return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
268130803Smarcel}
269130803Smarcel
270130803Smarcel/*
271130803Smarcel * Step a pointer one character left in the command buffer.
272130803Smarcel */
273130803Smarcel	static char *
274130803Smarcelcmd_step_left(pp, pwidth, bswidth)
275130803Smarcel	char **pp;
276130803Smarcel	int *pwidth;
277130803Smarcel	int *bswidth;
278130803Smarcel{
279130803Smarcel	char *p = *pp;
280130803Smarcel	LWCHAR ch = step_char(pp, -1, cmdbuf);
281130803Smarcel
282130803Smarcel	return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
283130803Smarcel}
284130803Smarcel
285130803Smarcel/*
286130803Smarcel * Repaint the line from cp onwards.
287130803Smarcel * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
288130803Smarcel */
289130803Smarcel	static void
290130803Smarcelcmd_repaint(old_cp)
291130803Smarcel	char *old_cp;
292130803Smarcel{
293130803Smarcel	/*
294130803Smarcel	 * Repaint the line from the current position.
295130803Smarcel	 */
296130803Smarcel	clear_eol();
297130803Smarcel	while (*cp != '\0')
298130803Smarcel	{
299130803Smarcel		char *np = cp;
300130803Smarcel		int width;
301130803Smarcel		char *pr = cmd_step_right(&np, &width, NULL);
302130803Smarcel		if (cmd_col + width >= sc_width)
303130803Smarcel			break;
304130803Smarcel		cp = np;
305130803Smarcel		putstr(pr);
306130803Smarcel		cmd_col += width;
307130803Smarcel	}
308130803Smarcel	while (*cp != '\0')
309130803Smarcel	{
310130803Smarcel		char *np = cp;
311130803Smarcel		int width;
312130803Smarcel		char *pr = cmd_step_right(&np, &width, NULL);
313130803Smarcel		if (width > 0)
314130803Smarcel			break;
315130803Smarcel		cp = np;
316130803Smarcel		putstr(pr);
317130803Smarcel	}
318130803Smarcel
319130803Smarcel	/*
320130803Smarcel	 * Back up the cursor to the correct position.
321130803Smarcel	 */
322130803Smarcel	while (cp > old_cp)
323130803Smarcel		cmd_left();
324130803Smarcel}
325130803Smarcel
326130803Smarcel/*
327130803Smarcel * Put the cursor at "home" (just after the prompt),
328130803Smarcel * and set cp to the corresponding char in cmdbuf.
329130803Smarcel */
330130803Smarcel	static void
331130803Smarcelcmd_home()
332130803Smarcel{
333130803Smarcel	while (cmd_col > prompt_col)
334130803Smarcel	{
335130803Smarcel		int width, bswidth;
336130803Smarcel
337130803Smarcel		cmd_step_left(&cp, &width, &bswidth);
338130803Smarcel		while (bswidth-- > 0)
339130803Smarcel			putbs();
340130803Smarcel		cmd_col -= width;
341130803Smarcel	}
342130803Smarcel
343130803Smarcel	cp = &cmdbuf[cmd_offset];
344130803Smarcel}
345130803Smarcel
346130803Smarcel/*
347130803Smarcel * Shift the cmdbuf display left a half-screen.
348130803Smarcel */
349130803Smarcel	static void
350130803Smarcelcmd_lshift()
351130803Smarcel{
352130803Smarcel	char *s;
353130803Smarcel	char *save_cp;
354130803Smarcel	int cols;
355130803Smarcel
356130803Smarcel	/*
357130803Smarcel	 * Start at the first displayed char, count how far to the
358130803Smarcel	 * right we'd have to move to reach the center of the screen.
359130803Smarcel	 */
360130803Smarcel	s = cmdbuf + cmd_offset;
361130803Smarcel	cols = 0;
362130803Smarcel	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
363130803Smarcel	{
364130803Smarcel		int width;
365130803Smarcel		cmd_step_right(&s, &width, NULL);
366130803Smarcel		cols += width;
367130803Smarcel	}
368130803Smarcel	while (*s != '\0')
369130803Smarcel	{
370130803Smarcel		int width;
371130803Smarcel		char *ns = s;
372130803Smarcel		cmd_step_right(&ns, &width, NULL);
373130803Smarcel		if (width > 0)
374130803Smarcel			break;
375130803Smarcel		s = ns;
376130803Smarcel	}
377130803Smarcel
378130803Smarcel	cmd_offset = s - cmdbuf;
379130803Smarcel	save_cp = cp;
380130803Smarcel	cmd_home();
381130803Smarcel	cmd_repaint(save_cp);
382130803Smarcel}
383130803Smarcel
384130803Smarcel/*
385130803Smarcel * Shift the cmdbuf display right a half-screen.
386130803Smarcel */
387130803Smarcel	static void
388130803Smarcelcmd_rshift()
389130803Smarcel{
390130803Smarcel	char *s;
391130803Smarcel	char *save_cp;
392130803Smarcel	int cols;
393130803Smarcel
394130803Smarcel	/*
395130803Smarcel	 * Start at the first displayed char, count how far to the
396130803Smarcel	 * left we'd have to move to traverse a half-screen width
397130803Smarcel	 * of displayed characters.
398130803Smarcel	 */
399130803Smarcel	s = cmdbuf + cmd_offset;
400130803Smarcel	cols = 0;
401130803Smarcel	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
402130803Smarcel	{
403130803Smarcel		int width;
404130803Smarcel		cmd_step_left(&s, &width, NULL);
405130803Smarcel		cols += width;
406130803Smarcel	}
407130803Smarcel
408130803Smarcel	cmd_offset = s - cmdbuf;
409130803Smarcel	save_cp = cp;
410130803Smarcel	cmd_home();
411130803Smarcel	cmd_repaint(save_cp);
412130803Smarcel}
413130803Smarcel
414130803Smarcel/*
415130803Smarcel * Move cursor right one character.
416130803Smarcel */
417130803Smarcel	static int
418130803Smarcelcmd_right()
419130803Smarcel{
420130803Smarcel	char *pr;
421130803Smarcel	char *ncp;
422130803Smarcel	int width;
423130803Smarcel
424130803Smarcel	if (*cp == '\0')
425130803Smarcel	{
426130803Smarcel		/* Already at the end of the line. */
427130803Smarcel		return (CC_OK);
428130803Smarcel	}
429130803Smarcel	ncp = cp;
430130803Smarcel	pr = cmd_step_right(&ncp, &width, NULL);
431130803Smarcel	if (cmd_col + width >= sc_width)
432130803Smarcel		cmd_lshift();
433130803Smarcel	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
434130803Smarcel		cmd_lshift();
435130803Smarcel	cp = ncp;
436130803Smarcel	cmd_col += width;
437130803Smarcel	putstr(pr);
438130803Smarcel	while (*cp != '\0')
439130803Smarcel	{
440130803Smarcel		pr = cmd_step_right(&ncp, &width, NULL);
441130803Smarcel		if (width > 0)
442130803Smarcel			break;
443130803Smarcel		putstr(pr);
444130803Smarcel		cp = ncp;
445130803Smarcel	}
446130803Smarcel	return (CC_OK);
447130803Smarcel}
448130803Smarcel
449130803Smarcel/*
450130803Smarcel * Move cursor left one character.
451130803Smarcel */
452130803Smarcel	static int
453130803Smarcelcmd_left()
454130803Smarcel{
455130803Smarcel	char *ncp;
456130803Smarcel	int width, bswidth;
457130803Smarcel
458130803Smarcel	if (cp <= cmdbuf)
459130803Smarcel	{
460130803Smarcel		/* Already at the beginning of the line */
461130803Smarcel		return (CC_OK);
462130803Smarcel	}
463130803Smarcel	ncp = cp;
464130803Smarcel	while (ncp > cmdbuf)
465130803Smarcel	{
466130803Smarcel		cmd_step_left(&ncp, &width, &bswidth);
467130803Smarcel		if (width > 0)
468130803Smarcel			break;
469130803Smarcel	}
470130803Smarcel	if (cmd_col < prompt_col + width)
471130803Smarcel		cmd_rshift();
472130803Smarcel	cp = ncp;
473130803Smarcel	cmd_col -= width;
474130803Smarcel	while (bswidth-- > 0)
475130803Smarcel		putbs();
476130803Smarcel	return (CC_OK);
477130803Smarcel}
478130803Smarcel
479130803Smarcel/*
480130803Smarcel * Insert a char into the command buffer, at the current position.
481130803Smarcel */
482130803Smarcel	static int
483130803Smarcelcmd_ichar(cs, clen)
484130803Smarcel	char *cs;
485130803Smarcel	int clen;
486130803Smarcel{
487130803Smarcel	char *s;
488130803Smarcel
489130803Smarcel	if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
490130803Smarcel	{
491130803Smarcel		/* No room in the command buffer for another char. */
492130803Smarcel		bell();
493130803Smarcel		return (CC_ERROR);
494130803Smarcel	}
495130803Smarcel
496130803Smarcel	/*
497130803Smarcel	 * Make room for the new character (shift the tail of the buffer right).
498130803Smarcel	 */
499130803Smarcel	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
500130803Smarcel		s[clen] = s[0];
501130803Smarcel	/*
502130803Smarcel	 * Insert the character into the buffer.
503130803Smarcel	 */
504130803Smarcel	for (s = cp;  s < cp + clen;  s++)
505130803Smarcel		*s = *cs++;
506130803Smarcel	/*
507130803Smarcel	 * Reprint the tail of the line from the inserted char.
508130803Smarcel	 */
509130803Smarcel	updown_match = -1;
510130803Smarcel	cmd_repaint(cp);
511130803Smarcel	cmd_right();
512130803Smarcel	return (CC_OK);
513130803Smarcel}
514130803Smarcel
515130803Smarcel/*
516130803Smarcel * Backspace in the command buffer.
517130803Smarcel * Delete the char to the left of the cursor.
518130803Smarcel */
519130803Smarcel	static int
520130803Smarcelcmd_erase()
521130803Smarcel{
522130803Smarcel	register char *s;
523130803Smarcel	int clen;
524130803Smarcel
525130803Smarcel	if (cp == cmdbuf)
526130803Smarcel	{
527130803Smarcel		/*
528130803Smarcel		 * Backspace past beginning of the buffer:
529130803Smarcel		 * this usually means abort the command.
530130803Smarcel		 */
531130803Smarcel		return (CC_QUIT);
532130803Smarcel	}
533130803Smarcel	/*
534130803Smarcel	 * Move cursor left (to the char being erased).
535130803Smarcel	 */
536130803Smarcel	s = cp;
537130803Smarcel	cmd_left();
538130803Smarcel	clen = s - cp;
539130803Smarcel
540130803Smarcel	/*
541130803Smarcel	 * Remove the char from the buffer (shift the buffer left).
542130803Smarcel	 */
543130803Smarcel	for (s = cp;  ;  s++)
544130803Smarcel	{
545130803Smarcel		s[0] = s[clen];
546130803Smarcel		if (s[0] == '\0')
547130803Smarcel			break;
548130803Smarcel	}
549130803Smarcel
550130803Smarcel	/*
551130803Smarcel	 * Repaint the buffer after the erased char.
552130803Smarcel	 */
553130803Smarcel	updown_match = -1;
554130803Smarcel	cmd_repaint(cp);
555130803Smarcel
556130803Smarcel	/*
557130803Smarcel	 * We say that erasing the entire command string causes us
558130803Smarcel	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
559130803Smarcel	 */
560130803Smarcel	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
561130803Smarcel		return (CC_QUIT);
562130803Smarcel	return (CC_OK);
563130803Smarcel}
564130803Smarcel
565130803Smarcel/*
566130803Smarcel * Delete the char under the cursor.
567130803Smarcel */
568130803Smarcel	static int
569130803Smarcelcmd_delete()
570130803Smarcel{
571130803Smarcel	if (*cp == '\0')
572130803Smarcel	{
573130803Smarcel		/* At end of string; there is no char under the cursor. */
574130803Smarcel		return (CC_OK);
575130803Smarcel	}
576130803Smarcel	/*
577130803Smarcel	 * Move right, then use cmd_erase.
578130803Smarcel	 */
579130803Smarcel	cmd_right();
580130803Smarcel	cmd_erase();
581130803Smarcel	return (CC_OK);
582130803Smarcel}
583130803Smarcel
584130803Smarcel/*
585130803Smarcel * Delete the "word" to the left of the cursor.
586130803Smarcel */
587130803Smarcel	static int
588130803Smarcelcmd_werase()
589130803Smarcel{
590130803Smarcel	if (cp > cmdbuf && cp[-1] == ' ')
591130803Smarcel	{
592130803Smarcel		/*
593130803Smarcel		 * If the char left of cursor is a space,
594130803Smarcel		 * erase all the spaces left of cursor (to the first non-space).
595130803Smarcel		 */
596130803Smarcel		while (cp > cmdbuf && cp[-1] == ' ')
597130803Smarcel			(void) cmd_erase();
598130803Smarcel	} else
599130803Smarcel	{
600130803Smarcel		/*
601130803Smarcel		 * If the char left of cursor is not a space,
602130803Smarcel		 * erase all the nonspaces left of cursor (the whole "word").
603130803Smarcel		 */
604130803Smarcel		while (cp > cmdbuf && cp[-1] != ' ')
605130803Smarcel			(void) cmd_erase();
606130803Smarcel	}
607130803Smarcel	return (CC_OK);
608130803Smarcel}
609130803Smarcel
610130803Smarcel/*
611130803Smarcel * Delete the "word" under the cursor.
612130803Smarcel */
613130803Smarcel	static int
614130803Smarcelcmd_wdelete()
615130803Smarcel{
616130803Smarcel	if (*cp == ' ')
617130803Smarcel	{
618130803Smarcel		/*
619130803Smarcel		 * If the char under the cursor is a space,
620130803Smarcel		 * delete it and all the spaces right of cursor.
621130803Smarcel		 */
622130803Smarcel		while (*cp == ' ')
623130803Smarcel			(void) cmd_delete();
624130803Smarcel	} else
625130803Smarcel	{
626130803Smarcel		/*
627130803Smarcel		 * If the char under the cursor is not a space,
628130803Smarcel		 * delete it and all nonspaces right of cursor (the whole word).
629130803Smarcel		 */
630130803Smarcel		while (*cp != ' ' && *cp != '\0')
631130803Smarcel			(void) cmd_delete();
632130803Smarcel	}
633130803Smarcel	return (CC_OK);
634130803Smarcel}
635130803Smarcel
636130803Smarcel/*
637130803Smarcel * Delete all chars in the command buffer.
638130803Smarcel */
639130803Smarcel	static int
640130803Smarcelcmd_kill()
641130803Smarcel{
642130803Smarcel	if (cmdbuf[0] == '\0')
643130803Smarcel	{
644130803Smarcel		/* Buffer is already empty; abort the current command. */
645130803Smarcel		return (CC_QUIT);
646130803Smarcel	}
647130803Smarcel	cmd_offset = 0;
648130803Smarcel	cmd_home();
649130803Smarcel	*cp = '\0';
650130803Smarcel	updown_match = -1;
651130803Smarcel	cmd_repaint(cp);
652130803Smarcel
653130803Smarcel	/*
654130803Smarcel	 * We say that erasing the entire command string causes us
655130803Smarcel	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
656130803Smarcel	 */
657130803Smarcel	if (curr_cmdflags & CF_QUIT_ON_ERASE)
658130803Smarcel		return (CC_QUIT);
659130803Smarcel	return (CC_OK);
660130803Smarcel}
661130803Smarcel
662130803Smarcel/*
663130803Smarcel * Select an mlist structure to be the current command history.
664130803Smarcel */
665130803Smarcel	public void
666130803Smarcelset_mlist(mlist, cmdflags)
667130803Smarcel	void *mlist;
668130803Smarcel	int cmdflags;
669130803Smarcel{
670130803Smarcel#if CMD_HISTORY
671130803Smarcel	curr_mlist = (struct mlist *) mlist;
672130803Smarcel	curr_cmdflags = cmdflags;
673130803Smarcel
674130803Smarcel	/* Make sure the next up-arrow moves to the last string in the mlist. */
675130803Smarcel	if (curr_mlist != NULL)
676130803Smarcel		curr_mlist->curr_mp = curr_mlist;
677130803Smarcel#endif
678130803Smarcel}
679130803Smarcel
680130803Smarcel#if CMD_HISTORY
681130803Smarcel/*
682130803Smarcel * Move up or down in the currently selected command history list.
683130803Smarcel * Only consider entries whose first updown_match chars are equal to
684130803Smarcel * cmdbuf's corresponding chars.
685130803Smarcel */
686130803Smarcel	static int
687130803Smarcelcmd_updown(action)
688130803Smarcel	int action;
689130803Smarcel{
690130803Smarcel	char *s;
691130803Smarcel	struct mlist *ml;
692130803Smarcel
693130803Smarcel	if (curr_mlist == NULL)
694130803Smarcel	{
695130803Smarcel		/*
696130803Smarcel		 * The current command has no history list.
697130803Smarcel		 */
698130803Smarcel		bell();
699130803Smarcel		return (CC_OK);
700130803Smarcel	}
701130803Smarcel
702130803Smarcel	if (updown_match < 0)
703130803Smarcel	{
704130803Smarcel		updown_match = cp - cmdbuf;
705130803Smarcel	}
706130803Smarcel
707130803Smarcel	/*
708130803Smarcel	 * Find the next history entry which matches.
709130803Smarcel	 */
710130803Smarcel	for (ml = curr_mlist->curr_mp;;)
711130803Smarcel	{
712130803Smarcel		ml = (action == EC_UP) ? ml->prev : ml->next;
713130803Smarcel		if (ml == curr_mlist)
714130803Smarcel		{
715130803Smarcel			/*
716130803Smarcel			 * We reached the end (or beginning) of the list.
717130803Smarcel			 */
718130803Smarcel			break;
719130803Smarcel		}
720130803Smarcel		if (strncmp(cmdbuf, ml->string, updown_match) == 0)
721130803Smarcel		{
722130803Smarcel			/*
723130803Smarcel			 * This entry matches; stop here.
724130803Smarcel			 * Copy the entry into cmdbuf and echo it on the screen.
725130803Smarcel			 */
726130803Smarcel			curr_mlist->curr_mp = ml;
727130803Smarcel			s = ml->string;
728130803Smarcel			if (s == NULL)
729130803Smarcel				s = "";
730130803Smarcel			strcpy(cmdbuf, s);
731130803Smarcel			cmd_home();
732130803Smarcel			clear_eol();
733130803Smarcel			for (cp = cmdbuf;  *cp != '\0';  )
734130803Smarcel				cmd_right();
735130803Smarcel			return (CC_OK);
736130803Smarcel		}
737130803Smarcel	}
738130803Smarcel	/*
739130803Smarcel	 * We didn't find a history entry that matches.
740130803Smarcel	 */
741130803Smarcel	bell();
742130803Smarcel	return (CC_OK);
743130803Smarcel}
744130803Smarcel#endif
745130803Smarcel
746130803Smarcel/*
747130803Smarcel * Add a string to a history list.
748130803Smarcel */
749130803Smarcel	public void
750130803Smarcelcmd_addhist(mlist, cmd)
751130803Smarcel	struct mlist *mlist;
752130803Smarcel	char *cmd;
753130803Smarcel{
754130803Smarcel#if CMD_HISTORY
755130803Smarcel	struct mlist *ml;
756130803Smarcel
757130803Smarcel	/*
758130803Smarcel	 * Don't save a trivial command.
759130803Smarcel	 */
760130803Smarcel	if (strlen(cmd) == 0)
761130803Smarcel		return;
762130803Smarcel
763130803Smarcel	/*
764130803Smarcel	 * Save the command unless it's a duplicate of the
765130803Smarcel	 * last command in the history.
766130803Smarcel	 */
767130803Smarcel	ml = mlist->prev;
768130803Smarcel	if (ml == mlist || strcmp(ml->string, cmd) != 0)
769130803Smarcel	{
770130803Smarcel		/*
771130803Smarcel		 * Did not find command in history.
772130803Smarcel		 * Save the command and put it at the end of the history list.
773130803Smarcel		 */
774130803Smarcel		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
775130803Smarcel		ml->string = save(cmd);
776130803Smarcel		ml->next = mlist;
777130803Smarcel		ml->prev = mlist->prev;
778130803Smarcel		mlist->prev->next = ml;
779130803Smarcel		mlist->prev = ml;
780130803Smarcel	}
781130803Smarcel	/*
782130803Smarcel	 * Point to the cmd just after the just-accepted command.
783130803Smarcel	 * Thus, an UPARROW will always retrieve the previous command.
784130803Smarcel	 */
785130803Smarcel	mlist->curr_mp = ml->next;
786130803Smarcel#endif
787130803Smarcel}
788130803Smarcel
789130803Smarcel/*
790130803Smarcel * Accept the command in the command buffer.
791130803Smarcel * Add it to the currently selected history list.
792130803Smarcel */
793130803Smarcel	public void
794130803Smarcelcmd_accept()
795130803Smarcel{
796130803Smarcel#if CMD_HISTORY
797130803Smarcel	/*
798130803Smarcel	 * Nothing to do if there is no currently selected history list.
799130803Smarcel	 */
800130803Smarcel	if (curr_mlist == NULL)
801130803Smarcel		return;
802130803Smarcel	cmd_addhist(curr_mlist, cmdbuf);
803130803Smarcel	curr_mlist->modified = 1;
804130803Smarcel#endif
805130803Smarcel}
806130803Smarcel
807130803Smarcel/*
808130803Smarcel * Try to perform a line-edit function on the command buffer,
809130803Smarcel * using a specified char as a line-editing command.
810130803Smarcel * Returns:
811130803Smarcel *	CC_PASS	The char does not invoke a line edit function.
812130803Smarcel *	CC_OK	Line edit function done.
813130803Smarcel *	CC_QUIT	The char requests the current command to be aborted.
814130803Smarcel */
815130803Smarcel	static int
816130803Smarcelcmd_edit(c)
817130803Smarcel	int c;
818130803Smarcel{
819130803Smarcel	int action;
820130803Smarcel	int flags;
821130803Smarcel
822130803Smarcel#if TAB_COMPLETE_FILENAME
823130803Smarcel#define	not_in_completion()	in_completion = 0
824130803Smarcel#else
825130803Smarcel#define	not_in_completion()
826130803Smarcel#endif
827130803Smarcel
828130803Smarcel	/*
829130803Smarcel	 * See if the char is indeed a line-editing command.
830130803Smarcel	 */
831130803Smarcel	flags = 0;
832130803Smarcel#if CMD_HISTORY
833130803Smarcel	if (curr_mlist == NULL)
834130803Smarcel		/*
835130803Smarcel		 * No current history; don't accept history manipulation cmds.
836130803Smarcel		 */
837130803Smarcel		flags |= EC_NOHISTORY;
838130803Smarcel#endif
839130803Smarcel#if TAB_COMPLETE_FILENAME
840130803Smarcel	if (curr_mlist == ml_search)
841130803Smarcel		/*
842130803Smarcel		 * In a search command; don't accept file-completion cmds.
843130803Smarcel		 */
844130803Smarcel		flags |= EC_NOCOMPLETE;
845130803Smarcel#endif
846130803Smarcel
847130803Smarcel	action = editchar(c, flags);
848130803Smarcel
849130803Smarcel	switch (action)
850130803Smarcel	{
851130803Smarcel	case EC_RIGHT:
852130803Smarcel		not_in_completion();
853130803Smarcel		return (cmd_right());
854130803Smarcel	case EC_LEFT:
855130803Smarcel		not_in_completion();
856130803Smarcel		return (cmd_left());
857130803Smarcel	case EC_W_RIGHT:
858130803Smarcel		not_in_completion();
859130803Smarcel		while (*cp != '\0' && *cp != ' ')
860130803Smarcel			cmd_right();
861130803Smarcel		while (*cp == ' ')
862130803Smarcel			cmd_right();
863130803Smarcel		return (CC_OK);
864130803Smarcel	case EC_W_LEFT:
865130803Smarcel		not_in_completion();
866130803Smarcel		while (cp > cmdbuf && cp[-1] == ' ')
867130803Smarcel			cmd_left();
868130803Smarcel		while (cp > cmdbuf && cp[-1] != ' ')
869130803Smarcel			cmd_left();
870130803Smarcel		return (CC_OK);
871130803Smarcel	case EC_HOME:
872130803Smarcel		not_in_completion();
873130803Smarcel		cmd_offset = 0;
874130803Smarcel		cmd_home();
875130803Smarcel		cmd_repaint(cp);
876130803Smarcel		return (CC_OK);
877130803Smarcel	case EC_END:
878130803Smarcel		not_in_completion();
879130803Smarcel		while (*cp != '\0')
880130803Smarcel			cmd_right();
881130803Smarcel		return (CC_OK);
882130803Smarcel	case EC_INSERT:
883130803Smarcel		not_in_completion();
884130803Smarcel		return (CC_OK);
885130803Smarcel	case EC_BACKSPACE:
886130803Smarcel		not_in_completion();
887130803Smarcel		return (cmd_erase());
888130803Smarcel	case EC_LINEKILL:
889130803Smarcel		not_in_completion();
890130803Smarcel		return (cmd_kill());
891130803Smarcel	case EC_ABORT:
892130803Smarcel		not_in_completion();
893130803Smarcel		(void) cmd_kill();
894130803Smarcel		return (CC_QUIT);
895130803Smarcel	case EC_W_BACKSPACE:
896130803Smarcel		not_in_completion();
897130803Smarcel		return (cmd_werase());
898130803Smarcel	case EC_DELETE:
899130803Smarcel		not_in_completion();
900130803Smarcel		return (cmd_delete());
901130803Smarcel	case EC_W_DELETE:
902130803Smarcel		not_in_completion();
903130803Smarcel		return (cmd_wdelete());
904130803Smarcel	case EC_LITERAL:
905130803Smarcel		literal = 1;
906130803Smarcel		return (CC_OK);
907130803Smarcel#if CMD_HISTORY
908130803Smarcel	case EC_UP:
909130803Smarcel	case EC_DOWN:
910130803Smarcel		not_in_completion();
911130803Smarcel		return (cmd_updown(action));
912130803Smarcel#endif
913130803Smarcel#if TAB_COMPLETE_FILENAME
914130803Smarcel	case EC_F_COMPLETE:
915130803Smarcel	case EC_B_COMPLETE:
916130803Smarcel	case EC_EXPAND:
917130803Smarcel		return (cmd_complete(action));
918130803Smarcel#endif
919130803Smarcel	case EC_NOACTION:
920130803Smarcel		return (CC_OK);
921130803Smarcel	default:
922130803Smarcel		not_in_completion();
923130803Smarcel		return (CC_PASS);
924130803Smarcel	}
925130803Smarcel}
926130803Smarcel
927130803Smarcel#if TAB_COMPLETE_FILENAME
928130803Smarcel/*
929130803Smarcel * Insert a string into the command buffer, at the current position.
930130803Smarcel */
931130803Smarcel	static int
932130803Smarcelcmd_istr(str)
933130803Smarcel	char *str;
934130803Smarcel{
935130803Smarcel	char *s;
936130803Smarcel	int action;
937130803Smarcel	char *endline = str + strlen(str);
938130803Smarcel
939130803Smarcel	for (s = str;  *s != '\0';  )
940130803Smarcel	{
941130803Smarcel		char *os = s;
942130803Smarcel		step_char(&s, +1, endline);
943130803Smarcel		action = cmd_ichar(os, s - os);
944130803Smarcel		if (action != CC_OK)
945130803Smarcel		{
946130803Smarcel			bell();
947130803Smarcel			return (action);
948130803Smarcel		}
949130803Smarcel	}
950130803Smarcel	return (CC_OK);
951130803Smarcel}
952130803Smarcel
953130803Smarcel/*
954130803Smarcel * Find the beginning and end of the "current" word.
955130803Smarcel * This is the word which the cursor (cp) is inside or at the end of.
956130803Smarcel * Return pointer to the beginning of the word and put the
957130803Smarcel * cursor at the end of the word.
958130803Smarcel */
959130803Smarcel	static char *
960130803Smarceldelimit_word()
961130803Smarcel{
962130803Smarcel	char *word;
963130803Smarcel#if SPACES_IN_FILENAMES
964130803Smarcel	char *p;
965130803Smarcel	int delim_quoted = 0;
966130803Smarcel	int meta_quoted = 0;
967130803Smarcel	char *esc = get_meta_escape();
968130803Smarcel	int esclen = strlen(esc);
969130803Smarcel#endif
970130803Smarcel
971130803Smarcel	/*
972130803Smarcel	 * Move cursor to end of word.
973130803Smarcel	 */
974130803Smarcel	if (*cp != ' ' && *cp != '\0')
975130803Smarcel	{
976130803Smarcel		/*
977130803Smarcel		 * Cursor is on a nonspace.
978130803Smarcel		 * Move cursor right to the next space.
979130803Smarcel		 */
980130803Smarcel		while (*cp != ' ' && *cp != '\0')
981130803Smarcel			cmd_right();
982130803Smarcel	} else if (cp > cmdbuf && cp[-1] != ' ')
983130803Smarcel	{
984130803Smarcel		/*
985130803Smarcel		 * Cursor is on a space, and char to the left is a nonspace.
986130803Smarcel		 * We're already at the end of the word.
987130803Smarcel		 */
988130803Smarcel		;
989130803Smarcel#if 0
990130803Smarcel	} else
991130803Smarcel	{
992130803Smarcel		/*
993130803Smarcel		 * Cursor is on a space and char to the left is a space.
994130803Smarcel		 * Huh? There's no word here.
995130803Smarcel		 */
996130803Smarcel		return (NULL);
997130803Smarcel#endif
998130803Smarcel	}
999130803Smarcel	/*
1000130803Smarcel	 * Find the beginning of the word which the cursor is in.
1001130803Smarcel	 */
1002130803Smarcel	if (cp == cmdbuf)
1003130803Smarcel		return (NULL);
1004130803Smarcel#if SPACES_IN_FILENAMES
1005130803Smarcel	/*
1006130803Smarcel	 * If we have an unbalanced quote (that is, an open quote
1007130803Smarcel	 * without a corresponding close quote), we return everything
1008130803Smarcel	 * from the open quote, including spaces.
1009130803Smarcel	 */
1010130803Smarcel	for (word = cmdbuf;  word < cp;  word++)
1011130803Smarcel		if (*word != ' ')
1012130803Smarcel			break;
1013130803Smarcel	if (word >= cp)
1014130803Smarcel		return (cp);
1015130803Smarcel	for (p = cmdbuf;  p < cp;  p++)
1016130803Smarcel	{
1017130803Smarcel		if (meta_quoted)
1018130803Smarcel		{
1019130803Smarcel			meta_quoted = 0;
1020130803Smarcel		} else if (esclen > 0 && p + esclen < cp &&
1021130803Smarcel		           strncmp(p, esc, esclen) == 0)
1022130803Smarcel		{
1023130803Smarcel			meta_quoted = 1;
1024130803Smarcel			p += esclen - 1;
1025130803Smarcel		} else if (delim_quoted)
1026130803Smarcel		{
1027130803Smarcel			if (*p == closequote)
1028130803Smarcel				delim_quoted = 0;
1029130803Smarcel		} else /* (!delim_quoted) */
1030130803Smarcel		{
1031130803Smarcel			if (*p == openquote)
1032130803Smarcel				delim_quoted = 1;
1033130803Smarcel			else if (*p == ' ')
1034130803Smarcel				word = p+1;
1035130803Smarcel		}
1036130803Smarcel	}
1037130803Smarcel#endif
1038130803Smarcel	return (word);
1039130803Smarcel}
1040130803Smarcel
1041130803Smarcel/*
1042130803Smarcel * Set things up to enter completion mode.
1043130803Smarcel * Expand the word under the cursor into a list of filenames
1044130803Smarcel * which start with that word, and set tk_text to that list.
1045130803Smarcel */
1046130803Smarcel	static void
1047130803Smarcelinit_compl()
1048130803Smarcel{
1049130803Smarcel	char *word;
1050130803Smarcel	char c;
1051130803Smarcel
1052130803Smarcel	/*
1053130803Smarcel	 * Get rid of any previous tk_text.
1054130803Smarcel	 */
1055130803Smarcel	if (tk_text != NULL)
1056130803Smarcel	{
1057130803Smarcel		free(tk_text);
1058130803Smarcel		tk_text = NULL;
1059130803Smarcel	}
1060130803Smarcel	/*
1061130803Smarcel	 * Find the original (uncompleted) word in the command buffer.
1062130803Smarcel	 */
1063130803Smarcel	word = delimit_word();
1064130803Smarcel	if (word == NULL)
1065130803Smarcel		return;
1066130803Smarcel	/*
1067130803Smarcel	 * Set the insertion point to the point in the command buffer
1068130803Smarcel	 * where the original (uncompleted) word now sits.
1069130803Smarcel	 */
1070130803Smarcel	tk_ipoint = word;
1071130803Smarcel	/*
1072130803Smarcel	 * Save the original (uncompleted) word
1073130803Smarcel	 */
1074130803Smarcel	if (tk_original != NULL)
1075130803Smarcel		free(tk_original);
1076130803Smarcel	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
1077130803Smarcel	strncpy(tk_original, word, cp-word);
1078130803Smarcel	/*
1079130803Smarcel	 * Get the expanded filename.
1080130803Smarcel	 * This may result in a single filename, or
1081130803Smarcel	 * a blank-separated list of filenames.
1082130803Smarcel	 */
1083130803Smarcel	c = *cp;
1084130803Smarcel	*cp = '\0';
1085130803Smarcel	if (*word != openquote)
1086130803Smarcel	{
1087130803Smarcel		tk_text = fcomplete(word);
1088130803Smarcel	} else
1089130803Smarcel	{
1090130803Smarcel#if MSDOS_COMPILER
1091130803Smarcel		char *qword = NULL;
1092130803Smarcel#else
1093130803Smarcel		char *qword = shell_quote(word+1);
1094130803Smarcel#endif
1095130803Smarcel		if (qword == NULL)
1096130803Smarcel			tk_text = fcomplete(word+1);
1097130803Smarcel		else
1098130803Smarcel		{
1099130803Smarcel			tk_text = fcomplete(qword);
1100130803Smarcel			free(qword);
1101130803Smarcel		}
1102130803Smarcel	}
1103130803Smarcel	*cp = c;
1104130803Smarcel}
1105130803Smarcel
1106130803Smarcel/*
1107130803Smarcel * Return the next word in the current completion list.
1108130803Smarcel */
1109130803Smarcel	static char *
1110130803Smarcelnext_compl(action, prev)
1111130803Smarcel	int action;
1112130803Smarcel	char *prev;
1113130803Smarcel{
1114130803Smarcel	switch (action)
1115130803Smarcel	{
1116130803Smarcel	case EC_F_COMPLETE:
1117130803Smarcel		return (forw_textlist(&tk_tlist, prev));
1118130803Smarcel	case EC_B_COMPLETE:
1119130803Smarcel		return (back_textlist(&tk_tlist, prev));
1120130803Smarcel	}
1121130803Smarcel	/* Cannot happen */
1122130803Smarcel	return ("?");
1123130803Smarcel}
1124130803Smarcel
1125130803Smarcel/*
1126130803Smarcel * Complete the filename before (or under) the cursor.
1127130803Smarcel * cmd_complete may be called multiple times.  The global in_completion
1128130803Smarcel * remembers whether this call is the first time (create the list),
1129130803Smarcel * or a subsequent time (step thru the list).
1130130803Smarcel */
1131130803Smarcel	static int
1132130803Smarcelcmd_complete(action)
1133130803Smarcel	int action;
1134130803Smarcel{
1135130803Smarcel	char *s;
1136130803Smarcel
1137130803Smarcel	if (!in_completion || action == EC_EXPAND)
1138130803Smarcel	{
1139130803Smarcel		/*
1140130803Smarcel		 * Expand the word under the cursor and
1141130803Smarcel		 * use the first word in the expansion
1142130803Smarcel		 * (or the entire expansion if we're doing EC_EXPAND).
1143130803Smarcel		 */
1144130803Smarcel		init_compl();
1145130803Smarcel		if (tk_text == NULL)
1146130803Smarcel		{
1147130803Smarcel			bell();
1148130803Smarcel			return (CC_OK);
1149130803Smarcel		}
1150130803Smarcel		if (action == EC_EXPAND)
1151130803Smarcel		{
1152130803Smarcel			/*
1153130803Smarcel			 * Use the whole list.
1154130803Smarcel			 */
1155130803Smarcel			tk_trial = tk_text;
1156130803Smarcel		} else
1157130803Smarcel		{
1158130803Smarcel			/*
1159130803Smarcel			 * Use the first filename in the list.
1160130803Smarcel			 */
1161130803Smarcel			in_completion = 1;
1162130803Smarcel			init_textlist(&tk_tlist, tk_text);
1163130803Smarcel			tk_trial = next_compl(action, (char*)NULL);
1164130803Smarcel		}
1165130803Smarcel	} else
1166130803Smarcel	{
1167130803Smarcel		/*
1168130803Smarcel		 * We already have a completion list.
1169130803Smarcel		 * Use the next/previous filename from the list.
1170130803Smarcel		 */
1171130803Smarcel		tk_trial = next_compl(action, tk_trial);
1172130803Smarcel	}
1173130803Smarcel
1174130803Smarcel  	/*
1175130803Smarcel  	 * Remove the original word, or the previous trial completion.
1176130803Smarcel  	 */
1177130803Smarcel	while (cp > tk_ipoint)
1178130803Smarcel		(void) cmd_erase();
1179130803Smarcel
1180130803Smarcel	if (tk_trial == NULL)
1181130803Smarcel	{
1182130803Smarcel		/*
1183130803Smarcel		 * There are no more trial completions.
1184130803Smarcel		 * Insert the original (uncompleted) filename.
1185130803Smarcel		 */
1186130803Smarcel		in_completion = 0;
1187130803Smarcel		if (cmd_istr(tk_original) != CC_OK)
1188130803Smarcel			goto fail;
1189130803Smarcel	} else
1190130803Smarcel	{
1191130803Smarcel		/*
1192130803Smarcel		 * Insert trial completion.
1193130803Smarcel		 */
1194130803Smarcel		if (cmd_istr(tk_trial) != CC_OK)
1195130803Smarcel			goto fail;
1196130803Smarcel		/*
1197130803Smarcel		 * If it is a directory, append a slash.
1198130803Smarcel		 */
1199130803Smarcel		if (is_dir(tk_trial))
1200130803Smarcel		{
1201130803Smarcel			if (cp > cmdbuf && cp[-1] == closequote)
1202130803Smarcel				(void) cmd_erase();
1203130803Smarcel			s = lgetenv("LESSSEPARATOR");
1204130803Smarcel			if (s == NULL)
1205130803Smarcel				s = PATHNAME_SEP;
1206130803Smarcel			if (cmd_istr(s) != CC_OK)
1207130803Smarcel				goto fail;
1208130803Smarcel		}
1209130803Smarcel	}
1210130803Smarcel
1211130803Smarcel	return (CC_OK);
1212130803Smarcel
1213130803Smarcelfail:
1214130803Smarcel	in_completion = 0;
1215130803Smarcel	bell();
1216130803Smarcel	return (CC_OK);
1217130803Smarcel}
1218130803Smarcel
1219130803Smarcel#endif /* TAB_COMPLETE_FILENAME */
1220130803Smarcel
1221130803Smarcel/*
1222130803Smarcel * Process a single character of a multi-character command, such as
1223130803Smarcel * a number, or the pattern of a search command.
1224130803Smarcel * Returns:
1225130803Smarcel *	CC_OK		The char was accepted.
1226130803Smarcel *	CC_QUIT		The char requests the command to be aborted.
1227130803Smarcel *	CC_ERROR	The char could not be accepted due to an error.
1228130803Smarcel */
1229130803Smarcel	public int
1230130803Smarcelcmd_char(c)
1231130803Smarcel	int c;
1232130803Smarcel{
1233130803Smarcel	int action;
1234130803Smarcel	int len;
1235130803Smarcel
1236130803Smarcel	if (!utf_mode)
1237130803Smarcel	{
1238130803Smarcel		cmd_mbc_buf[0] = c;
1239130803Smarcel		len = 1;
1240130803Smarcel	} else
1241130803Smarcel	{
1242130803Smarcel		/* Perform strict validation in all possible cases.  */
1243130803Smarcel		if (cmd_mbc_buf_len == 0)
1244130803Smarcel		{
1245130803Smarcel		 retry:
1246130803Smarcel			cmd_mbc_buf_index = 1;
1247130803Smarcel			*cmd_mbc_buf = c;
1248130803Smarcel			if (IS_ASCII_OCTET(c))
1249130803Smarcel				cmd_mbc_buf_len = 1;
1250130803Smarcel			else if (IS_UTF8_LEAD(c))
1251130803Smarcel			{
1252130803Smarcel				cmd_mbc_buf_len = utf_len(c);
1253130803Smarcel				return (CC_OK);
1254130803Smarcel			} else
1255130803Smarcel			{
1256130803Smarcel				/* UTF8_INVALID or stray UTF8_TRAIL */
1257130803Smarcel				bell();
1258130803Smarcel				return (CC_ERROR);
1259130803Smarcel			}
1260130803Smarcel		} else if (IS_UTF8_TRAIL(c))
1261130803Smarcel		{
1262130803Smarcel			cmd_mbc_buf[cmd_mbc_buf_index++] = c;
1263130803Smarcel			if (cmd_mbc_buf_index < cmd_mbc_buf_len)
1264130803Smarcel				return (CC_OK);
1265130803Smarcel			if (!is_utf8_well_formed(cmd_mbc_buf))
1266130803Smarcel			{
1267130803Smarcel				/* complete, but not well formed (non-shortest form), sequence */
1268130803Smarcel				cmd_mbc_buf_len = 0;
1269130803Smarcel				bell();
1270130803Smarcel				return (CC_ERROR);
1271130803Smarcel			}
1272130803Smarcel		} else
1273130803Smarcel		{
1274130803Smarcel			/* Flush incomplete (truncated) sequence.  */
1275130803Smarcel			cmd_mbc_buf_len = 0;
1276130803Smarcel			bell();
1277130803Smarcel			/* Handle new char.  */
1278130803Smarcel			goto retry;
1279130803Smarcel		}
1280130803Smarcel
1281130803Smarcel		len = cmd_mbc_buf_len;
1282130803Smarcel		cmd_mbc_buf_len = 0;
1283130803Smarcel	}
1284130803Smarcel
1285130803Smarcel	if (literal)
1286130803Smarcel	{
1287130803Smarcel		/*
1288130803Smarcel		 * Insert the char, even if it is a line-editing char.
1289130803Smarcel		 */
1290130803Smarcel		literal = 0;
1291130803Smarcel		return (cmd_ichar(cmd_mbc_buf, len));
1292130803Smarcel	}
1293130803Smarcel
1294130803Smarcel	/*
1295130803Smarcel	 * See if it is a line-editing character.
1296130803Smarcel	 */
1297130803Smarcel	if (in_mca() && len == 1)
1298130803Smarcel	{
1299130803Smarcel		action = cmd_edit(c);
1300130803Smarcel		switch (action)
1301130803Smarcel		{
1302130803Smarcel		case CC_OK:
1303130803Smarcel		case CC_QUIT:
1304130803Smarcel			return (action);
1305130803Smarcel		case CC_PASS:
1306130803Smarcel			break;
1307130803Smarcel		}
1308130803Smarcel	}
1309130803Smarcel
1310130803Smarcel	/*
1311130803Smarcel	 * Insert the char into the command buffer.
1312130803Smarcel	 */
1313130803Smarcel	return (cmd_ichar(cmd_mbc_buf, len));
1314130803Smarcel}
1315130803Smarcel
1316130803Smarcel/*
1317130803Smarcel * Return the number currently in the command buffer.
1318130803Smarcel */
1319130803Smarcel	public LINENUM
1320130803Smarcelcmd_int(frac)
1321130803Smarcel	long *frac;
1322130803Smarcel{
1323130803Smarcel	char *p;
1324130803Smarcel	LINENUM n = 0;
1325130803Smarcel	int err;
1326130803Smarcel
1327130803Smarcel	for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
1328130803Smarcel		n = (n * 10) + (*p - '0');
1329130803Smarcel	*frac = 0;
1330130803Smarcel	if (*p++ == '.')
1331130803Smarcel	{
1332130803Smarcel		*frac = getfraction(&p, NULL, &err);
1333130803Smarcel		/* {{ do something if err is set? }} */
1334130803Smarcel	}
1335130803Smarcel	return (n);
1336130803Smarcel}
1337130803Smarcel
1338130803Smarcel/*
1339130803Smarcel * Return a pointer to the command buffer.
1340130803Smarcel */
1341130803Smarcel	public char *
1342130803Smarcelget_cmdbuf()
1343130803Smarcel{
1344130803Smarcel	return (cmdbuf);
1345130803Smarcel}
1346130803Smarcel
1347130803Smarcel#if CMD_HISTORY
1348130803Smarcel/*
1349130803Smarcel * Return the last (most recent) string in the current command history.
1350130803Smarcel */
1351130803Smarcel	public char *
1352130803Smarcelcmd_lastpattern()
1353130803Smarcel{
1354130803Smarcel	if (curr_mlist == NULL)
1355130803Smarcel		return (NULL);
1356130803Smarcel	return (curr_mlist->curr_mp->prev->string);
1357130803Smarcel}
1358130803Smarcel#endif
1359130803Smarcel
1360130803Smarcel#if CMD_HISTORY
1361130803Smarcel/*
1362130803Smarcel * Get the name of the history file.
1363130803Smarcel */
1364130803Smarcel	static char *
1365130803Smarcelhistfile_name()
1366130803Smarcel{
1367130803Smarcel	char *home;
1368130803Smarcel	char *name;
1369130803Smarcel	int len;
1370130803Smarcel
1371130803Smarcel	/* See if filename is explicitly specified by $LESSHISTFILE. */
1372130803Smarcel	name = lgetenv("LESSHISTFILE");
1373130803Smarcel	if (name != NULL && *name != '\0')
1374130803Smarcel	{
1375130803Smarcel		if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
1376130803Smarcel			/* $LESSHISTFILE == "-" means don't use a history file. */
1377130803Smarcel			return (NULL);
1378130803Smarcel		return (save(name));
1379130803Smarcel	}
1380130803Smarcel
1381130803Smarcel	/* Otherwise, file is in $HOME. */
1382130803Smarcel	home = lgetenv("HOME");
1383130803Smarcel	if (home == NULL || *home == '\0')
1384130803Smarcel	{
1385130803Smarcel#if OS2
1386130803Smarcel		home = lgetenv("INIT");
1387130803Smarcel		if (home == NULL || *home == '\0')
1388130803Smarcel#endif
1389130803Smarcel			return (NULL);
1390130803Smarcel	}
1391130803Smarcel	len = strlen(home) + strlen(LESSHISTFILE) + 2;
1392130803Smarcel	name = (char *) ecalloc(len, sizeof(char));
1393130803Smarcel	SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
1394130803Smarcel	return (name);
1395130803Smarcel}
1396130803Smarcel#endif /* CMD_HISTORY */
1397130803Smarcel
1398130803Smarcel/*
1399130803Smarcel * Initialize history from a .lesshist file.
1400130803Smarcel */
1401130803Smarcel	public void
1402130803Smarcelinit_cmdhist()
1403130803Smarcel{
1404130803Smarcel#if CMD_HISTORY
1405130803Smarcel	struct mlist *ml = NULL;
1406130803Smarcel	char line[CMDBUF_SIZE];
1407130803Smarcel	char *filename;
1408130803Smarcel	FILE *f;
1409130803Smarcel	char *p;
1410130803Smarcel
1411130803Smarcel	filename = histfile_name();
1412130803Smarcel	if (filename == NULL)
1413130803Smarcel		return;
1414130803Smarcel	f = fopen(filename, "r");
1415130803Smarcel	free(filename);
1416130803Smarcel	if (f == NULL)
1417130803Smarcel		return;
1418130803Smarcel	if (fgets(line, sizeof(line), f) == NULL ||
1419130803Smarcel	    strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
1420130803Smarcel	{
1421130803Smarcel		fclose(f);
1422130803Smarcel		return;
1423130803Smarcel	}
1424130803Smarcel	while (fgets(line, sizeof(line), f) != NULL)
1425130803Smarcel	{
1426130803Smarcel		for (p = line;  *p != '\0';  p++)
1427130803Smarcel		{
1428130803Smarcel			if (*p == '\n' || *p == '\r')
1429130803Smarcel			{
1430130803Smarcel				*p = '\0';
1431130803Smarcel				break;
1432130803Smarcel			}
1433130803Smarcel		}
1434130803Smarcel		if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1435130803Smarcel			ml = &mlist_search;
1436130803Smarcel		else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
1437130803Smarcel		{
1438130803Smarcel#if SHELL_ESCAPE || PIPEC
1439130803Smarcel			ml = &mlist_shell;
1440130803Smarcel#else
1441130803Smarcel			ml = NULL;
1442130803Smarcel#endif
1443130803Smarcel		} else if (*line == '"')
1444130803Smarcel		{
1445130803Smarcel			if (ml != NULL)
1446130803Smarcel				cmd_addhist(ml, line+1);
1447130803Smarcel		}
1448130803Smarcel	}
1449130803Smarcel	fclose(f);
1450130803Smarcel#endif /* CMD_HISTORY */
1451130803Smarcel}
1452130803Smarcel
1453130803Smarcel/*
1454130803Smarcel *
1455130803Smarcel */
1456130803Smarcel#if CMD_HISTORY
1457130803Smarcel	static void
1458130803Smarcelsave_mlist(ml, f)
1459130803Smarcel	struct mlist *ml;
1460130803Smarcel	FILE *f;
1461130803Smarcel{
1462130803Smarcel	int histsize = 0;
1463130803Smarcel	int n;
1464130803Smarcel	char *s;
1465130803Smarcel
1466130803Smarcel	s = lgetenv("LESSHISTSIZE");
1467130803Smarcel	if (s != NULL)
1468130803Smarcel		histsize = atoi(s);
1469130803Smarcel	if (histsize == 0)
1470130803Smarcel		histsize = 100;
1471130803Smarcel
1472130803Smarcel	ml = ml->prev;
1473130803Smarcel	for (n = 0;  n < histsize;  n++)
1474130803Smarcel	{
1475130803Smarcel		if (ml->string == NULL)
1476130803Smarcel			break;
1477130803Smarcel		ml = ml->prev;
1478130803Smarcel	}
1479130803Smarcel	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1480130803Smarcel		fprintf(f, "\"%s\n", ml->string);
1481130803Smarcel}
1482130803Smarcel#endif /* CMD_HISTORY */
1483130803Smarcel
1484130803Smarcel/*
1485130803Smarcel *
1486130803Smarcel */
1487130803Smarcel	public void
1488130803Smarcelsave_cmdhist()
1489130803Smarcel{
1490130803Smarcel#if CMD_HISTORY
1491130803Smarcel	char *filename;
1492130803Smarcel	FILE *f;
1493130803Smarcel	int modified = 0;
1494130803Smarcel
1495130803Smarcel	if (mlist_search.modified)
1496130803Smarcel		modified = 1;
1497130803Smarcel#if SHELL_ESCAPE || PIPEC
1498130803Smarcel	if (mlist_shell.modified)
1499130803Smarcel		modified = 1;
1500130803Smarcel#endif
1501130803Smarcel	if (!modified)
1502130803Smarcel		return;
1503130803Smarcel	filename = histfile_name();
1504130803Smarcel	if (filename == NULL)
1505130803Smarcel		return;
1506130803Smarcel	f = fopen(filename, "w");
1507130803Smarcel	free(filename);
1508130803Smarcel	if (f == NULL)
1509130803Smarcel		return;
1510130803Smarcel#if HAVE_FCHMOD
1511130803Smarcel{
1512130803Smarcel	/* Make history file readable only by owner. */
1513130803Smarcel	int do_chmod = 1;
1514130803Smarcel#if HAVE_STAT
1515130803Smarcel	struct stat statbuf;
1516130803Smarcel	int r = fstat(fileno(f), &statbuf);
1517130803Smarcel	if (r < 0 || !S_ISREG(statbuf.st_mode))
1518130803Smarcel		/* Don't chmod if not a regular file. */
1519130803Smarcel		do_chmod = 0;
1520130803Smarcel#endif
1521130803Smarcel	if (do_chmod)
1522130803Smarcel		fchmod(fileno(f), 0600);
1523130803Smarcel}
1524130803Smarcel#endif
1525130803Smarcel
1526130803Smarcel	fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
1527130803Smarcel
1528130803Smarcel	fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1529130803Smarcel	save_mlist(&mlist_search, f);
1530130803Smarcel
1531130803Smarcel#if SHELL_ESCAPE || PIPEC
1532130803Smarcel	fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1533130803Smarcel	save_mlist(&mlist_shell, f);
1534130803Smarcel#endif
1535130803Smarcel
1536130803Smarcel	fclose(f);
1537130803Smarcel#endif /* CMD_HISTORY */
1538130803Smarcel}
1539130803Smarcel