1*085b99e6Ssimonb /* $NetBSD: cmdbuf.c,v 1.6 2023/10/06 07:05:59 simonb Exp $ */
220006a0bStron
320006a0bStron /*
4838f5788Ssimonb * Copyright (C) 1984-2023 Mark Nudelman
520006a0bStron *
620006a0bStron * You may distribute under the terms of either the GNU General Public
720006a0bStron * License or the Less License, as specified in the README file.
820006a0bStron *
9ec18bca0Stron * For more information, see the README file.
1020006a0bStron */
1120006a0bStron
1220006a0bStron
1320006a0bStron /*
1420006a0bStron * Functions which manipulate the command buffer.
1520006a0bStron * Used only by command() and related functions.
1620006a0bStron */
1720006a0bStron
1820006a0bStron #include "less.h"
1920006a0bStron #include "cmd.h"
2020006a0bStron #include "charset.h"
2120006a0bStron #if HAVE_STAT
2220006a0bStron #include <sys/stat.h>
2320006a0bStron #endif
2420006a0bStron
2520006a0bStron extern int sc_width;
2620006a0bStron extern int utf_mode;
27838f5788Ssimonb extern int no_hist_dups;
28838f5788Ssimonb extern int marks_modified;
29838f5788Ssimonb extern int secure;
3020006a0bStron
3120006a0bStron static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
3220006a0bStron static int cmd_col; /* Current column of the cursor */
3320006a0bStron static int prompt_col; /* Column of cursor just after prompt */
3420006a0bStron static char *cp; /* Pointer into cmdbuf */
3520006a0bStron static int cmd_offset; /* Index into cmdbuf of first displayed char */
3620006a0bStron static int literal; /* Next input char should not be interpreted */
37838f5788Ssimonb public int updown_match = -1; /* Prefix length in up/down movement */
3820006a0bStron
3920006a0bStron #if TAB_COMPLETE_FILENAME
40838f5788Ssimonb static int cmd_complete(int action);
4120006a0bStron /*
4220006a0bStron * These variables are statics used by cmd_complete.
4320006a0bStron */
4420006a0bStron static int in_completion = 0;
4520006a0bStron static char *tk_text;
4620006a0bStron static char *tk_original;
4720006a0bStron static char *tk_ipoint;
48838f5788Ssimonb static char *tk_trial = NULL;
4920006a0bStron static struct textlist tk_tlist;
5020006a0bStron #endif
5120006a0bStron
52838f5788Ssimonb static int cmd_left(void);
5320006a0bStron
5420006a0bStron #if SPACES_IN_FILENAMES
5520006a0bStron public char openquote = '"';
5620006a0bStron public char closequote = '"';
5720006a0bStron #endif
5820006a0bStron
5920006a0bStron #if CMD_HISTORY
6020006a0bStron
6120006a0bStron /* History file */
6220006a0bStron #define HISTFILE_FIRST_LINE ".less-history-file:"
6320006a0bStron #define HISTFILE_SEARCH_SECTION ".search"
6420006a0bStron #define HISTFILE_SHELL_SECTION ".shell"
65838f5788Ssimonb #define HISTFILE_MARK_SECTION ".mark"
6620006a0bStron
6720006a0bStron /*
6820006a0bStron * A mlist structure represents a command history.
6920006a0bStron */
7020006a0bStron struct mlist
7120006a0bStron {
7220006a0bStron struct mlist *next;
7320006a0bStron struct mlist *prev;
7420006a0bStron struct mlist *curr_mp;
7520006a0bStron char *string;
7620006a0bStron int modified;
7720006a0bStron };
7820006a0bStron
7920006a0bStron /*
8020006a0bStron * These are the various command histories that exist.
8120006a0bStron */
8220006a0bStron struct mlist mlist_search =
8320006a0bStron { &mlist_search, &mlist_search, &mlist_search, NULL, 0 };
84838f5788Ssimonb public void *ml_search = (void *) &mlist_search;
8520006a0bStron
8620006a0bStron struct mlist mlist_examine =
8720006a0bStron { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
88838f5788Ssimonb public void *ml_examine = (void *) &mlist_examine;
8920006a0bStron
9020006a0bStron #if SHELL_ESCAPE || PIPEC
9120006a0bStron struct mlist mlist_shell =
9220006a0bStron { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 };
93838f5788Ssimonb public void *ml_shell = (void *) &mlist_shell;
9420006a0bStron #endif
9520006a0bStron
9620006a0bStron #else /* CMD_HISTORY */
9720006a0bStron
9820006a0bStron /* If CMD_HISTORY is off, these are just flags. */
99838f5788Ssimonb public void *ml_search = (void *)1;
100838f5788Ssimonb public void *ml_examine = (void *)2;
10120006a0bStron #if SHELL_ESCAPE || PIPEC
102838f5788Ssimonb public void *ml_shell = (void *)3;
10320006a0bStron #endif
10420006a0bStron
10520006a0bStron #endif /* CMD_HISTORY */
10620006a0bStron
10720006a0bStron /*
10820006a0bStron * History for the current command.
10920006a0bStron */
11020006a0bStron static struct mlist *curr_mlist = NULL;
11120006a0bStron static int curr_cmdflags;
11220006a0bStron
11320006a0bStron static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
11420006a0bStron static int cmd_mbc_buf_len;
11520006a0bStron static int cmd_mbc_buf_index;
11620006a0bStron
11720006a0bStron
11820006a0bStron /*
11920006a0bStron * Reset command buffer (to empty).
12020006a0bStron */
cmd_reset(void)121838f5788Ssimonb public void cmd_reset(void)
12220006a0bStron {
12320006a0bStron cp = cmdbuf;
12420006a0bStron *cp = '\0';
12520006a0bStron cmd_col = 0;
12620006a0bStron cmd_offset = 0;
12720006a0bStron literal = 0;
12820006a0bStron cmd_mbc_buf_len = 0;
129ec18bca0Stron updown_match = -1;
13020006a0bStron }
13120006a0bStron
13220006a0bStron /*
13320006a0bStron * Clear command line.
13420006a0bStron */
clear_cmd(void)135838f5788Ssimonb public void clear_cmd(void)
13620006a0bStron {
13720006a0bStron cmd_col = prompt_col = 0;
13820006a0bStron cmd_mbc_buf_len = 0;
139ec18bca0Stron updown_match = -1;
14020006a0bStron }
14120006a0bStron
14220006a0bStron /*
14320006a0bStron * Display a string, usually as a prompt for input into the command buffer.
14420006a0bStron */
cmd_putstr(constant char * s)145838f5788Ssimonb public void cmd_putstr(constant char *s)
14620006a0bStron {
14720006a0bStron LWCHAR prev_ch = 0;
14820006a0bStron LWCHAR ch;
149838f5788Ssimonb constant char *endline = s + strlen(s);
15020006a0bStron while (*s != '\0')
15120006a0bStron {
152838f5788Ssimonb char *ns = (char *) s;
153838f5788Ssimonb int width;
15420006a0bStron ch = step_char(&ns, +1, endline);
15520006a0bStron while (s < ns)
15620006a0bStron putchr(*s++);
15720006a0bStron if (!utf_mode)
158838f5788Ssimonb width = 1;
159838f5788Ssimonb else if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
160838f5788Ssimonb width = 0;
161838f5788Ssimonb else
162838f5788Ssimonb width = is_wide_char(ch) ? 2 : 1;
16320006a0bStron cmd_col += width;
16420006a0bStron prompt_col += width;
16520006a0bStron prev_ch = ch;
16620006a0bStron }
16720006a0bStron }
16820006a0bStron
16920006a0bStron /*
17020006a0bStron * How many characters are in the command buffer?
17120006a0bStron */
len_cmdbuf(void)172838f5788Ssimonb public int len_cmdbuf(void)
17320006a0bStron {
17420006a0bStron char *s = cmdbuf;
17520006a0bStron char *endline = s + strlen(s);
17620006a0bStron int len = 0;
17720006a0bStron
17820006a0bStron while (*s != '\0')
17920006a0bStron {
18020006a0bStron step_char(&s, +1, endline);
18120006a0bStron len++;
18220006a0bStron }
18320006a0bStron return (len);
18420006a0bStron }
18520006a0bStron
18620006a0bStron /*
18720006a0bStron * Common part of cmd_step_right() and cmd_step_left().
188838f5788Ssimonb * {{ Returning pwidth and bswidth separately is a historical artifact
189838f5788Ssimonb * since they're always the same. Maybe clean this up someday. }}
19020006a0bStron */
cmd_step_common(char * p,LWCHAR ch,int len,int * pwidth,int * bswidth)191838f5788Ssimonb static char * cmd_step_common(char *p, LWCHAR ch, int len, int *pwidth, int *bswidth)
19220006a0bStron {
19320006a0bStron char *pr;
194838f5788Ssimonb int width;
19520006a0bStron
19620006a0bStron if (len == 1)
19720006a0bStron {
19820006a0bStron pr = prchar((int) ch);
199838f5788Ssimonb width = (int) strlen(pr);
20020006a0bStron } else
20120006a0bStron {
20220006a0bStron pr = prutfchar(ch);
20320006a0bStron if (is_composing_char(ch))
204838f5788Ssimonb width = 0;
205838f5788Ssimonb else if (is_ubin_char(ch))
206838f5788Ssimonb width = (int) strlen(pr);
207838f5788Ssimonb else
20820006a0bStron {
20920006a0bStron LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
21020006a0bStron if (is_combining_char(prev_ch, ch))
211838f5788Ssimonb width = 0;
212838f5788Ssimonb else
213838f5788Ssimonb width = is_wide_char(ch) ? 2 : 1;
214838f5788Ssimonb }
215838f5788Ssimonb }
21620006a0bStron if (pwidth != NULL)
217838f5788Ssimonb *pwidth = width;
21820006a0bStron if (bswidth != NULL)
219838f5788Ssimonb *bswidth = width;
22020006a0bStron return (pr);
22120006a0bStron }
22220006a0bStron
22320006a0bStron /*
22420006a0bStron * Step a pointer one character right in the command buffer.
22520006a0bStron */
cmd_step_right(char ** pp,int * pwidth,int * bswidth)226838f5788Ssimonb static char * cmd_step_right(char **pp, int *pwidth, int *bswidth)
22720006a0bStron {
22820006a0bStron char *p = *pp;
22920006a0bStron LWCHAR ch = step_char(pp, +1, p + strlen(p));
23020006a0bStron
23120006a0bStron return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
23220006a0bStron }
23320006a0bStron
23420006a0bStron /*
23520006a0bStron * Step a pointer one character left in the command buffer.
23620006a0bStron */
cmd_step_left(char ** pp,int * pwidth,int * bswidth)237838f5788Ssimonb static char * cmd_step_left(char **pp, int *pwidth, int *bswidth)
23820006a0bStron {
23920006a0bStron char *p = *pp;
24020006a0bStron LWCHAR ch = step_char(pp, -1, cmdbuf);
24120006a0bStron
24220006a0bStron return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
24320006a0bStron }
24420006a0bStron
24520006a0bStron /*
246838f5788Ssimonb * Put the cursor at "home" (just after the prompt),
247838f5788Ssimonb * and set cp to the corresponding char in cmdbuf.
248838f5788Ssimonb */
cmd_home(void)249838f5788Ssimonb static void cmd_home(void)
250838f5788Ssimonb {
251838f5788Ssimonb while (cmd_col > prompt_col)
252838f5788Ssimonb {
253838f5788Ssimonb int width, bswidth;
254838f5788Ssimonb
255838f5788Ssimonb cmd_step_left(&cp, &width, &bswidth);
256838f5788Ssimonb while (bswidth-- > 0)
257838f5788Ssimonb putbs();
258838f5788Ssimonb cmd_col -= width;
259838f5788Ssimonb }
260838f5788Ssimonb
261838f5788Ssimonb cp = &cmdbuf[cmd_offset];
262838f5788Ssimonb }
263838f5788Ssimonb
264838f5788Ssimonb /*
26520006a0bStron * Repaint the line from cp onwards.
26620006a0bStron * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
26720006a0bStron */
cmd_repaint(constant char * old_cp)268838f5788Ssimonb public void cmd_repaint(constant char *old_cp)
26920006a0bStron {
27020006a0bStron /*
27120006a0bStron * Repaint the line from the current position.
27220006a0bStron */
273838f5788Ssimonb if (old_cp == NULL)
274838f5788Ssimonb {
275838f5788Ssimonb old_cp = cp;
276838f5788Ssimonb cmd_home();
277838f5788Ssimonb }
27820006a0bStron clear_eol();
27920006a0bStron while (*cp != '\0')
28020006a0bStron {
28120006a0bStron char *np = cp;
28220006a0bStron int width;
28320006a0bStron char *pr = cmd_step_right(&np, &width, NULL);
28420006a0bStron if (cmd_col + width >= sc_width)
28520006a0bStron break;
28620006a0bStron cp = np;
28720006a0bStron putstr(pr);
28820006a0bStron cmd_col += width;
28920006a0bStron }
29020006a0bStron while (*cp != '\0')
29120006a0bStron {
29220006a0bStron char *np = cp;
29320006a0bStron int width;
29420006a0bStron char *pr = cmd_step_right(&np, &width, NULL);
29520006a0bStron if (width > 0)
29620006a0bStron break;
29720006a0bStron cp = np;
29820006a0bStron putstr(pr);
29920006a0bStron }
30020006a0bStron
30120006a0bStron /*
30220006a0bStron * Back up the cursor to the correct position.
30320006a0bStron */
30420006a0bStron while (cp > old_cp)
30520006a0bStron cmd_left();
30620006a0bStron }
30720006a0bStron
30820006a0bStron /*
30920006a0bStron * Shift the cmdbuf display left a half-screen.
31020006a0bStron */
cmd_lshift(void)311838f5788Ssimonb static void cmd_lshift(void)
31220006a0bStron {
31320006a0bStron char *s;
31420006a0bStron char *save_cp;
31520006a0bStron int cols;
31620006a0bStron
31720006a0bStron /*
31820006a0bStron * Start at the first displayed char, count how far to the
31920006a0bStron * right we'd have to move to reach the center of the screen.
32020006a0bStron */
32120006a0bStron s = cmdbuf + cmd_offset;
32220006a0bStron cols = 0;
32320006a0bStron while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
32420006a0bStron {
32520006a0bStron int width;
32620006a0bStron cmd_step_right(&s, &width, NULL);
32720006a0bStron cols += width;
32820006a0bStron }
32920006a0bStron while (*s != '\0')
33020006a0bStron {
33120006a0bStron int width;
33220006a0bStron char *ns = s;
33320006a0bStron cmd_step_right(&ns, &width, NULL);
33420006a0bStron if (width > 0)
33520006a0bStron break;
33620006a0bStron s = ns;
33720006a0bStron }
33820006a0bStron
339838f5788Ssimonb cmd_offset = (int) (s - cmdbuf);
34020006a0bStron save_cp = cp;
34120006a0bStron cmd_home();
34220006a0bStron cmd_repaint(save_cp);
34320006a0bStron }
34420006a0bStron
34520006a0bStron /*
34620006a0bStron * Shift the cmdbuf display right a half-screen.
34720006a0bStron */
cmd_rshift(void)348838f5788Ssimonb static void cmd_rshift(void)
34920006a0bStron {
35020006a0bStron char *s;
35120006a0bStron char *save_cp;
35220006a0bStron int cols;
35320006a0bStron
35420006a0bStron /*
35520006a0bStron * Start at the first displayed char, count how far to the
35620006a0bStron * left we'd have to move to traverse a half-screen width
35720006a0bStron * of displayed characters.
35820006a0bStron */
35920006a0bStron s = cmdbuf + cmd_offset;
36020006a0bStron cols = 0;
36120006a0bStron while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
36220006a0bStron {
36320006a0bStron int width;
36420006a0bStron cmd_step_left(&s, &width, NULL);
36520006a0bStron cols += width;
36620006a0bStron }
36720006a0bStron
368838f5788Ssimonb cmd_offset = (int) (s - cmdbuf);
36920006a0bStron save_cp = cp;
37020006a0bStron cmd_home();
37120006a0bStron cmd_repaint(save_cp);
37220006a0bStron }
37320006a0bStron
37420006a0bStron /*
37520006a0bStron * Move cursor right one character.
37620006a0bStron */
cmd_right(void)377838f5788Ssimonb static int cmd_right(void)
37820006a0bStron {
37920006a0bStron char *pr;
38020006a0bStron char *ncp;
38120006a0bStron int width;
38220006a0bStron
38320006a0bStron if (*cp == '\0')
38420006a0bStron {
38520006a0bStron /* Already at the end of the line. */
38620006a0bStron return (CC_OK);
38720006a0bStron }
38820006a0bStron ncp = cp;
38920006a0bStron pr = cmd_step_right(&ncp, &width, NULL);
39020006a0bStron if (cmd_col + width >= sc_width)
39120006a0bStron cmd_lshift();
39220006a0bStron else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
39320006a0bStron cmd_lshift();
39420006a0bStron cp = ncp;
39520006a0bStron cmd_col += width;
39620006a0bStron putstr(pr);
39720006a0bStron while (*cp != '\0')
39820006a0bStron {
39920006a0bStron pr = cmd_step_right(&ncp, &width, NULL);
40020006a0bStron if (width > 0)
40120006a0bStron break;
40220006a0bStron putstr(pr);
40320006a0bStron cp = ncp;
40420006a0bStron }
40520006a0bStron return (CC_OK);
40620006a0bStron }
40720006a0bStron
40820006a0bStron /*
40920006a0bStron * Move cursor left one character.
41020006a0bStron */
cmd_left(void)411838f5788Ssimonb static int cmd_left(void)
41220006a0bStron {
41320006a0bStron char *ncp;
414838f5788Ssimonb int width = 0;
415838f5788Ssimonb int bswidth = 0;
41620006a0bStron
41720006a0bStron if (cp <= cmdbuf)
41820006a0bStron {
41920006a0bStron /* Already at the beginning of the line */
42020006a0bStron return (CC_OK);
42120006a0bStron }
42220006a0bStron ncp = cp;
42320006a0bStron while (ncp > cmdbuf)
42420006a0bStron {
42520006a0bStron cmd_step_left(&ncp, &width, &bswidth);
42620006a0bStron if (width > 0)
42720006a0bStron break;
42820006a0bStron }
42920006a0bStron if (cmd_col < prompt_col + width)
43020006a0bStron cmd_rshift();
43120006a0bStron cp = ncp;
43220006a0bStron cmd_col -= width;
43320006a0bStron while (bswidth-- > 0)
43420006a0bStron putbs();
43520006a0bStron return (CC_OK);
43620006a0bStron }
43720006a0bStron
43820006a0bStron /*
43920006a0bStron * Insert a char into the command buffer, at the current position.
44020006a0bStron */
cmd_ichar(char * cs,int clen)441838f5788Ssimonb static int cmd_ichar(char *cs, int clen)
44220006a0bStron {
44320006a0bStron char *s;
44420006a0bStron
44520006a0bStron if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
44620006a0bStron {
44720006a0bStron /* No room in the command buffer for another char. */
44820006a0bStron bell();
44920006a0bStron return (CC_ERROR);
45020006a0bStron }
45120006a0bStron
45220006a0bStron /*
45320006a0bStron * Make room for the new character (shift the tail of the buffer right).
45420006a0bStron */
45520006a0bStron for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--)
45620006a0bStron s[clen] = s[0];
45720006a0bStron /*
45820006a0bStron * Insert the character into the buffer.
45920006a0bStron */
46020006a0bStron for (s = cp; s < cp + clen; s++)
46120006a0bStron *s = *cs++;
46220006a0bStron /*
46320006a0bStron * Reprint the tail of the line from the inserted char.
46420006a0bStron */
465ec18bca0Stron updown_match = -1;
46620006a0bStron cmd_repaint(cp);
46720006a0bStron cmd_right();
46820006a0bStron return (CC_OK);
46920006a0bStron }
47020006a0bStron
47120006a0bStron /*
47220006a0bStron * Backspace in the command buffer.
47320006a0bStron * Delete the char to the left of the cursor.
47420006a0bStron */
cmd_erase(void)475838f5788Ssimonb static int cmd_erase(void)
47620006a0bStron {
477838f5788Ssimonb char *s;
47820006a0bStron int clen;
47920006a0bStron
48020006a0bStron if (cp == cmdbuf)
48120006a0bStron {
48220006a0bStron /*
48320006a0bStron * Backspace past beginning of the buffer:
48420006a0bStron * this usually means abort the command.
48520006a0bStron */
48620006a0bStron return (CC_QUIT);
48720006a0bStron }
48820006a0bStron /*
48920006a0bStron * Move cursor left (to the char being erased).
49020006a0bStron */
49120006a0bStron s = cp;
49220006a0bStron cmd_left();
493838f5788Ssimonb clen = (int) (s - cp);
49420006a0bStron
49520006a0bStron /*
49620006a0bStron * Remove the char from the buffer (shift the buffer left).
49720006a0bStron */
49820006a0bStron for (s = cp; ; s++)
49920006a0bStron {
50020006a0bStron s[0] = s[clen];
50120006a0bStron if (s[0] == '\0')
50220006a0bStron break;
50320006a0bStron }
50420006a0bStron
50520006a0bStron /*
50620006a0bStron * Repaint the buffer after the erased char.
50720006a0bStron */
508ec18bca0Stron updown_match = -1;
50920006a0bStron cmd_repaint(cp);
51020006a0bStron
51120006a0bStron /*
51220006a0bStron * We say that erasing the entire command string causes us
51320006a0bStron * to abort the current command, if CF_QUIT_ON_ERASE is set.
51420006a0bStron */
51520006a0bStron if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
51620006a0bStron return (CC_QUIT);
51720006a0bStron return (CC_OK);
51820006a0bStron }
51920006a0bStron
52020006a0bStron /*
52120006a0bStron * Delete the char under the cursor.
52220006a0bStron */
cmd_delete(void)523838f5788Ssimonb static int cmd_delete(void)
52420006a0bStron {
52520006a0bStron if (*cp == '\0')
52620006a0bStron {
52720006a0bStron /* At end of string; there is no char under the cursor. */
52820006a0bStron return (CC_OK);
52920006a0bStron }
53020006a0bStron /*
53120006a0bStron * Move right, then use cmd_erase.
53220006a0bStron */
53320006a0bStron cmd_right();
53420006a0bStron cmd_erase();
53520006a0bStron return (CC_OK);
53620006a0bStron }
53720006a0bStron
53820006a0bStron /*
53920006a0bStron * Delete the "word" to the left of the cursor.
54020006a0bStron */
cmd_werase(void)541838f5788Ssimonb static int cmd_werase(void)
54220006a0bStron {
54320006a0bStron if (cp > cmdbuf && cp[-1] == ' ')
54420006a0bStron {
54520006a0bStron /*
54620006a0bStron * If the char left of cursor is a space,
54720006a0bStron * erase all the spaces left of cursor (to the first non-space).
54820006a0bStron */
54920006a0bStron while (cp > cmdbuf && cp[-1] == ' ')
55020006a0bStron (void) cmd_erase();
55120006a0bStron } else
55220006a0bStron {
55320006a0bStron /*
55420006a0bStron * If the char left of cursor is not a space,
55520006a0bStron * erase all the nonspaces left of cursor (the whole "word").
55620006a0bStron */
55720006a0bStron while (cp > cmdbuf && cp[-1] != ' ')
55820006a0bStron (void) cmd_erase();
55920006a0bStron }
56020006a0bStron return (CC_OK);
56120006a0bStron }
56220006a0bStron
56320006a0bStron /*
56420006a0bStron * Delete the "word" under the cursor.
56520006a0bStron */
cmd_wdelete(void)566838f5788Ssimonb static int cmd_wdelete(void)
56720006a0bStron {
56820006a0bStron if (*cp == ' ')
56920006a0bStron {
57020006a0bStron /*
57120006a0bStron * If the char under the cursor is a space,
57220006a0bStron * delete it and all the spaces right of cursor.
57320006a0bStron */
57420006a0bStron while (*cp == ' ')
57520006a0bStron (void) cmd_delete();
57620006a0bStron } else
57720006a0bStron {
57820006a0bStron /*
57920006a0bStron * If the char under the cursor is not a space,
58020006a0bStron * delete it and all nonspaces right of cursor (the whole word).
58120006a0bStron */
58220006a0bStron while (*cp != ' ' && *cp != '\0')
58320006a0bStron (void) cmd_delete();
58420006a0bStron }
58520006a0bStron return (CC_OK);
58620006a0bStron }
58720006a0bStron
58820006a0bStron /*
58920006a0bStron * Delete all chars in the command buffer.
59020006a0bStron */
cmd_kill(void)591838f5788Ssimonb static int cmd_kill(void)
59220006a0bStron {
59320006a0bStron if (cmdbuf[0] == '\0')
59420006a0bStron {
59520006a0bStron /* Buffer is already empty; abort the current command. */
59620006a0bStron return (CC_QUIT);
59720006a0bStron }
59820006a0bStron cmd_offset = 0;
59920006a0bStron cmd_home();
60020006a0bStron *cp = '\0';
601ec18bca0Stron updown_match = -1;
60220006a0bStron cmd_repaint(cp);
60320006a0bStron
60420006a0bStron /*
60520006a0bStron * We say that erasing the entire command string causes us
60620006a0bStron * to abort the current command, if CF_QUIT_ON_ERASE is set.
60720006a0bStron */
60820006a0bStron if (curr_cmdflags & CF_QUIT_ON_ERASE)
60920006a0bStron return (CC_QUIT);
61020006a0bStron return (CC_OK);
61120006a0bStron }
61220006a0bStron
61320006a0bStron /*
61420006a0bStron * Select an mlist structure to be the current command history.
61520006a0bStron */
set_mlist(void * mlist,int cmdflags)616838f5788Ssimonb public void set_mlist(void *mlist, int cmdflags)
61720006a0bStron {
61820006a0bStron #if CMD_HISTORY
61920006a0bStron curr_mlist = (struct mlist *) mlist;
62020006a0bStron curr_cmdflags = cmdflags;
62120006a0bStron
62220006a0bStron /* Make sure the next up-arrow moves to the last string in the mlist. */
62320006a0bStron if (curr_mlist != NULL)
62420006a0bStron curr_mlist->curr_mp = curr_mlist;
62520006a0bStron #endif
62620006a0bStron }
62720006a0bStron
62820006a0bStron #if CMD_HISTORY
62920006a0bStron /*
63020006a0bStron * Move up or down in the currently selected command history list.
631ec18bca0Stron * Only consider entries whose first updown_match chars are equal to
632ec18bca0Stron * cmdbuf's corresponding chars.
63320006a0bStron */
cmd_updown(int action)634838f5788Ssimonb static int cmd_updown(int action)
63520006a0bStron {
636838f5788Ssimonb constant char *s;
637ec18bca0Stron struct mlist *ml;
63820006a0bStron
63920006a0bStron if (curr_mlist == NULL)
64020006a0bStron {
64120006a0bStron /*
64220006a0bStron * The current command has no history list.
64320006a0bStron */
64420006a0bStron bell();
64520006a0bStron return (CC_OK);
64620006a0bStron }
647ec18bca0Stron
648ec18bca0Stron if (updown_match < 0)
649ec18bca0Stron {
650838f5788Ssimonb updown_match = (int) (cp - cmdbuf);
651ec18bca0Stron }
652ec18bca0Stron
65320006a0bStron /*
654ec18bca0Stron * Find the next history entry which matches.
65520006a0bStron */
656ec18bca0Stron for (ml = curr_mlist->curr_mp;;)
657ec18bca0Stron {
658ec18bca0Stron ml = (action == EC_UP) ? ml->prev : ml->next;
659ec18bca0Stron if (ml == curr_mlist)
660ec18bca0Stron {
66120006a0bStron /*
662ec18bca0Stron * We reached the end (or beginning) of the list.
663ec18bca0Stron */
664ec18bca0Stron break;
665ec18bca0Stron }
666ec18bca0Stron if (strncmp(cmdbuf, ml->string, updown_match) == 0)
667ec18bca0Stron {
668ec18bca0Stron /*
669ec18bca0Stron * This entry matches; stop here.
67020006a0bStron * Copy the entry into cmdbuf and echo it on the screen.
67120006a0bStron */
672ec18bca0Stron curr_mlist->curr_mp = ml;
673ec18bca0Stron s = ml->string;
67420006a0bStron if (s == NULL)
67520006a0bStron s = "";
676838f5788Ssimonb cmd_offset = 0;
677ec18bca0Stron cmd_home();
678ec18bca0Stron clear_eol();
67920006a0bStron strcpy(cmdbuf, s);
68020006a0bStron for (cp = cmdbuf; *cp != '\0'; )
68120006a0bStron cmd_right();
68220006a0bStron return (CC_OK);
68320006a0bStron }
684ec18bca0Stron }
685ec18bca0Stron /*
686ec18bca0Stron * We didn't find a history entry that matches.
687ec18bca0Stron */
688ec18bca0Stron bell();
689ec18bca0Stron return (CC_OK);
690ec18bca0Stron }
69120006a0bStron #endif
69220006a0bStron
69320006a0bStron /*
694838f5788Ssimonb *
69520006a0bStron */
ml_link(struct mlist * mlist,struct mlist * ml)696838f5788Ssimonb static void ml_link(struct mlist *mlist, struct mlist *ml)
697838f5788Ssimonb {
698838f5788Ssimonb ml->next = mlist;
699838f5788Ssimonb ml->prev = mlist->prev;
700838f5788Ssimonb mlist->prev->next = ml;
701838f5788Ssimonb mlist->prev = ml;
702838f5788Ssimonb }
703838f5788Ssimonb
704838f5788Ssimonb /*
705838f5788Ssimonb *
706838f5788Ssimonb */
ml_unlink(struct mlist * ml)707838f5788Ssimonb static void ml_unlink(struct mlist *ml)
708838f5788Ssimonb {
709838f5788Ssimonb ml->prev->next = ml->next;
710838f5788Ssimonb ml->next->prev = ml->prev;
711838f5788Ssimonb }
712838f5788Ssimonb
713838f5788Ssimonb /*
714838f5788Ssimonb * Add a string to an mlist.
715838f5788Ssimonb */
cmd_addhist(struct mlist * mlist,constant char * cmd,int modified)716838f5788Ssimonb public void cmd_addhist(struct mlist *mlist, constant char *cmd, int modified)
71720006a0bStron {
71820006a0bStron #if CMD_HISTORY
71920006a0bStron struct mlist *ml;
72020006a0bStron
72120006a0bStron /*
72220006a0bStron * Don't save a trivial command.
72320006a0bStron */
72420006a0bStron if (strlen(cmd) == 0)
72520006a0bStron return;
72620006a0bStron
727838f5788Ssimonb if (no_hist_dups)
728838f5788Ssimonb {
729838f5788Ssimonb struct mlist *next = NULL;
730838f5788Ssimonb for (ml = mlist->next; ml->string != NULL; ml = next)
731838f5788Ssimonb {
732838f5788Ssimonb next = ml->next;
733838f5788Ssimonb if (strcmp(ml->string, cmd) == 0)
734838f5788Ssimonb {
735838f5788Ssimonb ml_unlink(ml);
736838f5788Ssimonb free(ml->string);
737838f5788Ssimonb free(ml);
738838f5788Ssimonb }
739838f5788Ssimonb }
740838f5788Ssimonb }
741838f5788Ssimonb
74220006a0bStron /*
74320006a0bStron * Save the command unless it's a duplicate of the
74420006a0bStron * last command in the history.
74520006a0bStron */
74620006a0bStron ml = mlist->prev;
74720006a0bStron if (ml == mlist || strcmp(ml->string, cmd) != 0)
74820006a0bStron {
74920006a0bStron /*
75020006a0bStron * Did not find command in history.
75120006a0bStron * Save the command and put it at the end of the history list.
75220006a0bStron */
75320006a0bStron ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
75420006a0bStron ml->string = save(cmd);
755838f5788Ssimonb ml->modified = modified;
756838f5788Ssimonb ml_link(mlist, ml);
75720006a0bStron }
75820006a0bStron /*
75920006a0bStron * Point to the cmd just after the just-accepted command.
76020006a0bStron * Thus, an UPARROW will always retrieve the previous command.
76120006a0bStron */
76220006a0bStron mlist->curr_mp = ml->next;
76320006a0bStron #endif
76420006a0bStron }
76520006a0bStron
76620006a0bStron /*
76720006a0bStron * Accept the command in the command buffer.
76820006a0bStron * Add it to the currently selected history list.
76920006a0bStron */
cmd_accept(void)770838f5788Ssimonb public void cmd_accept(void)
77120006a0bStron {
77220006a0bStron #if CMD_HISTORY
77320006a0bStron /*
77420006a0bStron * Nothing to do if there is no currently selected history list.
77520006a0bStron */
776838f5788Ssimonb if (curr_mlist == NULL || curr_mlist == ml_examine)
77720006a0bStron return;
778838f5788Ssimonb cmd_addhist(curr_mlist, cmdbuf, 1);
77920006a0bStron curr_mlist->modified = 1;
78020006a0bStron #endif
78120006a0bStron }
78220006a0bStron
78320006a0bStron /*
78420006a0bStron * Try to perform a line-edit function on the command buffer,
78520006a0bStron * using a specified char as a line-editing command.
78620006a0bStron * Returns:
78720006a0bStron * CC_PASS The char does not invoke a line edit function.
78820006a0bStron * CC_OK Line edit function done.
78920006a0bStron * CC_QUIT The char requests the current command to be aborted.
79020006a0bStron */
cmd_edit(int c)791838f5788Ssimonb static int cmd_edit(int c)
79220006a0bStron {
79320006a0bStron int action;
79420006a0bStron int flags;
79520006a0bStron
79620006a0bStron #if TAB_COMPLETE_FILENAME
79720006a0bStron #define not_in_completion() in_completion = 0
79820006a0bStron #else
799838f5788Ssimonb #define not_in_completion(void)
80020006a0bStron #endif
80120006a0bStron
80220006a0bStron /*
80320006a0bStron * See if the char is indeed a line-editing command.
80420006a0bStron */
80520006a0bStron flags = 0;
80620006a0bStron #if CMD_HISTORY
80720006a0bStron if (curr_mlist == NULL)
80820006a0bStron /*
80920006a0bStron * No current history; don't accept history manipulation cmds.
81020006a0bStron */
811838f5788Ssimonb flags |= ECF_NOHISTORY;
81220006a0bStron #endif
81320006a0bStron #if TAB_COMPLETE_FILENAME
814838f5788Ssimonb if (curr_mlist == ml_search || curr_mlist == NULL)
81520006a0bStron /*
816838f5788Ssimonb * Don't accept file-completion cmds in contexts
817838f5788Ssimonb * such as search pattern, digits, long option name, etc.
81820006a0bStron */
819838f5788Ssimonb flags |= ECF_NOCOMPLETE;
82020006a0bStron #endif
82120006a0bStron
82220006a0bStron action = editchar(c, flags);
82320006a0bStron
82420006a0bStron switch (action)
82520006a0bStron {
826838f5788Ssimonb case A_NOACTION:
827838f5788Ssimonb return (CC_OK);
82820006a0bStron case EC_RIGHT:
82920006a0bStron not_in_completion();
83020006a0bStron return (cmd_right());
83120006a0bStron case EC_LEFT:
83220006a0bStron not_in_completion();
83320006a0bStron return (cmd_left());
83420006a0bStron case EC_W_RIGHT:
83520006a0bStron not_in_completion();
83620006a0bStron while (*cp != '\0' && *cp != ' ')
83720006a0bStron cmd_right();
83820006a0bStron while (*cp == ' ')
83920006a0bStron cmd_right();
84020006a0bStron return (CC_OK);
84120006a0bStron case EC_W_LEFT:
84220006a0bStron not_in_completion();
84320006a0bStron while (cp > cmdbuf && cp[-1] == ' ')
84420006a0bStron cmd_left();
84520006a0bStron while (cp > cmdbuf && cp[-1] != ' ')
84620006a0bStron cmd_left();
84720006a0bStron return (CC_OK);
84820006a0bStron case EC_HOME:
84920006a0bStron not_in_completion();
85020006a0bStron cmd_offset = 0;
85120006a0bStron cmd_home();
85220006a0bStron cmd_repaint(cp);
85320006a0bStron return (CC_OK);
85420006a0bStron case EC_END:
85520006a0bStron not_in_completion();
85620006a0bStron while (*cp != '\0')
85720006a0bStron cmd_right();
85820006a0bStron return (CC_OK);
85920006a0bStron case EC_INSERT:
86020006a0bStron not_in_completion();
86120006a0bStron return (CC_OK);
86220006a0bStron case EC_BACKSPACE:
86320006a0bStron not_in_completion();
86420006a0bStron return (cmd_erase());
86520006a0bStron case EC_LINEKILL:
86620006a0bStron not_in_completion();
86720006a0bStron return (cmd_kill());
86820006a0bStron case EC_ABORT:
86920006a0bStron not_in_completion();
87020006a0bStron (void) cmd_kill();
87120006a0bStron return (CC_QUIT);
87220006a0bStron case EC_W_BACKSPACE:
87320006a0bStron not_in_completion();
87420006a0bStron return (cmd_werase());
87520006a0bStron case EC_DELETE:
87620006a0bStron not_in_completion();
87720006a0bStron return (cmd_delete());
87820006a0bStron case EC_W_DELETE:
87920006a0bStron not_in_completion();
88020006a0bStron return (cmd_wdelete());
88120006a0bStron case EC_LITERAL:
88220006a0bStron literal = 1;
88320006a0bStron return (CC_OK);
88420006a0bStron #if CMD_HISTORY
88520006a0bStron case EC_UP:
88620006a0bStron case EC_DOWN:
88720006a0bStron not_in_completion();
88820006a0bStron return (cmd_updown(action));
88920006a0bStron #endif
89020006a0bStron #if TAB_COMPLETE_FILENAME
89120006a0bStron case EC_F_COMPLETE:
89220006a0bStron case EC_B_COMPLETE:
89320006a0bStron case EC_EXPAND:
89420006a0bStron return (cmd_complete(action));
89520006a0bStron #endif
89620006a0bStron default:
89720006a0bStron not_in_completion();
89820006a0bStron return (CC_PASS);
89920006a0bStron }
90020006a0bStron }
90120006a0bStron
90220006a0bStron #if TAB_COMPLETE_FILENAME
90320006a0bStron /*
90420006a0bStron * Insert a string into the command buffer, at the current position.
90520006a0bStron */
cmd_istr(char * str)906838f5788Ssimonb static int cmd_istr(char *str)
90720006a0bStron {
90820006a0bStron char *s;
90920006a0bStron int action;
91020006a0bStron char *endline = str + strlen(str);
91120006a0bStron
91220006a0bStron for (s = str; *s != '\0'; )
91320006a0bStron {
91420006a0bStron char *os = s;
91520006a0bStron step_char(&s, +1, endline);
91620006a0bStron action = cmd_ichar(os, s - os);
91720006a0bStron if (action != CC_OK)
91820006a0bStron return (action);
91920006a0bStron }
92020006a0bStron return (CC_OK);
92120006a0bStron }
92220006a0bStron
92320006a0bStron /*
92420006a0bStron * Find the beginning and end of the "current" word.
92520006a0bStron * This is the word which the cursor (cp) is inside or at the end of.
92620006a0bStron * Return pointer to the beginning of the word and put the
92720006a0bStron * cursor at the end of the word.
92820006a0bStron */
delimit_word(void)929838f5788Ssimonb static char * delimit_word(void)
93020006a0bStron {
931838f5788Ssimonb char *word;
93220006a0bStron #if SPACES_IN_FILENAMES
93320006a0bStron char *p;
93420006a0bStron int delim_quoted = 0;
93520006a0bStron int meta_quoted = 0;
936838f5788Ssimonb constant char *esc = get_meta_escape();
937838f5788Ssimonb int esclen = (int) strlen(esc);
93820006a0bStron #endif
93920006a0bStron
94020006a0bStron /*
94120006a0bStron * Move cursor to end of word.
94220006a0bStron */
94320006a0bStron if (*cp != ' ' && *cp != '\0')
94420006a0bStron {
94520006a0bStron /*
94620006a0bStron * Cursor is on a nonspace.
94720006a0bStron * Move cursor right to the next space.
94820006a0bStron */
94920006a0bStron while (*cp != ' ' && *cp != '\0')
95020006a0bStron cmd_right();
95120006a0bStron } else if (cp > cmdbuf && cp[-1] != ' ')
95220006a0bStron {
95320006a0bStron /*
95420006a0bStron * Cursor is on a space, and char to the left is a nonspace.
95520006a0bStron * We're already at the end of the word.
95620006a0bStron */
95720006a0bStron ;
95820006a0bStron #if 0
95920006a0bStron } else
96020006a0bStron {
96120006a0bStron /*
96220006a0bStron * Cursor is on a space and char to the left is a space.
96320006a0bStron * Huh? There's no word here.
96420006a0bStron */
96520006a0bStron return (NULL);
96620006a0bStron #endif
96720006a0bStron }
96820006a0bStron /*
96920006a0bStron * Find the beginning of the word which the cursor is in.
97020006a0bStron */
97120006a0bStron if (cp == cmdbuf)
97220006a0bStron return (NULL);
97320006a0bStron #if SPACES_IN_FILENAMES
97420006a0bStron /*
97520006a0bStron * If we have an unbalanced quote (that is, an open quote
97620006a0bStron * without a corresponding close quote), we return everything
97720006a0bStron * from the open quote, including spaces.
97820006a0bStron */
97920006a0bStron for (word = cmdbuf; word < cp; word++)
98020006a0bStron if (*word != ' ')
98120006a0bStron break;
98220006a0bStron if (word >= cp)
98320006a0bStron return (cp);
98420006a0bStron for (p = cmdbuf; p < cp; p++)
98520006a0bStron {
98620006a0bStron if (meta_quoted)
98720006a0bStron {
98820006a0bStron meta_quoted = 0;
98920006a0bStron } else if (esclen > 0 && p + esclen < cp &&
99020006a0bStron strncmp(p, esc, esclen) == 0)
99120006a0bStron {
99220006a0bStron meta_quoted = 1;
99320006a0bStron p += esclen - 1;
99420006a0bStron } else if (delim_quoted)
99520006a0bStron {
99620006a0bStron if (*p == closequote)
99720006a0bStron delim_quoted = 0;
99820006a0bStron } else /* (!delim_quoted) */
99920006a0bStron {
100020006a0bStron if (*p == openquote)
100120006a0bStron delim_quoted = 1;
100220006a0bStron else if (*p == ' ')
100320006a0bStron word = p+1;
100420006a0bStron }
100520006a0bStron }
100620006a0bStron #endif
100720006a0bStron return (word);
100820006a0bStron }
100920006a0bStron
101020006a0bStron /*
101120006a0bStron * Set things up to enter completion mode.
101220006a0bStron * Expand the word under the cursor into a list of filenames
101320006a0bStron * which start with that word, and set tk_text to that list.
101420006a0bStron */
init_compl(void)1015838f5788Ssimonb static void init_compl(void)
101620006a0bStron {
101720006a0bStron char *word;
101820006a0bStron char c;
101920006a0bStron
102020006a0bStron /*
102120006a0bStron * Get rid of any previous tk_text.
102220006a0bStron */
102320006a0bStron if (tk_text != NULL)
102420006a0bStron {
102520006a0bStron free(tk_text);
102620006a0bStron tk_text = NULL;
102720006a0bStron }
102820006a0bStron /*
102920006a0bStron * Find the original (uncompleted) word in the command buffer.
103020006a0bStron */
103120006a0bStron word = delimit_word();
103220006a0bStron if (word == NULL)
103320006a0bStron return;
103420006a0bStron /*
103520006a0bStron * Set the insertion point to the point in the command buffer
103620006a0bStron * where the original (uncompleted) word now sits.
103720006a0bStron */
103820006a0bStron tk_ipoint = word;
103920006a0bStron /*
104020006a0bStron * Save the original (uncompleted) word
104120006a0bStron */
104220006a0bStron if (tk_original != NULL)
104320006a0bStron free(tk_original);
104420006a0bStron tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
104520006a0bStron strncpy(tk_original, word, cp-word);
104620006a0bStron /*
104720006a0bStron * Get the expanded filename.
104820006a0bStron * This may result in a single filename, or
104920006a0bStron * a blank-separated list of filenames.
105020006a0bStron */
105120006a0bStron c = *cp;
105220006a0bStron *cp = '\0';
105320006a0bStron if (*word != openquote)
105420006a0bStron {
105520006a0bStron tk_text = fcomplete(word);
105620006a0bStron } else
105720006a0bStron {
1058ec18bca0Stron #if MSDOS_COMPILER
1059ec18bca0Stron char *qword = NULL;
1060ec18bca0Stron #else
106120006a0bStron char *qword = shell_quote(word+1);
1062ec18bca0Stron #endif
106320006a0bStron if (qword == NULL)
106420006a0bStron tk_text = fcomplete(word+1);
106520006a0bStron else
106620006a0bStron {
106720006a0bStron tk_text = fcomplete(qword);
106820006a0bStron free(qword);
106920006a0bStron }
107020006a0bStron }
107120006a0bStron *cp = c;
107220006a0bStron }
107320006a0bStron
107420006a0bStron /*
107520006a0bStron * Return the next word in the current completion list.
107620006a0bStron */
next_compl(int action,char * prev)1077838f5788Ssimonb static char * next_compl(int action, char *prev)
107820006a0bStron {
107920006a0bStron switch (action)
108020006a0bStron {
108120006a0bStron case EC_F_COMPLETE:
108220006a0bStron return (forw_textlist(&tk_tlist, prev));
108320006a0bStron case EC_B_COMPLETE:
108420006a0bStron return (back_textlist(&tk_tlist, prev));
108520006a0bStron }
108620006a0bStron /* Cannot happen */
108720006a0bStron return ("?");
108820006a0bStron }
108920006a0bStron
109020006a0bStron /*
109120006a0bStron * Complete the filename before (or under) the cursor.
109220006a0bStron * cmd_complete may be called multiple times. The global in_completion
109320006a0bStron * remembers whether this call is the first time (create the list),
109420006a0bStron * or a subsequent time (step thru the list).
109520006a0bStron */
cmd_complete(int action)1096838f5788Ssimonb static int cmd_complete(int action)
109720006a0bStron {
109820006a0bStron char *s;
109920006a0bStron
110020006a0bStron if (!in_completion || action == EC_EXPAND)
110120006a0bStron {
110220006a0bStron /*
110320006a0bStron * Expand the word under the cursor and
110420006a0bStron * use the first word in the expansion
110520006a0bStron * (or the entire expansion if we're doing EC_EXPAND).
110620006a0bStron */
110720006a0bStron init_compl();
110820006a0bStron if (tk_text == NULL)
110920006a0bStron {
111020006a0bStron bell();
111120006a0bStron return (CC_OK);
111220006a0bStron }
111320006a0bStron if (action == EC_EXPAND)
111420006a0bStron {
111520006a0bStron /*
111620006a0bStron * Use the whole list.
111720006a0bStron */
111820006a0bStron tk_trial = tk_text;
111920006a0bStron } else
112020006a0bStron {
112120006a0bStron /*
112220006a0bStron * Use the first filename in the list.
112320006a0bStron */
112420006a0bStron in_completion = 1;
112520006a0bStron init_textlist(&tk_tlist, tk_text);
112620006a0bStron tk_trial = next_compl(action, (char*)NULL);
112720006a0bStron }
112820006a0bStron } else
112920006a0bStron {
113020006a0bStron /*
113120006a0bStron * We already have a completion list.
113220006a0bStron * Use the next/previous filename from the list.
113320006a0bStron */
113420006a0bStron tk_trial = next_compl(action, tk_trial);
113520006a0bStron }
113620006a0bStron
113720006a0bStron /*
113820006a0bStron * Remove the original word, or the previous trial completion.
113920006a0bStron */
114020006a0bStron while (cp > tk_ipoint)
114120006a0bStron (void) cmd_erase();
114220006a0bStron
114320006a0bStron if (tk_trial == NULL)
114420006a0bStron {
114520006a0bStron /*
114620006a0bStron * There are no more trial completions.
114720006a0bStron * Insert the original (uncompleted) filename.
114820006a0bStron */
114920006a0bStron in_completion = 0;
115020006a0bStron if (cmd_istr(tk_original) != CC_OK)
115120006a0bStron goto fail;
115220006a0bStron } else
115320006a0bStron {
115420006a0bStron /*
115520006a0bStron * Insert trial completion.
115620006a0bStron */
115720006a0bStron if (cmd_istr(tk_trial) != CC_OK)
115820006a0bStron goto fail;
115920006a0bStron /*
116020006a0bStron * If it is a directory, append a slash.
116120006a0bStron */
116220006a0bStron if (is_dir(tk_trial))
116320006a0bStron {
116420006a0bStron if (cp > cmdbuf && cp[-1] == closequote)
116520006a0bStron (void) cmd_erase();
116620006a0bStron s = lgetenv("LESSSEPARATOR");
116720006a0bStron if (s == NULL)
116820006a0bStron s = PATHNAME_SEP;
116920006a0bStron if (cmd_istr(s) != CC_OK)
117020006a0bStron goto fail;
117120006a0bStron }
117220006a0bStron }
117320006a0bStron
117420006a0bStron return (CC_OK);
117520006a0bStron
117620006a0bStron fail:
117720006a0bStron in_completion = 0;
117820006a0bStron bell();
117920006a0bStron return (CC_OK);
118020006a0bStron }
118120006a0bStron
118220006a0bStron #endif /* TAB_COMPLETE_FILENAME */
118320006a0bStron
118420006a0bStron /*
118520006a0bStron * Process a single character of a multi-character command, such as
118620006a0bStron * a number, or the pattern of a search command.
118720006a0bStron * Returns:
118820006a0bStron * CC_OK The char was accepted.
118920006a0bStron * CC_QUIT The char requests the command to be aborted.
119020006a0bStron * CC_ERROR The char could not be accepted due to an error.
119120006a0bStron */
cmd_char(int c)1192838f5788Ssimonb public int cmd_char(int c)
119320006a0bStron {
119420006a0bStron int action;
119520006a0bStron int len;
119620006a0bStron
119720006a0bStron if (!utf_mode)
119820006a0bStron {
119920006a0bStron cmd_mbc_buf[0] = c;
120020006a0bStron len = 1;
120120006a0bStron } else
120220006a0bStron {
120320006a0bStron /* Perform strict validation in all possible cases. */
120420006a0bStron if (cmd_mbc_buf_len == 0)
120520006a0bStron {
120620006a0bStron retry:
120720006a0bStron cmd_mbc_buf_index = 1;
120820006a0bStron *cmd_mbc_buf = c;
120920006a0bStron if (IS_ASCII_OCTET(c))
121020006a0bStron cmd_mbc_buf_len = 1;
1211838f5788Ssimonb #if MSDOS_COMPILER || OS2
1212838f5788Ssimonb else if (c == (unsigned char) '\340' && IS_ASCII_OCTET(peekcc()))
1213838f5788Ssimonb {
1214838f5788Ssimonb /* Assume a special key. */
1215838f5788Ssimonb cmd_mbc_buf_len = 1;
1216838f5788Ssimonb }
1217838f5788Ssimonb #endif
121820006a0bStron else if (IS_UTF8_LEAD(c))
121920006a0bStron {
122020006a0bStron cmd_mbc_buf_len = utf_len(c);
122120006a0bStron return (CC_OK);
122220006a0bStron } else
122320006a0bStron {
122420006a0bStron /* UTF8_INVALID or stray UTF8_TRAIL */
122520006a0bStron bell();
122620006a0bStron return (CC_ERROR);
122720006a0bStron }
122820006a0bStron } else if (IS_UTF8_TRAIL(c))
122920006a0bStron {
123020006a0bStron cmd_mbc_buf[cmd_mbc_buf_index++] = c;
123120006a0bStron if (cmd_mbc_buf_index < cmd_mbc_buf_len)
123220006a0bStron return (CC_OK);
1233838f5788Ssimonb if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index))
123420006a0bStron {
123520006a0bStron /* complete, but not well formed (non-shortest form), sequence */
123620006a0bStron cmd_mbc_buf_len = 0;
123720006a0bStron bell();
123820006a0bStron return (CC_ERROR);
123920006a0bStron }
124020006a0bStron } else
124120006a0bStron {
124220006a0bStron /* Flush incomplete (truncated) sequence. */
124320006a0bStron cmd_mbc_buf_len = 0;
124420006a0bStron bell();
124520006a0bStron /* Handle new char. */
124620006a0bStron goto retry;
124720006a0bStron }
124820006a0bStron
124920006a0bStron len = cmd_mbc_buf_len;
125020006a0bStron cmd_mbc_buf_len = 0;
125120006a0bStron }
125220006a0bStron
125320006a0bStron if (literal)
125420006a0bStron {
125520006a0bStron /*
125620006a0bStron * Insert the char, even if it is a line-editing char.
125720006a0bStron */
125820006a0bStron literal = 0;
125920006a0bStron return (cmd_ichar(cmd_mbc_buf, len));
126020006a0bStron }
126120006a0bStron
126220006a0bStron /*
126320006a0bStron * See if it is a line-editing character.
126420006a0bStron */
126520006a0bStron if (in_mca() && len == 1)
126620006a0bStron {
126720006a0bStron action = cmd_edit(c);
126820006a0bStron switch (action)
126920006a0bStron {
127020006a0bStron case CC_OK:
127120006a0bStron case CC_QUIT:
127220006a0bStron return (action);
127320006a0bStron case CC_PASS:
127420006a0bStron break;
127520006a0bStron }
127620006a0bStron }
127720006a0bStron
127820006a0bStron /*
127920006a0bStron * Insert the char into the command buffer.
128020006a0bStron */
128120006a0bStron return (cmd_ichar(cmd_mbc_buf, len));
128220006a0bStron }
128320006a0bStron
128420006a0bStron /*
128520006a0bStron * Return the number currently in the command buffer.
128620006a0bStron */
cmd_int(long * frac)1287838f5788Ssimonb public LINENUM cmd_int(long *frac)
128820006a0bStron {
128920006a0bStron char *p;
129020006a0bStron LINENUM n = 0;
129120006a0bStron int err;
129220006a0bStron
129320006a0bStron for (p = cmdbuf; *p >= '0' && *p <= '9'; p++)
1294838f5788Ssimonb {
1295838f5788Ssimonb if (ckd_mul(&n, n, 10) || ckd_add(&n, n, *p - '0'))
1296838f5788Ssimonb {
1297838f5788Ssimonb error("Integer is too big", NULL_PARG);
1298838f5788Ssimonb return (0);
1299838f5788Ssimonb }
1300838f5788Ssimonb }
130120006a0bStron *frac = 0;
130220006a0bStron if (*p++ == '.')
130320006a0bStron {
130420006a0bStron *frac = getfraction(&p, NULL, &err);
130520006a0bStron /* {{ do something if err is set? }} */
130620006a0bStron }
130720006a0bStron return (n);
130820006a0bStron }
130920006a0bStron
131020006a0bStron /*
131120006a0bStron * Return a pointer to the command buffer.
131220006a0bStron */
get_cmdbuf(void)1313838f5788Ssimonb public char * get_cmdbuf(void)
131420006a0bStron {
1315838f5788Ssimonb if (cmd_mbc_buf_index < cmd_mbc_buf_len)
1316838f5788Ssimonb /* Don't return buffer containing an incomplete multibyte char. */
1317838f5788Ssimonb return (NULL);
131820006a0bStron return (cmdbuf);
131920006a0bStron }
132020006a0bStron
132120006a0bStron #if CMD_HISTORY
132220006a0bStron /*
132320006a0bStron * Return the last (most recent) string in the current command history.
132420006a0bStron */
cmd_lastpattern(void)1325838f5788Ssimonb public char * cmd_lastpattern(void)
132620006a0bStron {
132720006a0bStron if (curr_mlist == NULL)
132820006a0bStron return (NULL);
132920006a0bStron return (curr_mlist->curr_mp->prev->string);
133020006a0bStron }
133120006a0bStron #endif
133220006a0bStron
133320006a0bStron #if CMD_HISTORY
133420006a0bStron /*
1335838f5788Ssimonb */
mlist_size(struct mlist * ml)1336838f5788Ssimonb static int mlist_size(struct mlist *ml)
1337838f5788Ssimonb {
1338838f5788Ssimonb int size = 0;
1339838f5788Ssimonb for (ml = ml->next; ml->string != NULL; ml = ml->next)
1340838f5788Ssimonb ++size;
1341838f5788Ssimonb return size;
1342838f5788Ssimonb }
1343838f5788Ssimonb
1344838f5788Ssimonb /*
134520006a0bStron * Get the name of the history file.
134620006a0bStron */
histfile_find(int must_exist)1347838f5788Ssimonb static char * histfile_find(int must_exist)
134820006a0bStron {
1349838f5788Ssimonb char *home = lgetenv("HOME");
1350838f5788Ssimonb char *name = NULL;
1351838f5788Ssimonb
1352838f5788Ssimonb /* Try in $XDG_STATE_HOME, then in $HOME/.local/state, then in $XDG_DATA_HOME, then in $HOME. */
1353838f5788Ssimonb #if OS2
1354838f5788Ssimonb if (isnullenv(home))
1355838f5788Ssimonb home = lgetenv("INIT");
1356838f5788Ssimonb #endif
1357838f5788Ssimonb name = dirfile(lgetenv("XDG_STATE_HOME"), &LESSHISTFILE[1], must_exist);
1358838f5788Ssimonb if (name == NULL)
1359838f5788Ssimonb {
1360838f5788Ssimonb char *dir = dirfile(home, ".local/state", 1);
1361838f5788Ssimonb if (dir != NULL)
1362838f5788Ssimonb {
1363838f5788Ssimonb name = dirfile(dir, &LESSHISTFILE[1], must_exist);
1364838f5788Ssimonb free(dir);
1365838f5788Ssimonb }
1366838f5788Ssimonb }
1367838f5788Ssimonb if (name == NULL)
1368838f5788Ssimonb name = dirfile(lgetenv("XDG_DATA_HOME"), &LESSHISTFILE[1], must_exist);
1369838f5788Ssimonb if (name == NULL)
1370838f5788Ssimonb name = dirfile(home, LESSHISTFILE, must_exist);
1371838f5788Ssimonb return (name);
1372838f5788Ssimonb }
1373838f5788Ssimonb
histfile_name(int must_exist)1374838f5788Ssimonb static char * histfile_name(int must_exist)
1375838f5788Ssimonb {
137620006a0bStron char *name;
137720006a0bStron
137820006a0bStron /* See if filename is explicitly specified by $LESSHISTFILE. */
137920006a0bStron name = lgetenv("LESSHISTFILE");
1380838f5788Ssimonb if (!isnullenv(name))
138120006a0bStron {
138220006a0bStron if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
138320006a0bStron /* $LESSHISTFILE == "-" means don't use a history file. */
138420006a0bStron return (NULL);
138520006a0bStron return (save(name));
138620006a0bStron }
138720006a0bStron
1388838f5788Ssimonb /* See if history file is disabled in the build. */
1389838f5788Ssimonb if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0)
139020006a0bStron return (NULL);
1391838f5788Ssimonb
1392838f5788Ssimonb name = NULL;
1393838f5788Ssimonb if (!must_exist)
1394838f5788Ssimonb {
1395838f5788Ssimonb /* If we're writing the file and the file already exists, use it. */
1396838f5788Ssimonb name = histfile_find(1);
139720006a0bStron }
1398838f5788Ssimonb if (name == NULL)
1399838f5788Ssimonb name = histfile_find(must_exist);
140020006a0bStron return (name);
140120006a0bStron }
140220006a0bStron
140320006a0bStron /*
1404838f5788Ssimonb * Read a .lesshst file and call a callback for each line in the file.
140520006a0bStron */
read_cmdhist2(void (* action)(void *,struct mlist *,char *),void * uparam,int skip_search,int skip_shell)1406838f5788Ssimonb static void read_cmdhist2(void (*action)(void*,struct mlist*,char*), void *uparam, int skip_search, int skip_shell)
140720006a0bStron {
140820006a0bStron struct mlist *ml = NULL;
140920006a0bStron char line[CMDBUF_SIZE];
141020006a0bStron char *filename;
141120006a0bStron FILE *f;
141220006a0bStron char *p;
1413838f5788Ssimonb int *skip = NULL;
1414824a88bbStron #ifdef HAVE_STAT
1415824a88bbStron struct stat st;
1416824a88bbStron #endif
141720006a0bStron
1418838f5788Ssimonb filename = histfile_name(1);
141920006a0bStron if (filename == NULL)
142020006a0bStron return;
1421824a88bbStron #ifdef HAVE_STAT
1422824a88bbStron /* ignore devices/fifos; allow symlinks */
1423824a88bbStron if (stat(filename, &st) < 0)
1424824a88bbStron return;
1425824a88bbStron if (!S_ISREG(st.st_mode))
1426824a88bbStron return;
1427824a88bbStron #endif
142820006a0bStron f = fopen(filename, "r");
142920006a0bStron free(filename);
143020006a0bStron if (f == NULL)
143120006a0bStron return;
143220006a0bStron if (fgets(line, sizeof(line), f) == NULL ||
143320006a0bStron strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
143420006a0bStron {
143520006a0bStron fclose(f);
143620006a0bStron return;
143720006a0bStron }
143820006a0bStron while (fgets(line, sizeof(line), f) != NULL)
143920006a0bStron {
144020006a0bStron for (p = line; *p != '\0'; p++)
144120006a0bStron {
144220006a0bStron if (*p == '\n' || *p == '\r')
144320006a0bStron {
144420006a0bStron *p = '\0';
144520006a0bStron break;
144620006a0bStron }
144720006a0bStron }
144820006a0bStron if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1449838f5788Ssimonb {
145020006a0bStron ml = &mlist_search;
1451838f5788Ssimonb skip = &skip_search;
1452838f5788Ssimonb } else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
145320006a0bStron {
145420006a0bStron #if SHELL_ESCAPE || PIPEC
145520006a0bStron ml = &mlist_shell;
1456838f5788Ssimonb skip = &skip_shell;
145720006a0bStron #else
145820006a0bStron ml = NULL;
1459838f5788Ssimonb skip = NULL;
146020006a0bStron #endif
1461838f5788Ssimonb } else if (strcmp(line, HISTFILE_MARK_SECTION) == 0)
1462838f5788Ssimonb {
1463838f5788Ssimonb ml = NULL;
146420006a0bStron } else if (*line == '"')
146520006a0bStron {
146620006a0bStron if (ml != NULL)
1467838f5788Ssimonb {
1468838f5788Ssimonb if (skip != NULL && *skip > 0)
1469838f5788Ssimonb --(*skip);
1470838f5788Ssimonb else
1471838f5788Ssimonb (*action)(uparam, ml, line+1);
1472838f5788Ssimonb }
1473838f5788Ssimonb } else if (*line == 'm')
1474838f5788Ssimonb {
1475838f5788Ssimonb (*action)(uparam, NULL, line);
147620006a0bStron }
147720006a0bStron }
147820006a0bStron fclose(f);
1479838f5788Ssimonb }
1480838f5788Ssimonb
read_cmdhist(void (* action)(void *,struct mlist *,char *),void * uparam,int skip_search,int skip_shell)1481838f5788Ssimonb static void read_cmdhist(void (*action)(void*,struct mlist*,char*), void *uparam, int skip_search, int skip_shell)
1482838f5788Ssimonb {
1483838f5788Ssimonb if (secure)
1484838f5788Ssimonb return;
1485838f5788Ssimonb read_cmdhist2(action, uparam, skip_search, skip_shell);
1486838f5788Ssimonb (*action)(uparam, NULL, NULL); /* signal end of file */
1487838f5788Ssimonb }
1488838f5788Ssimonb
addhist_init(void * uparam,struct mlist * ml,char * string)1489838f5788Ssimonb static void addhist_init(void *uparam, struct mlist *ml, char *string)
1490838f5788Ssimonb {
1491838f5788Ssimonb if (ml != NULL)
1492838f5788Ssimonb cmd_addhist(ml, string, 0);
1493838f5788Ssimonb else if (string != NULL)
1494838f5788Ssimonb restore_mark((char*)string); /* stupid const cast */
1495838f5788Ssimonb }
1496838f5788Ssimonb #endif /* CMD_HISTORY */
1497838f5788Ssimonb
1498838f5788Ssimonb /*
1499838f5788Ssimonb * Initialize history from a .lesshist file.
1500838f5788Ssimonb */
init_cmdhist(void)1501838f5788Ssimonb public void init_cmdhist(void)
1502838f5788Ssimonb {
1503838f5788Ssimonb #if CMD_HISTORY
1504838f5788Ssimonb read_cmdhist(&addhist_init, NULL, 0, 0);
150520006a0bStron #endif /* CMD_HISTORY */
150620006a0bStron }
150720006a0bStron
150820006a0bStron /*
1509838f5788Ssimonb * Write the header for a section of the history file.
151020006a0bStron */
151120006a0bStron #if CMD_HISTORY
write_mlist_header(struct mlist * ml,FILE * f)1512838f5788Ssimonb static void write_mlist_header(struct mlist *ml, FILE *f)
151320006a0bStron {
1514838f5788Ssimonb if (ml == &mlist_search)
1515838f5788Ssimonb fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1516838f5788Ssimonb #if SHELL_ESCAPE || PIPEC
1517838f5788Ssimonb else if (ml == &mlist_shell)
1518838f5788Ssimonb fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1519838f5788Ssimonb #endif
152020006a0bStron }
1521838f5788Ssimonb
1522838f5788Ssimonb /*
1523838f5788Ssimonb * Write all modified entries in an mlist to the history file.
1524838f5788Ssimonb */
write_mlist(struct mlist * ml,FILE * f)1525838f5788Ssimonb static void write_mlist(struct mlist *ml, FILE *f)
1526838f5788Ssimonb {
152720006a0bStron for (ml = ml->next; ml->string != NULL; ml = ml->next)
1528838f5788Ssimonb {
1529838f5788Ssimonb if (!ml->modified)
1530838f5788Ssimonb continue;
153120006a0bStron fprintf(f, "\"%s\n", ml->string);
1532838f5788Ssimonb ml->modified = 0;
153320006a0bStron }
1534838f5788Ssimonb ml->modified = 0; /* entire mlist is now unmodified */
1535838f5788Ssimonb }
153620006a0bStron
153720006a0bStron /*
1538838f5788Ssimonb * Make a temp name in the same directory as filename.
153920006a0bStron */
make_tempname(char * filename)1540838f5788Ssimonb static char * make_tempname(char *filename)
154120006a0bStron {
1542838f5788Ssimonb char lastch;
1543838f5788Ssimonb char *tempname = ecalloc(1, strlen(filename)+1);
1544838f5788Ssimonb strcpy(tempname, filename);
1545838f5788Ssimonb lastch = tempname[strlen(tempname)-1];
1546838f5788Ssimonb tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q';
1547838f5788Ssimonb return tempname;
1548838f5788Ssimonb }
154920006a0bStron
1550838f5788Ssimonb struct save_ctx
1551838f5788Ssimonb {
1552838f5788Ssimonb struct mlist *mlist;
1553838f5788Ssimonb FILE *fout;
1554838f5788Ssimonb };
1555838f5788Ssimonb
1556838f5788Ssimonb /*
1557838f5788Ssimonb * Copy entries from the saved history file to a new file.
1558838f5788Ssimonb * At the end of each mlist, append any new entries
1559838f5788Ssimonb * created during this session.
1560838f5788Ssimonb */
copy_hist(void * uparam,struct mlist * ml,char * string)1561838f5788Ssimonb static void copy_hist(void *uparam, struct mlist *ml, char *string)
1562838f5788Ssimonb {
1563838f5788Ssimonb struct save_ctx *ctx = (struct save_ctx *) uparam;
1564838f5788Ssimonb
1565838f5788Ssimonb if (ml != NULL && ml != ctx->mlist) {
1566838f5788Ssimonb /* We're changing mlists. */
1567838f5788Ssimonb if (ctx->mlist)
1568838f5788Ssimonb /* Append any new entries to the end of the current mlist. */
1569838f5788Ssimonb write_mlist(ctx->mlist, ctx->fout);
1570838f5788Ssimonb /* Write the header for the new mlist. */
1571838f5788Ssimonb ctx->mlist = ml;
1572838f5788Ssimonb write_mlist_header(ctx->mlist, ctx->fout);
1573838f5788Ssimonb }
1574838f5788Ssimonb
1575838f5788Ssimonb if (string == NULL) /* End of file */
1576838f5788Ssimonb {
1577838f5788Ssimonb /* Write any sections that were not in the original file. */
157820006a0bStron if (mlist_search.modified)
1579838f5788Ssimonb {
1580838f5788Ssimonb write_mlist_header(&mlist_search, ctx->fout);
1581838f5788Ssimonb write_mlist(&mlist_search, ctx->fout);
1582838f5788Ssimonb }
158320006a0bStron #if SHELL_ESCAPE || PIPEC
158420006a0bStron if (mlist_shell.modified)
158520006a0bStron {
1586838f5788Ssimonb write_mlist_header(&mlist_shell, ctx->fout);
1587838f5788Ssimonb write_mlist(&mlist_shell, ctx->fout);
1588838f5788Ssimonb }
1589838f5788Ssimonb #endif
1590838f5788Ssimonb } else if (ml != NULL)
1591838f5788Ssimonb {
1592838f5788Ssimonb /* Copy mlist entry. */
1593838f5788Ssimonb fprintf(ctx->fout, "\"%s\n", string);
1594838f5788Ssimonb }
1595838f5788Ssimonb /* Skip marks */
1596838f5788Ssimonb }
1597838f5788Ssimonb #endif /* CMD_HISTORY */
1598838f5788Ssimonb
1599838f5788Ssimonb /*
1600838f5788Ssimonb * Make a file readable only by its owner.
1601838f5788Ssimonb */
make_file_private(FILE * f)1602838f5788Ssimonb static void make_file_private(FILE *f)
1603838f5788Ssimonb {
1604838f5788Ssimonb #if HAVE_FCHMOD
160520006a0bStron int do_chmod = 1;
160620006a0bStron #if HAVE_STAT
160720006a0bStron struct stat statbuf;
160820006a0bStron int r = fstat(fileno(f), &statbuf);
160920006a0bStron if (r < 0 || !S_ISREG(statbuf.st_mode))
161020006a0bStron /* Don't chmod if not a regular file. */
161120006a0bStron do_chmod = 0;
161220006a0bStron #endif
161320006a0bStron if (do_chmod)
161420006a0bStron fchmod(fileno(f), 0600);
1615838f5788Ssimonb #endif
1616838f5788Ssimonb }
1617838f5788Ssimonb
1618838f5788Ssimonb /*
1619838f5788Ssimonb * Does the history file need to be updated?
1620838f5788Ssimonb */
1621838f5788Ssimonb #if CMD_HISTORY
histfile_modified(void)1622838f5788Ssimonb static int histfile_modified(void)
1623838f5788Ssimonb {
1624838f5788Ssimonb if (mlist_search.modified)
1625838f5788Ssimonb return 1;
1626838f5788Ssimonb #if SHELL_ESCAPE || PIPEC
1627838f5788Ssimonb if (mlist_shell.modified)
1628838f5788Ssimonb return 1;
1629838f5788Ssimonb #endif
1630838f5788Ssimonb if (marks_modified)
1631838f5788Ssimonb return 1;
1632838f5788Ssimonb return 0;
163320006a0bStron }
163420006a0bStron #endif
163520006a0bStron
1636838f5788Ssimonb /*
1637838f5788Ssimonb * Update the .lesshst file.
1638838f5788Ssimonb */
save_cmdhist(void)1639838f5788Ssimonb public void save_cmdhist(void)
1640838f5788Ssimonb {
1641838f5788Ssimonb #if CMD_HISTORY
1642838f5788Ssimonb char *histname;
1643838f5788Ssimonb char *tempname;
1644838f5788Ssimonb int skip_search;
1645838f5788Ssimonb int skip_shell;
1646838f5788Ssimonb struct save_ctx ctx;
1647838f5788Ssimonb char *s;
1648838f5788Ssimonb FILE *fout = NULL;
1649838f5788Ssimonb int histsize = 0;
165020006a0bStron
1651838f5788Ssimonb if (secure || !histfile_modified())
1652838f5788Ssimonb return;
1653838f5788Ssimonb histname = histfile_name(0);
1654838f5788Ssimonb if (histname == NULL)
1655838f5788Ssimonb return;
1656838f5788Ssimonb tempname = make_tempname(histname);
1657838f5788Ssimonb fout = fopen(tempname, "w");
1658838f5788Ssimonb if (fout != NULL)
1659838f5788Ssimonb {
1660838f5788Ssimonb make_file_private(fout);
1661838f5788Ssimonb s = lgetenv("LESSHISTSIZE");
1662838f5788Ssimonb if (s != NULL)
1663838f5788Ssimonb histsize = atoi(s);
1664838f5788Ssimonb if (histsize <= 0)
1665838f5788Ssimonb histsize = 100;
1666838f5788Ssimonb skip_search = mlist_size(&mlist_search) - histsize;
166720006a0bStron #if SHELL_ESCAPE || PIPEC
1668838f5788Ssimonb skip_shell = mlist_size(&mlist_shell) - histsize;
166920006a0bStron #endif
1670838f5788Ssimonb fprintf(fout, "%s\n", HISTFILE_FIRST_LINE);
1671838f5788Ssimonb ctx.fout = fout;
1672838f5788Ssimonb ctx.mlist = NULL;
1673838f5788Ssimonb read_cmdhist(©_hist, &ctx, skip_search, skip_shell);
1674838f5788Ssimonb save_marks(fout, HISTFILE_MARK_SECTION);
1675838f5788Ssimonb fclose(fout);
1676838f5788Ssimonb #if MSDOS_COMPILER==WIN32C
1677838f5788Ssimonb /*
1678838f5788Ssimonb * Windows rename doesn't remove an existing file,
1679838f5788Ssimonb * making it useless for atomic operations. Sigh.
1680838f5788Ssimonb */
1681838f5788Ssimonb remove(histname);
1682838f5788Ssimonb #endif
1683838f5788Ssimonb rename(tempname, histname);
1684838f5788Ssimonb }
1685838f5788Ssimonb free(tempname);
1686838f5788Ssimonb free(histname);
168720006a0bStron #endif /* CMD_HISTORY */
168820006a0bStron }
1689