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