NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
bin/50484: script command of vi(1) is broken
>Number: 50484
>Category: bin
>Synopsis: script command of vi(1) is broken
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Nov 28 06:00:00 +0000 2015
>Originator: Rin Okuyama
>Release: 7.99.22
>Organization:
Department of Physics, Tohoku University
>Environment:
NetBSD XXX 7.99.22 NetBSD 7.99.22 (GENERIC) #0: Fri Nov 27 15:30:56 JST 2015 root@XXX:XXX amd64
>Description:
Vi(1) has undocumented script command, that is similar to shell-mode in
Emacs. However, unfortunately, it is completely broken. If you invoke
the command, you will receive the following error message.
% vi
:script
Error: pty: No such file or directory.
This is because we don't have old-fashioned pty devices by default (and
COMPAT_BSDPTY is disabled in GENERIC kernels on some arch). After making
old pty devices, shell starts on vi(1), but its output becomes corrupted
terribly:
https://twitter.com/LabDrunker/status/670460802927849477
>How-To-Repeat:
Make old pty devices, then invoke script command in vi(1).
% cd /dev; sudo ./MAKEDEV opty
% vi
:script
Then you obtain corrupted outputs from shell.
>Fix:
I've fixed the command:
* convert character encoding appropriately (taken from nvi2)
* use openpty(3) to remove dependency on old pty devices
* avoid buffer overflow in sscr_insert()
* other minor changes
- constify CHAR_T* argument of db_set()
- avoid double run of script command
- simplify sscr_getprompt(); remove duplicate code and use sscr_input()
- replace sscr_matchprompt() with strnstr(3)
Please apply the attached patch below, then it works fine:
https://twitter.com/LabDrunker/status/670463156897083393
Alternatively, if you feel it unpleasant to have such an undocumented
and non-standard command in vi(1), I will provide another patch to
remove it.
======
--- src/external/bsd/nvi/Makefile.inc.orig 2015-11-28 14:05:28.000000000 +0900
+++ src/external/bsd/nvi/Makefile.inc 2015-11-28 14:13:24.000000000 +0900
@@ -7,4 +7,4 @@
BINDIR=/usr/bin
CWARNFLAGS.clang+= -Wno-error=unused-const-variable
-VERSION=1.81.6-2013-11-20nb1
+VERSION=1.81.6-2013-11-20nb2
--- src/external/bsd/nvi/dist/common/vi_db1.c.orig 2015-11-28 14:05:10.000000000 +0900
+++ src/external/bsd/nvi/dist/common/vi_db1.c 2015-11-28 14:13:24.000000000 +0900
@@ -421,10 +421,10 @@
* db_set --
* Store a line in the file.
*
- * PUBLIC: int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t));
+ * PUBLIC: int db_set __P((SCR *, db_recno_t, const CHAR_T *, size_t));
*/
int
-db_set(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len)
+db_set(SCR *sp, db_recno_t lno, const CHAR_T *p, size_t len)
{
DBT data, key;
EXF *ep;
--- src/external/bsd/nvi/dist/ex/ex_script.c.orig 2015-11-28 14:04:45.000000000 +0900
+++ src/external/bsd/nvi/dist/ex/ex_script.c 2015-11-28 14:13:24.000000000 +0900
@@ -29,7 +29,7 @@
#include <sys/select.h>
#endif
#include <sys/stat.h>
-#if defined(HAVE_SYS5_PTY) && !defined(__NetBSD__)
+#if defined(HAVE_SYS5_PTY)
#include <sys/stropts.h>
#endif
#include <sys/time.h>
@@ -45,6 +45,9 @@
#include <string.h>
#include <termios.h>
#include <unistd.h>
+#ifdef __NetBSD__
+#include <util.h>
+#endif
#include "../common/common.h"
#include "../vi/vi.h"
@@ -55,9 +58,12 @@
static int sscr_getprompt __P((SCR *));
static int sscr_init __P((SCR *));
static int sscr_insert __P((SCR *));
-static int sscr_matchprompt __P((SCR *, CHAR_T *, size_t, size_t *));
+#ifdef __NetBSD__
+#define sscr_pty openpty
+#else
static int sscr_pty __P((int *, int *, char *, struct termios *, void *));
-static int sscr_setprompt __P((SCR *, CHAR_T *, size_t));
+#endif
+static int sscr_setprompt __P((SCR *, char *, size_t));
/*
* ex_script -- : sc[ript][!] [file]
@@ -75,6 +81,17 @@
return (1);
}
+ /* Avoid double run. */
+ if (F_ISSET(sp, SC_SCRIPT)) {
+ msgq(sp, M_ERR,
+ "The script command is already runninng");
+ return (1);
+ }
+
+ /* We're going to need a shell. */
+ if (opts_empty(sp, O_SHELL, 0))
+ return (1);
+
/* Switch to the new file. */
if (cmdp->argc != 0 && ex_edit(sp, cmdp))
return (1);
@@ -96,10 +113,6 @@
SCRIPT *sc;
const char *sh, *sh_path;
- /* We're going to need a shell. */
- if (opts_empty(sp, O_SHELL, 0))
- return (1);
-
MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT));
sp->script = sc;
sc->sh_prompt = NULL;
@@ -209,89 +222,28 @@
sscr_getprompt(SCR *sp)
{
struct timeval tv;
- CHAR_T *endp, *p, *t, buf[1024];
- SCRIPT *sc;
fd_set fdset;
- db_recno_t lline;
- size_t llen, len;
- e_key_t value;
- int nr;
-
- FD_ZERO(&fdset);
- endp = buf;
- len = sizeof(buf);
+ int master;
/* Wait up to a second for characters to read. */
tv.tv_sec = 5;
tv.tv_usec = 0;
- sc = sp->script;
- FD_SET(sc->sh_master, &fdset);
- switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+ master = sp->script->sh_master;
+ FD_ZERO(&fdset);
+ FD_SET(master, &fdset);
+ switch (select(master + 1, &fdset, NULL, NULL, &tv)) {
case -1: /* Error or interrupt. */
msgq(sp, M_SYSERR, "select");
- goto prompterr;
- case 0: /* Timeout */
- msgq(sp, M_ERR, "Error: timed out");
- goto prompterr;
- case 1: /* Characters to read. */
break;
- }
-
- /* Read the characters. */
-more: len = sizeof(buf) - (endp - buf);
- switch (nr = read(sc->sh_master, endp, len)) {
- case 0: /* EOF. */
- msgq(sp, M_ERR, "Error: shell: EOF");
- goto prompterr;
- case -1: /* Error or interrupt. */
- msgq(sp, M_SYSERR, "shell");
- goto prompterr;
- default:
- endp += nr;
- break;
- }
-
- /* If any complete lines, push them into the file. */
- for (p = t = buf; p < endp; ++p) {
- value = KEY_VAL(sp, *p);
- if (value == K_CR || value == K_NL) {
- if (db_last(sp, &lline) ||
- db_append(sp, 0, lline, t, p - t))
- goto prompterr;
- t = p + 1;
- }
- }
- if (p > buf) {
- MEMMOVE(buf, t, endp - t);
- endp = buf + (endp - t);
- }
- if (endp == buf)
- goto more;
-
- /* Wait up 1/10 of a second to make sure that we got it all. */
- tv.tv_sec = 0;
- tv.tv_usec = 100000;
- switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
- case -1: /* Error or interrupt. */
- msgq(sp, M_SYSERR, "select");
- goto prompterr;
case 0: /* Timeout */
+ msgq(sp, M_ERR, "Error: timed out");
break;
case 1: /* Characters to read. */
- goto more;
- }
-
- /* Timed out, so theoretically we have a prompt. */
- llen = endp - buf;
- endp = buf;
-
- /* Append the line into the file. */
- if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) {
-prompterr: sscr_end(sp);
- return (1);
+ return (sscr_insert(sp) || sp->script == NULL);
}
- return (sscr_setprompt(sp, buf, llen));
+ sscr_end(sp);
+ return (1);
}
/*
@@ -305,47 +257,53 @@
{
SCRIPT *sc;
db_recno_t last_lno;
- size_t blen, len, last_len, tlen;
+ size_t blen, len, last_len;
int isempty, matchprompt, rval;
ssize_t nw;
- CHAR_T *bp = NULL;
- CHAR_T *p;
+ char *bp = NULL;
+ const char *p;
+ const CHAR_T *ip;
+ size_t ilen;
+
+ sc = sp->script;
/* If there's a prompt on the last line, append the command. */
if (db_last(sp, &last_lno))
return (1);
- if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
+ if (db_get(sp, last_lno, DBG_FATAL, __UNCONST(&ip), &ilen))
return (1);
- if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
+ INT2CHAR(sp, ip, ilen, p, last_len);
+ if (last_len == sc->sh_prompt_len &&
+ strnstr(p, sc->sh_prompt, last_len) == p) {
matchprompt = 1;
- GET_SPACE_RETW(sp, bp, blen, last_len + 128);
- MEMMOVEW(bp, p, last_len);
+ GET_SPACE_RETC(sp, bp, blen, last_len + 128);
+ memmove(bp, p, last_len);
} else
matchprompt = 0;
/* Get something to execute. */
- if (db_eget(sp, lno, &p, &len, &isempty)) {
+ if (db_eget(sp, lno, __UNCONST(&ip), &ilen, &isempty)) {
if (isempty)
goto empty;
goto err1;
}
/* Empty lines aren't interesting. */
- if (len == 0)
+ if (ilen == 0)
goto empty;
+ INT2CHAR(sp, ip, ilen, p, len);
/* Delete any prompt. */
- if (sscr_matchprompt(sp, p, len, &tlen)) {
- if (tlen == len) {
+ if (sc->sh_prompt != NULL && strnstr(p, sc->sh_prompt, len) == p) {
+ len -= sc->sh_prompt_len;
+ if (len == 0) {
empty: msgq(sp, M_BERR, "151|No command to execute");
goto err1;
}
- p += (len - tlen);
- len = tlen;
+ p += sc->sh_prompt_len;
}
/* Push the line to the shell. */
- sc = sp->script;
if ((size_t)(nw = write(sc->sh_master, p, len)) != len)
goto err2;
rval = 0;
@@ -357,13 +315,14 @@
}
if (matchprompt) {
- ADD_SPACE_RETW(sp, bp, blen, last_len + len);
- MEMMOVEW(bp + last_len, p, len);
- if (db_set(sp, last_lno, bp, last_len + len))
+ ADD_SPACE_GOTO(sp, char, bp, blen, last_len + len);
+ memmove(bp + last_len, p, len);
+ CHAR2INT(sp, bp, last_len + len, ip, ilen);
+ if (db_set(sp, last_lno, ip, ilen))
err1: rval = 1;
}
if (matchprompt)
- FREE_SPACEW(sp, bp, blen);
+alloc_err: FREE_SPACE(sp, bp, blen);
return (rval);
}
@@ -465,34 +424,31 @@
sscr_insert(SCR *sp)
{
struct timeval tv;
- CHAR_T *endp, *p, *t;
+ char *endp, *p, *t;
SCRIPT *sc;
fd_set rdfd;
db_recno_t lno;
- size_t blen, len = 0, tlen;
- e_key_t value;
- int nr, rval;
- CHAR_T *bp;
+ size_t len;
+ ssize_t nr;
+ char bp[1024];
+ const CHAR_T *ip;
+ size_t ilen = 0;
/* Find out where the end of the file is. */
if (db_last(sp, &lno))
return (1);
-#define MINREAD 1024
- GET_SPACE_RETW(sp, bp, blen, MINREAD);
endp = bp;
/* Read the characters. */
- rval = 1;
sc = sp->script;
-more: switch (nr = read(sc->sh_master, endp, MINREAD)) {
+more: switch (nr = read(sc->sh_master, endp, bp + sizeof(bp) - endp)) {
case 0: /* EOF; shell just exited. */
sscr_end(sp);
- rval = 0;
- goto ret;
+ return (0);
case -1: /* Error or interrupt. */
msgq(sp, M_SYSERR, "shell");
- goto ret;
+ return (1);
default:
endp += nr;
break;
@@ -500,11 +456,11 @@
/* Append the lines into the file. */
for (p = t = bp; p < endp; ++p) {
- value = KEY_VAL(sp, *p);
- if (value == K_CR || value == K_NL) {
+ if (p == bp + sizeof(bp) - 1 || *p == '\r' || *p == '\n') {
len = p - t;
- if (db_append(sp, 1, lno++, t, len))
- goto ret;
+ if (CHAR2INT(sp, t, len, ip, ilen) ||
+ db_append(sp, 1, lno++, ip, ilen))
+ return (1);
t = p + 1;
}
}
@@ -517,31 +473,29 @@
* want to hang indefinitely because some program is hanging,
* confused the shell, or whatever.
*/
- if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
+ if (len != sc->sh_prompt_len ||
+ strnstr(t, sc->sh_prompt, len) == NULL) {
tv.tv_sec = 0;
tv.tv_usec = 100000;
FD_ZERO(&rdfd);
FD_SET(sc->sh_master, &rdfd);
if (select(sc->sh_master + 1,
&rdfd, NULL, NULL, &tv) == 1) {
- MEMMOVE(bp, t, len);
+ memmove(bp, t, len);
endp = bp + len;
goto more;
}
}
- if (sscr_setprompt(sp, t, len))
+ if (sscr_setprompt(sp, t, len) ||
+ CHAR2INT(sp, t, len, ip, ilen) ||
+ db_append(sp, 1, lno++, ip, ilen))
return (1);
- if (db_append(sp, 1, lno++, t, len))
- goto ret;
}
/* The cursor moves to EOF. */
sp->lno = lno;
- sp->cno = len ? len - 1 : 0;
- rval = vs_refresh(sp, 1);
-
-ret: FREE_SPACEW(sp, bp, blen);
- return (rval);
+ sp->cno = ilen ? ilen - 1 : 0;
+ return (vs_refresh(sp, 1));
}
/*
@@ -551,11 +505,9 @@
*
*/
static int
-sscr_setprompt(SCR *sp, CHAR_T *buf, size_t len)
+sscr_setprompt(SCR *sp, char *buf, size_t len)
{
SCRIPT *sc;
- const char *np;
- size_t nlen;
sc = sp->script;
if (sc->sh_prompt)
@@ -565,51 +517,13 @@
sscr_end(sp);
return (1);
}
- INT2CHAR(sp, buf, len, np, nlen);
- memmove(sc->sh_prompt, np, nlen);
+ memmove(sc->sh_prompt, buf, len);
sc->sh_prompt_len = len;
sc->sh_prompt[len] = '\0';
return (0);
}
/*
- * sscr_matchprompt --
- * Check to see if a line matches the prompt. Nul's indicate
- * parts that can change, in both content and size.
- */
-static int
-sscr_matchprompt(SCR *sp, CHAR_T *lp, size_t line_len, size_t *lenp)
-{
- SCRIPT *sc;
- size_t prompt_len;
- char *pp;
-
- sc = sp->script;
- if (line_len < (prompt_len = sc->sh_prompt_len))
- return (0);
-
- for (pp = sc->sh_prompt;
- prompt_len && line_len; --prompt_len, --line_len) {
- if (*pp == '\0') {
- for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
- if (!prompt_len)
- return (0);
- for (; line_len && *lp != *pp; --line_len, ++lp);
- if (!line_len)
- return (0);
- }
- if (*pp++ != *lp++)
- break;
- }
-
- if (prompt_len)
- return (0);
- if (lenp != NULL)
- *lenp = line_len;
- return (1);
-}
-
-/*
* sscr_end --
* End the pipe to a shell.
*
@@ -664,6 +578,7 @@
F_CLR(gp, G_SCRWIN);
}
+#ifndef __NetBSD__
#ifdef HAVE_SYS5_PTY
static int ptys_open __P((int, char *));
static int ptym_open __P((char *));
@@ -829,4 +744,6 @@
errno = ENOENT; /* out of ptys */
return (-1);
}
+
#endif /* HAVE_SYS5_PTY */
+#endif /* !__NetBSD__ */
--- src/external/bsd/nvi/usr.bin/nvi/Makefile.orig 2015-11-28 14:05:41.000000000 +0900
+++ src/external/bsd/nvi/usr.bin/nvi/Makefile 2015-11-28 14:13:24.000000000 +0900
@@ -18,8 +18,8 @@
#COPTS+=-fno-strict-aliasing
#.endif
-LDADD+= -lcurses -lterminfo
-DPADD+= ${LIBCURSES} ${LIBTERMINFO}
+LDADD+= -lcurses -lterminfo -lutil
+DPADD+= ${LIBCURSES} ${LIBTERMINFO} ${LIBUTIL}
PROG= vi
SRCS= api.c cl_bsd.c cl_funcs.c cl_main.c cl_read.c cl_screen.c cl_term.c \
conv.c cut.c delete.c ex.c ex_abbrev.c ex_append.c \
Home |
Main Index |
Thread Index |
Old Index