aboutsummaryrefslogtreecommitdiff
path: root/src/read.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/read.c')
-rw-r--r--src/read.c498
1 files changed, 196 insertions, 302 deletions
diff --git a/src/read.c b/src/read.c
index df6ac20..ec10be7 100644
--- a/src/read.c
+++ b/src/read.c
@@ -1,4 +1,4 @@
-/* $NetBSD: read.c,v 1.70 2013/05/27 23:55:55 christos Exp $ */
+/* $NetBSD: read.c,v 1.102 2016/12/11 15:47:06 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,49 +37,91 @@
#if 0
static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: read.c,v 1.70 2013/05/27 23:55:55 christos Exp $");
+__RCSID("$NetBSD: read.c,v 1.102 2016/12/11 15:47:06 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
- * read.c: Clean this junk up! This is horrible code.
- * Terminal read functions
+ * read.c: Terminal read functions
*/
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
#include <limits.h>
-#include "el.h"
-
-#define OKCMD -1 /* must be -1! */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-private int read__fixio(int, int);
-private int read_preread(EditLine *);
-private int read_char(EditLine *, Char *);
-private int read_getcmd(EditLine *, el_action_t *, Char *);
-private void read_pop(c_macro_t *);
+#include "el.h"
+#include "fcns.h"
+#include "read.h"
+
+#define EL_MAXMACRO 10
+
+struct macros {
+ wchar_t **macro;
+ int level;
+ int offset;
+};
+
+struct el_read_t {
+ struct macros macros;
+ el_rfunc_t read_char; /* Function to read a character. */
+ int read_errno;
+};
+
+static int read__fixio(int, int);
+static int read_char(EditLine *, wchar_t *);
+static int read_getcmd(EditLine *, el_action_t *, wchar_t *);
+static void read_clearmacros(struct macros *);
+static void read_pop(struct macros *);
+static const wchar_t *noedit_wgets(EditLine *, int *);
/* read_init():
* Initialize the read stuff
*/
-protected int
+libedit_private int
read_init(EditLine *el)
{
+ struct macros *ma;
+
+ if ((el->el_read = el_malloc(sizeof(*el->el_read))) == NULL)
+ return -1;
+
+ ma = &el->el_read->macros;
+ if ((ma->macro = el_malloc(EL_MAXMACRO *
+ sizeof(*ma->macro))) == NULL) {
+ free(el->el_read);
+ return -1;
+ }
+ ma->level = -1;
+ ma->offset = 0;
+
/* builtin read_char */
- el->el_read.read_char = read_char;
+ el->el_read->read_char = read_char;
return 0;
}
+/* el_read_end():
+ * Free the data structures used by the read stuff.
+ */
+libedit_private void
+read_end(struct el_read_t *el_read)
+{
+ read_clearmacros(&el_read->macros);
+ el_free(el_read->macros.macro);
+ el_read->macros.macro = NULL;
+ el_free(el_read);
+}
/* el_read_setfn():
* Set the read char function to the one provided.
* If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one.
*/
-protected int
-el_read_setfn(EditLine *el, el_rfunc_t rc)
+libedit_private int
+el_read_setfn(struct el_read_t *el_read, el_rfunc_t rc)
{
- el->el_read.read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
+ el_read->read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
return 0;
}
@@ -88,42 +130,19 @@ el_read_setfn(EditLine *el, el_rfunc_t rc)
* return the current read char function, or EL_BUILTIN_GETCFN
* if it is the default one
*/
-protected el_rfunc_t
-el_read_getfn(EditLine *el)
+libedit_private el_rfunc_t
+el_read_getfn(struct el_read_t *el_read)
{
- return el->el_read.read_char == read_char ?
- EL_BUILTIN_GETCFN : el->el_read.read_char;
+ return el_read->read_char == read_char ?
+ EL_BUILTIN_GETCFN : el_read->read_char;
}
-#ifndef MIN
-#define MIN(A,B) ((A) < (B) ? (A) : (B))
-#endif
-
-#ifdef DEBUG_EDIT
-private void
-read_debug(EditLine *el)
-{
-
- if (el->el_line.cursor > el->el_line.lastchar)
- (void) fprintf(el->el_errfile, "cursor > lastchar\r\n");
- if (el->el_line.cursor < el->el_line.buffer)
- (void) fprintf(el->el_errfile, "cursor < buffer\r\n");
- if (el->el_line.cursor > el->el_line.limit)
- (void) fprintf(el->el_errfile, "cursor > limit\r\n");
- if (el->el_line.lastchar > el->el_line.limit)
- (void) fprintf(el->el_errfile, "lastchar > limit\r\n");
- if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2])
- (void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n");
-}
-#endif /* DEBUG_EDIT */
-
-
/* read__fixio():
* Try to recover from a read error
*/
/* ARGSUSED */
-private int
+static int
read__fixio(int fd __attribute__((__unused__)), int e)
{
@@ -181,49 +200,17 @@ read__fixio(int fd __attribute__((__unused__)), int e)
}
-/* read_preread():
- * Try to read the stuff in the input queue;
- */
-private int
-read_preread(EditLine *el)
-{
- int chrs = 0;
-
- if (el->el_tty.t_mode == ED_IO)
- return 0;
-
-#ifndef WIDECHAR
-/* FIONREAD attempts to buffer up multiple bytes, and to make that work
- * properly with partial wide/UTF-8 characters would need some careful work. */
-#ifdef FIONREAD
- (void) ioctl(el->el_infd, FIONREAD, &chrs);
- if (chrs > 0) {
- char buf[EL_BUFSIZ];
-
- chrs = read(el->el_infd, buf,
- (size_t) MIN(chrs, EL_BUFSIZ - 1));
- if (chrs > 0) {
- buf[chrs] = '\0';
- el_push(el, buf);
- }
- }
-#endif /* FIONREAD */
-#endif
- return chrs > 0;
-}
-
-
/* el_push():
* Push a macro
*/
-public void
-FUN(el,push)(EditLine *el, const Char *str)
+void
+el_wpush(EditLine *el, const wchar_t *str)
{
- c_macro_t *ma = &el->el_chared.c_macro;
+ struct macros *ma = &el->el_read->macros;
if (str != NULL && ma->level + 1 < EL_MAXMACRO) {
ma->level++;
- if ((ma->macro[ma->level] = Strdup(str)) != NULL)
+ if ((ma->macro[ma->level] = wcsdup(str)) != NULL)
return;
ma->level--;
}
@@ -233,24 +220,23 @@ FUN(el,push)(EditLine *el, const Char *str)
/* read_getcmd():
- * Get next command from the input stream, return OKCMD on success.
+ * Get next command from the input stream,
+ * return 0 on success or -1 on EOF or error.
* Character values > 255 are not looked up in the map, but inserted.
*/
-private int
-read_getcmd(EditLine *el, el_action_t *cmdnum, Char *ch)
+static int
+read_getcmd(EditLine *el, el_action_t *cmdnum, wchar_t *ch)
{
+ static const wchar_t meta = (wchar_t)0x80;
el_action_t cmd;
int num;
- el->el_errno = 0;
do {
- if ((num = FUN(el,getc)(el, ch)) != 1) {/* if EOF or error */
- el->el_errno = num == 0 ? 0 : errno;
- return 0; /* not OKCMD */
- }
+ if ((num = el_wgetc(el, ch)) != 1)
+ return -1;
#ifdef KANJI
- if ((*ch & 0200)) {
+ if ((*ch & meta)) {
el->el_state.metanext = 0;
cmd = CcViMap[' '];
break;
@@ -259,13 +245,11 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, Char *ch)
if (el->el_state.metanext) {
el->el_state.metanext = 0;
- *ch |= 0200;
+ *ch |= meta;
}
-#ifdef WIDECHAR
if (*ch >= N_KEYS)
cmd = ED_INSERT;
else
-#endif
cmd = el->el_map.current[(unsigned char) *ch];
if (cmd == ED_SEQUENCE_LEAD_IN) {
keymacro_value_t val;
@@ -274,49 +258,31 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, Char *ch)
cmd = val.cmd;
break;
case XK_STR:
- FUN(el,push)(el, val.str);
+ el_wpush(el, val.str);
break;
-#ifdef notyet
- case XK_EXE:
- /* XXX: In the future to run a user function */
- RunCommand(val.str);
- break;
-#endif
+ case XK_NOD:
+ return -1;
default:
EL_ABORT((el->el_errfile, "Bad XK_ type \n"));
break;
}
}
- if (el->el_map.alt == NULL)
- el->el_map.current = el->el_map.key;
} while (cmd == ED_SEQUENCE_LEAD_IN);
*cmdnum = cmd;
- return OKCMD;
-}
-
-#ifdef WIDECHAR
-/* utf8_islead():
- * Test whether a byte is a leading byte of a UTF-8 sequence.
- */
-private int
-utf8_islead(int c)
-{
- return c < 0x80 || /* single byte char */
- (c >= 0xc2 && c <= 0xf4); /* start of multibyte sequence */
+ return 0;
}
-#endif
/* read_char():
* Read a character from the tty.
*/
-private int
-read_char(EditLine *el, Char *cp)
+static int
+read_char(EditLine *el, wchar_t *cp)
{
ssize_t num_read;
int tried = 0;
char cbuf[MB_LEN_MAX];
size_t cbp = 0;
- int bytes = 0;
+ int save_errno = errno;
again:
el->el_signal->sig_no = 0;
@@ -324,7 +290,7 @@ read_char(EditLine *el, Char *cp)
int e = errno;
switch (el->el_signal->sig_no) {
case SIGCONT:
- FUN(el,set)(el, EL_REFRESH);
+ el_wset(el, EL_REFRESH);
/*FALLTHROUGH*/
case SIGWINCH:
sig_set(el);
@@ -332,57 +298,69 @@ read_char(EditLine *el, Char *cp)
default:
break;
}
- if (!tried && read__fixio(el->el_infd, e) == 0)
+ if (!tried && read__fixio(el->el_infd, e) == 0) {
+ errno = save_errno;
tried = 1;
- else {
+ } else {
errno = e;
- *cp = '\0';
+ *cp = L'\0';
return -1;
}
}
/* Test for EOF */
if (num_read == 0) {
- errno = 0;
- *cp = '\0';
+ *cp = L'\0';
return 0;
}
-#ifdef WIDECHAR
- if (el->el_flags & CHARSET_IS_UTF8) {
- if (!utf8_islead((unsigned char)cbuf[0]))
- goto again; /* discard the byte we read and try again */
+ for (;;) {
+ mbstate_t mbs;
+
++cbp;
- if ((bytes = ct_mbtowc(cp, cbuf, cbp)) == -1) {
- ct_mbtowc_reset;
- if (cbp >= MB_LEN_MAX) { /* "shouldn't happen" */
+ /* This only works because UTF8 is stateless. */
+ memset(&mbs, 0, sizeof(mbs));
+ switch (mbrtowc(cp, cbuf, cbp, &mbs)) {
+ case (size_t)-1:
+ if (cbp > 1) {
+ /*
+ * Invalid sequence, discard all bytes
+ * except the last one.
+ */
+ cbuf[0] = cbuf[cbp - 1];
+ cbp = 0;
+ break;
+ } else {
+ /* Invalid byte, discard it. */
+ cbp = 0;
+ goto again;
+ }
+ case (size_t)-2:
+ /*
+ * We don't support other multibyte charsets.
+ * The second condition shouldn't happen
+ * and is here merely for additional safety.
+ */
+ if ((el->el_flags & CHARSET_IS_UTF8) == 0 ||
+ cbp >= MB_LEN_MAX) {
errno = EILSEQ;
- *cp = '\0';
+ *cp = L'\0';
return -1;
}
+ /* Incomplete sequence, read another byte. */
goto again;
+ default:
+ /* Valid character, process it. */
+ return 1;
}
- } else if (isascii((unsigned char)cbuf[0]) ||
- /* we don't support other multibyte charsets */
- ++cbp != 1 ||
- /* Try non-ASCII characters in a 8-bit character set */
- (bytes = ct_mbtowc(cp, cbuf, cbp)) != 1)
-#endif
- *cp = (unsigned char)cbuf[0];
-
- if ((el->el_flags & IGNORE_EXTCHARS) && bytes > 1) {
- cbp = 0; /* skip this character */
- goto again;
}
-
- return (int)num_read;
}
/* read_pop():
* Pop a macro from the stack
*/
-private void
-read_pop(c_macro_t *ma)
+static void
+read_pop(struct macros *ma)
{
int i;
@@ -393,22 +371,25 @@ read_pop(c_macro_t *ma)
ma->offset = 0;
}
-/* el_getc():
- * Read a character
+static void
+read_clearmacros(struct macros *ma)
+{
+ while (ma->level >= 0)
+ el_free(ma->macro[ma->level--]);
+ ma->offset = 0;
+}
+
+/* el_wgetc():
+ * Read a wide character
*/
-public int
-FUN(el,getc)(EditLine *el, Char *cp)
+int
+el_wgetc(EditLine *el, wchar_t *cp)
{
+ struct macros *ma = &el->el_read->macros;
int num_read;
- c_macro_t *ma = &el->el_chared.c_macro;
terminal__flush(el);
for (;;) {
- if (ma->level < 0) {
- if (!read_preread(el))
- break;
- }
-
if (ma->level < 0)
break;
@@ -427,29 +408,23 @@ FUN(el,getc)(EditLine *el, Char *cp)
return 1;
}
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile, "Turning raw mode on\n");
-#endif /* DEBUG_READ */
if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */
return 0;
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile, "Reading a character\n");
-#endif /* DEBUG_READ */
- num_read = (*el->el_read.read_char)(el, cp);
+ num_read = (*el->el_read->read_char)(el, cp);
+
+ /*
+ * Remember the original reason of a read failure
+ * such that el_wgets() can restore it after doing
+ * various cleanup operation that might change errno.
+ */
if (num_read < 0)
- el->el_errno = errno;
-#ifdef WIDECHAR
- if (el->el_flags & NARROW_READ)
- *cp = *(char *)(void *)cp;
-#endif
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile, "Got it %c\n", *cp);
-#endif /* DEBUG_READ */
+ el->el_read->read_errno = errno;
+
return num_read;
}
-protected void
+libedit_private void
read_prepare(EditLine *el)
{
if (el->el_flags & HANDLE_SIGNALS)
@@ -463,14 +438,14 @@ read_prepare(EditLine *el)
we have the wrong size. */
el_resize(el);
re_clear_display(el); /* reset the display stuff */
- ch_reset(el, 0);
+ ch_reset(el);
re_refresh(el); /* print the prompt */
if (el->el_flags & UNBUFFERED)
terminal__flush(el);
}
-protected void
+libedit_private void
read_finish(EditLine *el)
{
if ((el->el_flags & UNBUFFERED) == 0)
@@ -479,54 +454,52 @@ read_finish(EditLine *el)
sig_clr(el);
}
-public const Char *
-FUN(el,gets)(EditLine *el, int *nread)
+static const wchar_t *
+noedit_wgets(EditLine *el, int *nread)
+{
+ el_line_t *lp = &el->el_line;
+ int num;
+
+ while ((num = (*el->el_read->read_char)(el, lp->lastchar)) == 1) {
+ if (lp->lastchar + 1 >= lp->limit &&
+ !ch_enlargebufs(el, (size_t)2))
+ break;
+ lp->lastchar++;
+ if (el->el_flags & UNBUFFERED ||
+ lp->lastchar[-1] == '\r' ||
+ lp->lastchar[-1] == '\n')
+ break;
+ }
+ if (num == -1 && errno == EINTR)
+ lp->lastchar = lp->buffer;
+ lp->cursor = lp->lastchar;
+ *lp->lastchar = '\0';
+ *nread = (int)(lp->lastchar - lp->buffer);
+ return *nread ? lp->buffer : NULL;
+}
+
+const wchar_t *
+el_wgets(EditLine *el, int *nread)
{
int retval;
el_action_t cmdnum = 0;
int num; /* how many chars we have read at NL */
- Char ch, *cp;
- int crlf = 0;
+ wchar_t ch;
int nrb;
-#ifdef FIONREAD
- c_macro_t *ma = &el->el_chared.c_macro;
-#endif /* FIONREAD */
if (nread == NULL)
nread = &nrb;
*nread = 0;
+ el->el_read->read_errno = 0;
if (el->el_flags & NO_TTY) {
- size_t idx;
-
- cp = el->el_line.buffer;
- while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
- /* make sure there is space for next character */
- if (cp + 1 >= el->el_line.limit) {
- idx = (size_t)(cp - el->el_line.buffer);
- if (!ch_enlargebufs(el, (size_t)2))
- break;
- cp = &el->el_line.buffer[idx];
- }
- cp++;
- if (el->el_flags & UNBUFFERED)
- break;
- if (cp[-1] == '\r' || cp[-1] == '\n')
- break;
- }
- if (num == -1) {
- if (errno == EINTR)
- cp = el->el_line.buffer;
- el->el_errno = errno;
- }
-
- goto noedit;
+ el->el_line.lastchar = el->el_line.buffer;
+ return noedit_wgets(el, nread);
}
-
#ifdef FIONREAD
- if (el->el_tty.t_mode == EX_IO && ma->level < 0) {
- long chrs = 0;
+ if (el->el_tty.t_mode == EX_IO && el->el_read->macros.level < 0) {
+ int chrs = 0;
(void) ioctl(el->el_infd, FIONREAD, &chrs);
if (chrs == 0) {
@@ -543,82 +516,19 @@ FUN(el,gets)(EditLine *el, int *nread)
read_prepare(el);
if (el->el_flags & EDIT_DISABLED) {
- size_t idx;
-
if ((el->el_flags & UNBUFFERED) == 0)
- cp = el->el_line.buffer;
- else
- cp = el->el_line.lastchar;
-
+ el->el_line.lastchar = el->el_line.buffer;
terminal__flush(el);
-
- while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
- /* make sure there is space next character */
- if (cp + 1 >= el->el_line.limit) {
- idx = (size_t)(cp - el->el_line.buffer);
- if (!ch_enlargebufs(el, (size_t)2))
- break;
- cp = &el->el_line.buffer[idx];
- }
- cp++;
- crlf = cp[-1] == '\r' || cp[-1] == '\n';
- if (el->el_flags & UNBUFFERED)
- break;
- if (crlf)
- break;
- }
-
- if (num == -1) {
- if (errno == EINTR)
- cp = el->el_line.buffer;
- el->el_errno = errno;
- }
-
- goto noedit;
+ return noedit_wgets(el, nread);
}
- for (num = OKCMD; num == OKCMD;) { /* while still editing this
- * line */
-#ifdef DEBUG_EDIT
- read_debug(el);
-#endif /* DEBUG_EDIT */
+ for (num = -1; num == -1;) { /* while still editing this line */
/* if EOF or error */
- if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
- num = -1;
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "Returning from el_gets %d\n", num);
-#endif /* DEBUG_READ */
+ if (read_getcmd(el, &cmdnum, &ch) == -1)
break;
- }
- if (el->el_errno == EINTR) {
- el->el_line.buffer[0] = '\0';
- el->el_line.lastchar =
- el->el_line.cursor = el->el_line.buffer;
- break;
- }
- if ((unsigned int)cmdnum >= (unsigned int)el->el_map.nfunc) { /* BUG CHECK command */
-#ifdef DEBUG_EDIT
- (void) fprintf(el->el_errfile,
- "ERROR: illegal command from key 0%o\r\n", ch);
-#endif /* DEBUG_EDIT */
+ if ((size_t)cmdnum >= el->el_map.nfunc) /* BUG CHECK command */
continue; /* try again */
- }
/* now do the real command */
-#ifdef DEBUG_READ
- {
- el_bindings_t *b;
- for (b = el->el_map.help; b->name; b++)
- if (b->func == cmdnum)
- break;
- if (b->name)
- (void) fprintf(el->el_errfile,
- "Executing %s\n", b->name);
- else
- (void) fprintf(el->el_errfile,
- "Error command = %d\n", cmdnum);
- }
-#endif /* DEBUG_READ */
/* vi redo needs these way down the levels... */
el->el_state.thiscmd = cmdnum;
el->el_state.thisch = ch;
@@ -627,16 +537,12 @@ FUN(el,gets)(EditLine *el, int *nread)
el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) {
if (cmdnum == VI_DELETE_PREV_CHAR &&
el->el_chared.c_redo.pos != el->el_chared.c_redo.buf
- && Isprint(el->el_chared.c_redo.pos[-1]))
+ && iswprint(el->el_chared.c_redo.pos[-1]))
el->el_chared.c_redo.pos--;
else
*el->el_chared.c_redo.pos++ = ch;
}
retval = (*el->el_map.func[cmdnum]) (el, ch);
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "Returned state %d\n", retval );
-#endif /* DEBUG_READ */
/* save the last command here */
el->el_state.lastcmd = cmdnum;
@@ -683,22 +589,15 @@ FUN(el,gets)(EditLine *el, int *nread)
break;
case CC_FATAL: /* fatal error, reset to known state */
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "*** editor fatal ERROR ***\r\n\n");
-#endif /* DEBUG_READ */
/* put (real) cursor in a known place */
re_clear_display(el); /* reset the display stuff */
- ch_reset(el, 1); /* reset the input pointers */
+ ch_reset(el); /* reset the input pointers */
+ read_clearmacros(&el->el_read->macros);
re_refresh(el); /* print the prompt again */
break;
case CC_ERROR:
default: /* functions we don't know about */
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "*** editor ERROR ***\r\n\n");
-#endif /* DEBUG_READ */
terminal_beep(el);
terminal__flush(el);
break;
@@ -715,19 +614,14 @@ FUN(el,gets)(EditLine *el, int *nread)
if ((el->el_flags & UNBUFFERED) == 0) {
read_finish(el);
*nread = num != -1 ? num : 0;
- } else {
+ } else
*nread = (int)(el->el_line.lastchar - el->el_line.buffer);
- }
- goto done;
-noedit:
- el->el_line.cursor = el->el_line.lastchar = cp;
- *cp = '\0';
- *nread = (int)(el->el_line.cursor - el->el_line.buffer);
-done:
+
if (*nread == 0) {
if (num == -1) {
*nread = -1;
- errno = el->el_errno;
+ if (el->el_read->read_errno)
+ errno = el->el_read->read_errno;
}
return NULL;
} else