summaryrefslogtreecommitdiffstats
path: root/contrib/less/cmdbuf.c
diff options
context:
space:
mode:
authordelphij <delphij@FreeBSD.org>2006-08-20 15:49:51 +0000
committerdelphij <delphij@FreeBSD.org>2006-08-20 15:49:51 +0000
commit5297f4bc59da861bbe3beb5ee12839fa05e79882 (patch)
tree66e8cb2705abe3d167ba3a94a60d37c894690ff2 /contrib/less/cmdbuf.c
parent71cad8a96df623ac7d9129aa5e8c983df1030b0c (diff)
downloadFreeBSD-src-5297f4bc59da861bbe3beb5ee12839fa05e79882.zip
FreeBSD-src-5297f4bc59da861bbe3beb5ee12839fa05e79882.tar.gz
Import less v394
Diffstat (limited to 'contrib/less/cmdbuf.c')
-rw-r--r--contrib/less/cmdbuf.c543
1 files changed, 472 insertions, 71 deletions
diff --git a/contrib/less/cmdbuf.c b/contrib/less/cmdbuf.c
index dbf5f2a..1d3e284 100644
--- a/contrib/less/cmdbuf.c
+++ b/contrib/less/cmdbuf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 1984-2002 Mark Nudelman
+ * Copyright (C) 1984-2005 Mark Nudelman
*
* You may distribute under the terms of either the GNU General Public
* License or the Less License, as specified in the README file.
@@ -16,8 +16,13 @@
#include "less.h"
#include "cmd.h"
+#include "charset.h"
+#if HAVE_STAT
+#include <sys/stat.h>
+#endif
extern int sc_width;
+extern int utf_mode;
static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
static int cmd_col; /* Current column of the cursor */
@@ -48,6 +53,12 @@ public char closequote = '"';
#endif
#if CMD_HISTORY
+
+/* History file */
+#define HISTFILE_FIRST_LINE ".less-history-file:"
+#define HISTFILE_SEARCH_SECTION ".search"
+#define HISTFILE_SHELL_SECTION ".shell"
+
/*
* A mlist structure represents a command history.
*/
@@ -93,6 +104,10 @@ public void * constant ml_shell = (void *)3;
static struct mlist *curr_mlist = NULL;
static int curr_cmdflags;
+static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
+static int cmd_mbc_buf_len;
+static int cmd_mbc_buf_index;
+
/*
* Reset command buffer (to empty).
@@ -105,6 +120,7 @@ cmd_reset()
cmd_col = 0;
cmd_offset = 0;
literal = 0;
+ cmd_mbc_buf_len = 0;
}
/*
@@ -115,6 +131,7 @@ clear_cmd()
{
clear_bot();
cmd_col = prompt_col = 0;
+ cmd_mbc_buf_len = 0;
}
/*
@@ -124,9 +141,28 @@ clear_cmd()
cmd_putstr(s)
char *s;
{
- putstr(s);
- cmd_col += strlen(s);
- prompt_col += strlen(s);
+ LWCHAR prev_ch = 0;
+ LWCHAR ch;
+ char *endline = s + strlen(s);
+ while (*s != '\0')
+ {
+ char *ns = s;
+ ch = step_char(&ns, +1, endline);
+ while (s < ns)
+ putchr(*s++);
+ if (!utf_mode)
+ {
+ cmd_col++;
+ prompt_col++;
+ } else if (!is_composing_char(ch) &&
+ !is_combining_char(prev_ch, ch))
+ {
+ int width = is_wide_char(ch) ? 2 : 1;
+ cmd_col += width;
+ prompt_col += width;
+ }
+ prev_ch = ch;
+ }
}
/*
@@ -135,7 +171,113 @@ cmd_putstr(s)
public int
len_cmdbuf()
{
- return (strlen(cmdbuf));
+ char *s = cmdbuf;
+ char *endline = s + strlen(s);
+ int len = 0;
+
+ while (*s != '\0')
+ {
+ step_char(&s, +1, endline);
+ len++;
+ }
+ return (len);
+}
+
+/*
+ * Common part of cmd_step_right() and cmd_step_left().
+ */
+ static char *
+cmd_step_common(p, ch, len, pwidth, bswidth)
+ char *p;
+ LWCHAR ch;
+ int len;
+ int *pwidth;
+ int *bswidth;
+{
+ char *pr;
+
+ if (len == 1)
+ {
+ pr = prchar((int) ch);
+ if (pwidth != NULL || bswidth != NULL)
+ {
+ int len = strlen(pr);
+ if (pwidth != NULL)
+ *pwidth = len;
+ if (bswidth != NULL)
+ *bswidth = len;
+ }
+ } else
+ {
+ pr = prutfchar(ch);
+ if (pwidth != NULL || bswidth != NULL)
+ {
+ if (is_composing_char(ch))
+ {
+ if (pwidth != NULL)
+ *pwidth = 0;
+ if (bswidth != NULL)
+ *bswidth = 0;
+ } else if (is_ubin_char(ch))
+ {
+ int len = strlen(pr);
+ if (pwidth != NULL)
+ *pwidth = len;
+ if (bswidth != NULL)
+ *bswidth = len;
+ } else
+ {
+ LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
+ if (is_combining_char(prev_ch, ch))
+ {
+ if (pwidth != NULL)
+ *pwidth = 0;
+ if (bswidth != NULL)
+ *bswidth = 0;
+ } else
+ {
+ if (pwidth != NULL)
+ *pwidth = is_wide_char(ch)
+ ? 2
+ : 1;
+ if (bswidth != NULL)
+ *bswidth = 1;
+ }
+ }
+ }
+ }
+
+ return (pr);
+}
+
+/*
+ * Step a pointer one character right in the command buffer.
+ */
+ static char *
+cmd_step_right(pp, pwidth, bswidth)
+ char **pp;
+ int *pwidth;
+ int *bswidth;
+{
+ char *p = *pp;
+ LWCHAR ch = step_char(pp, +1, p + strlen(p));
+
+ return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
+}
+
+/*
+ * Step a pointer one character left in the command buffer.
+ */
+ static char *
+cmd_step_left(pp, pwidth, bswidth)
+ char **pp;
+ int *pwidth;
+ int *bswidth;
+{
+ char *p = *pp;
+ LWCHAR ch = step_char(pp, -1, cmdbuf);
+
+ return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
}
/*
@@ -146,19 +288,30 @@ len_cmdbuf()
cmd_repaint(old_cp)
char *old_cp;
{
- char *p;
-
/*
* Repaint the line from the current position.
*/
clear_eol();
- for ( ; *cp != '\0'; cp++)
+ while (*cp != '\0')
+ {
+ char *np = cp;
+ int width;
+ char *pr = cmd_step_right(&np, &width, NULL);
+ if (cmd_col + width >= sc_width)
+ break;
+ cp = np;
+ putstr(pr);
+ cmd_col += width;
+ }
+ while (*cp != '\0')
{
- p = prchar(*cp);
- if (cmd_col + (int)strlen(p) >= sc_width)
+ char *np = cp;
+ int width;
+ char *pr = cmd_step_right(&np, &width, NULL);
+ if (width > 0)
break;
- putstr(p);
- cmd_col += strlen(p);
+ cp = np;
+ putstr(pr);
}
/*
@@ -177,8 +330,12 @@ cmd_home()
{
while (cmd_col > prompt_col)
{
- putbs();
- cmd_col--;
+ int width, bswidth;
+
+ cmd_step_left(&cp, &width, &bswidth);
+ while (bswidth-- > 0)
+ putbs();
+ cmd_col -= width;
}
cp = &cmdbuf[cmd_offset];
@@ -201,7 +358,20 @@ cmd_lshift()
s = cmdbuf + cmd_offset;
cols = 0;
while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
- cols += strlen(prchar(*s++));
+ {
+ int width;
+ cmd_step_right(&s, &width, NULL);
+ cols += width;
+ }
+ while (*s != '\0')
+ {
+ int width;
+ char *ns = s;
+ cmd_step_right(&ns, &width, NULL);
+ if (width > 0)
+ break;
+ s = ns;
+ }
cmd_offset = s - cmdbuf;
save_cp = cp;
@@ -216,7 +386,6 @@ cmd_lshift()
cmd_rshift()
{
char *s;
- char *p;
char *save_cp;
int cols;
@@ -229,8 +398,9 @@ cmd_rshift()
cols = 0;
while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
{
- p = prchar(*--s);
- cols += strlen(p);
+ int width;
+ cmd_step_left(&s, &width, NULL);
+ cols += width;
}
cmd_offset = s - cmdbuf;
@@ -245,23 +415,32 @@ cmd_rshift()
static int
cmd_right()
{
- char *p;
+ char *pr;
+ char *ncp;
+ int width;
if (*cp == '\0')
{
- /*
- * Already at the end of the line.
- */
+ /* Already at the end of the line. */
return (CC_OK);
}
- p = prchar(*cp);
- if (cmd_col + (int)strlen(p) >= sc_width)
+ ncp = cp;
+ pr = cmd_step_right(&ncp, &width, NULL);
+ if (cmd_col + width >= sc_width)
cmd_lshift();
- else if (cmd_col + (int)strlen(p) == sc_width - 1 && cp[1] != '\0')
+ else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
cmd_lshift();
- cp++;
- putstr(p);
- cmd_col += strlen(p);
+ cp = ncp;
+ cmd_col += width;
+ putstr(pr);
+ while (*cp != '\0')
+ {
+ pr = cmd_step_right(&ncp, &width, NULL);
+ if (width > 0)
+ break;
+ putstr(pr);
+ cp = ncp;
+ }
return (CC_OK);
}
@@ -271,19 +450,26 @@ cmd_right()
static int
cmd_left()
{
- char *p;
+ char *ncp;
+ int width, bswidth;
if (cp <= cmdbuf)
{
/* Already at the beginning of the line */
return (CC_OK);
}
- p = prchar(cp[-1]);
- if (cmd_col < prompt_col + (int)strlen(p))
+ ncp = cp;
+ while (ncp > cmdbuf)
+ {
+ cmd_step_left(&ncp, &width, &bswidth);
+ if (width > 0)
+ break;
+ }
+ if (cmd_col < prompt_col + width)
cmd_rshift();
- cp--;
- cmd_col -= strlen(p);
- while (*p++ != '\0')
+ cp = ncp;
+ cmd_col -= width;
+ while (bswidth-- > 0)
putbs();
return (CC_OK);
}
@@ -292,26 +478,29 @@ cmd_left()
* Insert a char into the command buffer, at the current position.
*/
static int
-cmd_ichar(c)
- int c;
+cmd_ichar(cs, clen)
+ char *cs;
+ int clen;
{
char *s;
- if (strlen(cmdbuf) >= sizeof(cmdbuf)-2)
+ if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
{
- /*
- * No room in the command buffer for another char.
- */
+ /* No room in the command buffer for another char. */
bell();
return (CC_ERROR);
}
/*
- * Insert the character into the buffer.
+ * Make room for the new character (shift the tail of the buffer right).
*/
for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--)
- s[1] = s[0];
- *cp = c;
+ s[clen] = s[0];
+ /*
+ * Insert the character into the buffer.
+ */
+ for (s = cp; s < cp + clen; s++)
+ *s = *cs++;
/*
* Reprint the tail of the line from the inserted char.
*/
@@ -328,6 +517,7 @@ cmd_ichar(c)
cmd_erase()
{
register char *s;
+ int clen;
if (cp == cmdbuf)
{
@@ -340,12 +530,20 @@ cmd_erase()
/*
* Move cursor left (to the char being erased).
*/
+ s = cp;
cmd_left();
+ clen = s - cp;
+
/*
* Remove the char from the buffer (shift the buffer left).
*/
- for (s = cp; *s != '\0'; s++)
- s[0] = s[1];
+ for (s = cp; ; s++)
+ {
+ s[0] = s[clen];
+ if (s[0] == '\0')
+ break;
+ }
+
/*
* Repaint the buffer after the erased char.
*/
@@ -368,9 +566,7 @@ cmd_delete()
{
if (*cp == '\0')
{
- /*
- * At end of string; there is no char under the cursor.
- */
+ /* At end of string; there is no char under the cursor. */
return (CC_OK);
}
/*
@@ -441,9 +637,7 @@ cmd_kill()
{
if (cmdbuf[0] == '\0')
{
- /*
- * Buffer is already empty; abort the current command.
- */
+ /* Buffer is already empty; abort the current command. */
return (CC_QUIT);
}
cmd_offset = 0;
@@ -470,6 +664,10 @@ set_mlist(mlist, cmdflags)
{
curr_mlist = (struct mlist *) mlist;
curr_cmdflags = cmdflags;
+
+ /* Make sure the next up-arrow moves to the last string in the mlist. */
+ if (curr_mlist != NULL)
+ curr_mlist->curr_mp = curr_mlist;
}
#if CMD_HISTORY
@@ -505,12 +703,9 @@ cmd_updown(action)
s = curr_mlist->curr_mp->string;
if (s == NULL)
s = "";
- for (cp = cmdbuf; *s != '\0'; s++)
- {
- *cp = *s;
+ strcpy(cmdbuf, s);
+ for (cp = cmdbuf; *cp != '\0'; )
cmd_right();
- }
- *cp = '\0';
return (CC_OK);
}
#endif
@@ -531,17 +726,13 @@ cmd_addhist(mlist, cmd)
*/
if (strlen(cmd) == 0)
return;
+
/*
- * Don't save if a duplicate of a command which is already
- * in the history.
- * But select the one already in the history to be current.
+ * Save the command unless it's a duplicate of the
+ * last command in the history.
*/
- for (ml = mlist->next; ml != mlist; ml = ml->next)
- {
- if (strcmp(ml->string, cmd) == 0)
- break;
- }
- if (ml == mlist)
+ ml = mlist->prev;
+ if (ml == mlist || strcmp(ml->string, cmd) != 0)
{
/*
* Did not find command in history.
@@ -705,10 +896,13 @@ cmd_istr(str)
{
char *s;
int action;
+ char *endline = str + strlen(str);
- for (s = str; *s != '\0'; s++)
+ for (s = str; *s != '\0'; )
{
- action = cmd_ichar(*s);
+ char *os = s;
+ step_char(&s, +1, endline);
+ action = cmd_ichar(os, s - os);
if (action != CC_OK)
{
bell();
@@ -995,6 +1189,56 @@ cmd_char(c)
int c;
{
int action;
+ int len;
+
+ if (!utf_mode)
+ {
+ cmd_mbc_buf[0] = c;
+ len = 1;
+ } else
+ {
+ /* Perform strict validation in all possible cases. */
+ if (cmd_mbc_buf_len == 0)
+ {
+ retry:
+ cmd_mbc_buf_index = 1;
+ *cmd_mbc_buf = c;
+ if (IS_ASCII_OCTET(c))
+ cmd_mbc_buf_len = 1;
+ else if (IS_UTF8_LEAD(c))
+ {
+ cmd_mbc_buf_len = utf_len(c);
+ return (CC_OK);
+ } else
+ {
+ /* UTF8_INVALID or stray UTF8_TRAIL */
+ bell();
+ return (CC_ERROR);
+ }
+ } else if (IS_UTF8_TRAIL(c))
+ {
+ cmd_mbc_buf[cmd_mbc_buf_index++] = c;
+ if (cmd_mbc_buf_index < cmd_mbc_buf_len)
+ return (CC_OK);
+ if (!is_utf8_well_formed(cmd_mbc_buf))
+ {
+ /* complete, but not well formed (non-shortest form), sequence */
+ cmd_mbc_buf_len = 0;
+ bell();
+ return (CC_ERROR);
+ }
+ } else
+ {
+ /* Flush incomplete (truncated) sequence. */
+ cmd_mbc_buf_len = 0;
+ bell();
+ /* Handle new char. */
+ goto retry;
+ }
+
+ len = cmd_mbc_buf_len;
+ cmd_mbc_buf_len = 0;
+ }
if (literal)
{
@@ -1002,13 +1246,13 @@ cmd_char(c)
* Insert the char, even if it is a line-editing char.
*/
literal = 0;
- return (cmd_ichar(c));
+ return (cmd_ichar(cmd_mbc_buf, len));
}
/*
- * See if it is a special line-editing character.
+ * See if it is a line-editing character.
*/
- if (in_mca())
+ if (in_mca() && len == 1)
{
action = cmd_edit(c);
switch (action)
@@ -1024,7 +1268,7 @@ cmd_char(c)
/*
* Insert the char into the command buffer.
*/
- return (cmd_ichar(c));
+ return (cmd_ichar(cmd_mbc_buf, len));
}
/*
@@ -1049,3 +1293,160 @@ get_cmdbuf()
{
return (cmdbuf);
}
+
+#if CMD_HISTORY
+/*
+ * Get the name of the history file.
+ */
+ static char *
+histfile_name()
+{
+ char *home;
+ char *name;
+ int len;
+
+ /* See if filename is explicitly specified by $LESSHISTFILE. */
+ name = lgetenv("LESSHISTFILE");
+ if (name != NULL && *name != '\0')
+ {
+ if (strcmp(name, "-") == 0)
+ /* $LESSHISTFILE == "-" means don't use a history file. */
+ return (NULL);
+ return (save(name));
+ }
+
+ /* Otherwise, file is in $HOME. */
+ home = lgetenv("HOME");
+ if (home == NULL || *home == '\0')
+ {
+#if OS2
+ home = lgetenv("INIT");
+ if (home == NULL || *home == '\0')
+#endif
+ return (NULL);
+ }
+ len = strlen(home) + strlen(LESSHISTFILE) + 2;
+ name = (char *) ecalloc(len, sizeof(char));
+ SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
+ return (name);
+}
+#endif /* CMD_HISTORY */
+
+/*
+ * Initialize history from a .lesshist file.
+ */
+ public void
+init_cmdhist()
+{
+#if CMD_HISTORY
+ struct mlist *ml = NULL;
+ char line[CMDBUF_SIZE];
+ char *filename;
+ FILE *f;
+ char *p;
+
+ filename = histfile_name();
+ if (filename == NULL)
+ return;
+ f = fopen(filename, "r");
+ free(filename);
+ if (f == NULL)
+ return;
+ if (fgets(line, sizeof(line), f) == NULL ||
+ strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
+ {
+ fclose(f);
+ return;
+ }
+ while (fgets(line, sizeof(line), f) != NULL)
+ {
+ for (p = line; *p != '\0'; p++)
+ {
+ if (*p == '\n' || *p == '\r')
+ {
+ *p = '\0';
+ break;
+ }
+ }
+ if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
+ ml = &mlist_search;
+#if SHELL_ESCAPE || PIPEC
+ else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
+ ml = &mlist_shell;
+#endif
+ else if (*line == '"')
+ {
+ if (ml != NULL)
+ cmd_addhist(ml, line+1);
+ }
+ }
+ fclose(f);
+#endif /* CMD_HISTORY */
+}
+
+/*
+ *
+ */
+#if CMD_HISTORY
+ static void
+save_mlist(ml, f)
+ struct mlist *ml;
+ FILE *f;
+{
+ int histsize = 0;
+ int n;
+ char *s;
+
+ s = lgetenv("LESSHISTSIZE");
+ if (s != NULL)
+ histsize = atoi(s);
+ if (histsize == 0)
+ histsize = 100;
+
+ ml = ml->prev;
+ for (n = 0; n < histsize; n++)
+ {
+ if (ml->string == NULL)
+ break;
+ ml = ml->prev;
+ }
+ for (ml = ml->next; ml->string != NULL; ml = ml->next)
+ fprintf(f, "\"%s\n", ml->string);
+}
+#endif /* CMD_HISTORY */
+
+/*
+ *
+ */
+ public void
+save_cmdhist()
+{
+#if CMD_HISTORY
+ char *filename;
+ FILE *f;
+
+ filename = histfile_name();
+ if (filename == NULL)
+ return;
+ f = fopen(filename, "w");
+ free(filename);
+ if (f == NULL)
+ return;
+#if HAVE_FCHMOD
+ /* Make history file readable only by owner. */
+ fchmod(fileno(f), 0600);
+#endif
+
+ fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
+
+ fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
+ save_mlist(&mlist_search, f);
+
+#if SHELL_ESCAPE || PIPEC
+ fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
+ save_mlist(&mlist_shell, f);
+#endif
+
+ fclose(f);
+#endif /* CMD_HISTORY */
+}
OpenPOWER on IntegriCloud