xref: /freebsd/contrib/nvi/ex/ex_script.c (revision 755cc40c21ca63388c6a67ba848a908b429e9391)
1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm  * Copyright (c) 1992, 1993, 1994
3b8ba871bSPeter Wemm  *	The Regents of the University of California.  All rights reserved.
4b8ba871bSPeter Wemm  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5b8ba871bSPeter Wemm  *	Keith Bostic.  All rights reserved.
6b8ba871bSPeter Wemm  *
7b8ba871bSPeter Wemm  * This code is derived from software contributed to Berkeley by
8b8ba871bSPeter Wemm  * Brian Hirt.
9b8ba871bSPeter Wemm  *
10b8ba871bSPeter Wemm  * See the LICENSE file for redistribution information.
11b8ba871bSPeter Wemm  */
12b8ba871bSPeter Wemm 
13b8ba871bSPeter Wemm #include "config.h"
14b8ba871bSPeter Wemm 
15b8ba871bSPeter Wemm #include <sys/types.h>
16b8ba871bSPeter Wemm #include <sys/ioctl.h>
17b8ba871bSPeter Wemm #include <sys/queue.h>
18b8ba871bSPeter Wemm #include <sys/select.h>
19b8ba871bSPeter Wemm #include <sys/stat.h>
20b8ba871bSPeter Wemm #include <sys/wait.h>
21b8ba871bSPeter Wemm 
22b8ba871bSPeter Wemm #include <bitstring.h>
23b8ba871bSPeter Wemm #include <errno.h>
24b8ba871bSPeter Wemm #include <fcntl.h>
25b8ba871bSPeter Wemm #include <grp.h>
26b8ba871bSPeter Wemm #include <limits.h>
27f0957ccaSPeter Wemm #include <signal.h>
28f0957ccaSPeter Wemm #include <stdio.h>
29b8ba871bSPeter Wemm #include <stdlib.h>
30b8ba871bSPeter Wemm #include <string.h>
31b8ba871bSPeter Wemm #include <termios.h>
32b8ba871bSPeter Wemm #include <unistd.h>
33f0957ccaSPeter Wemm #ifdef HAVE_LIBUTIL_H
34f0957ccaSPeter Wemm #include <libutil.h>
35*755cc40cSBaptiste Daroussin #elif defined HAVE_PTY_H
36*755cc40cSBaptiste Daroussin #include <pty.h>
37f0957ccaSPeter Wemm #else
38f0957ccaSPeter Wemm #include <util.h>
39f0957ccaSPeter Wemm #endif
40b8ba871bSPeter Wemm 
41b8ba871bSPeter Wemm #include "../common/common.h"
42b8ba871bSPeter Wemm #include "../vi/vi.h"
43b8ba871bSPeter Wemm #include "script.h"
44b8ba871bSPeter Wemm #include "pathnames.h"
45b8ba871bSPeter Wemm 
46c271fa92SBaptiste Daroussin static void	sscr_check(SCR *);
47c271fa92SBaptiste Daroussin static int	sscr_getprompt(SCR *);
48c271fa92SBaptiste Daroussin static int	sscr_init(SCR *);
49c271fa92SBaptiste Daroussin static int	sscr_insert(SCR *);
50c271fa92SBaptiste Daroussin static int	sscr_matchprompt(SCR *, char *, size_t, size_t *);
51c271fa92SBaptiste Daroussin static int	sscr_setprompt(SCR *, char *, size_t);
52b8ba871bSPeter Wemm 
53b8ba871bSPeter Wemm /*
54b8ba871bSPeter Wemm  * ex_script -- : sc[ript][!] [file]
55b8ba871bSPeter Wemm  *	Switch to script mode.
56b8ba871bSPeter Wemm  *
57c271fa92SBaptiste Daroussin  * PUBLIC: int ex_script(SCR *, EXCMD *);
58b8ba871bSPeter Wemm  */
59b8ba871bSPeter Wemm int
ex_script(SCR * sp,EXCMD * cmdp)60f0957ccaSPeter Wemm ex_script(SCR *sp, EXCMD *cmdp)
61b8ba871bSPeter Wemm {
62b8ba871bSPeter Wemm 	/* Vi only command. */
63b8ba871bSPeter Wemm 	if (!F_ISSET(sp, SC_VI)) {
64b8ba871bSPeter Wemm 		msgq(sp, M_ERR,
65b8ba871bSPeter Wemm 		    "150|The script command is only available in vi mode");
66b8ba871bSPeter Wemm 		return (1);
67b8ba871bSPeter Wemm 	}
68b8ba871bSPeter Wemm 
69b8ba871bSPeter Wemm 	/* Switch to the new file. */
70b8ba871bSPeter Wemm 	if (cmdp->argc != 0 && ex_edit(sp, cmdp))
71b8ba871bSPeter Wemm 		return (1);
72b8ba871bSPeter Wemm 
73b8ba871bSPeter Wemm 	/* Create the shell, figure out the prompt. */
74b8ba871bSPeter Wemm 	if (sscr_init(sp))
75b8ba871bSPeter Wemm 		return (1);
76b8ba871bSPeter Wemm 
77b8ba871bSPeter Wemm 	return (0);
78b8ba871bSPeter Wemm }
79b8ba871bSPeter Wemm 
80b8ba871bSPeter Wemm /*
81b8ba871bSPeter Wemm  * sscr_init --
82b8ba871bSPeter Wemm  *	Create a pty setup for a shell.
83b8ba871bSPeter Wemm  */
84b8ba871bSPeter Wemm static int
sscr_init(SCR * sp)85f0957ccaSPeter Wemm sscr_init(SCR *sp)
86b8ba871bSPeter Wemm {
87b8ba871bSPeter Wemm 	SCRIPT *sc;
88b8ba871bSPeter Wemm 	char *sh, *sh_path;
89b8ba871bSPeter Wemm 
90b8ba871bSPeter Wemm 	/* We're going to need a shell. */
91b8ba871bSPeter Wemm 	if (opts_empty(sp, O_SHELL, 0))
92b8ba871bSPeter Wemm 		return (1);
93b8ba871bSPeter Wemm 
94110d525eSBaptiste Daroussin 	MALLOC_RET(sp, sc, sizeof(SCRIPT));
95b8ba871bSPeter Wemm 	sp->script = sc;
96b8ba871bSPeter Wemm 	sc->sh_prompt = NULL;
97b8ba871bSPeter Wemm 	sc->sh_prompt_len = 0;
98b8ba871bSPeter Wemm 
99b8ba871bSPeter Wemm 	/*
100b8ba871bSPeter Wemm 	 * There are two different processes running through this code.
101b8ba871bSPeter Wemm 	 * They are the shell and the parent.
102b8ba871bSPeter Wemm 	 */
103b8ba871bSPeter Wemm 	sc->sh_master = sc->sh_slave = -1;
104b8ba871bSPeter Wemm 
105b8ba871bSPeter Wemm 	if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
106b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "tcgetattr");
107b8ba871bSPeter Wemm 		goto err;
108b8ba871bSPeter Wemm 	}
109b8ba871bSPeter Wemm 
110b8ba871bSPeter Wemm 	/*
111b8ba871bSPeter Wemm 	 * Turn off output postprocessing and echo.
112b8ba871bSPeter Wemm 	 */
113b8ba871bSPeter Wemm 	sc->sh_term.c_oflag &= ~OPOST;
114b8ba871bSPeter Wemm 	sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);
115b8ba871bSPeter Wemm 
116b8ba871bSPeter Wemm 	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
117b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "tcgetattr");
118b8ba871bSPeter Wemm 		goto err;
119b8ba871bSPeter Wemm 	}
120b8ba871bSPeter Wemm 
121f0957ccaSPeter Wemm 	if (openpty(&sc->sh_master,
122b8ba871bSPeter Wemm 	    &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
123f0957ccaSPeter Wemm 		msgq(sp, M_SYSERR, "openpty");
124b8ba871bSPeter Wemm 		goto err;
125b8ba871bSPeter Wemm 	}
126b8ba871bSPeter Wemm 
127b8ba871bSPeter Wemm 	/*
128b8ba871bSPeter Wemm 	 * __TK__ huh?
129b8ba871bSPeter Wemm 	 * Don't use vfork() here, because the signal semantics differ from
130b8ba871bSPeter Wemm 	 * implementation to implementation.
131b8ba871bSPeter Wemm 	 */
132b8ba871bSPeter Wemm 	switch (sc->sh_pid = fork()) {
133b8ba871bSPeter Wemm 	case -1:			/* Error. */
134b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "fork");
135b8ba871bSPeter Wemm err:		if (sc->sh_master != -1)
136b8ba871bSPeter Wemm 			(void)close(sc->sh_master);
137b8ba871bSPeter Wemm 		if (sc->sh_slave != -1)
138b8ba871bSPeter Wemm 			(void)close(sc->sh_slave);
139b8ba871bSPeter Wemm 		return (1);
140b8ba871bSPeter Wemm 	case 0:				/* Utility. */
141b8ba871bSPeter Wemm 		/*
142b8ba871bSPeter Wemm 		 * XXX
143b8ba871bSPeter Wemm 		 * So that shells that do command line editing turn it off.
144b8ba871bSPeter Wemm 		 */
145b8ba871bSPeter Wemm 		(void)setenv("TERM", "emacs", 1);
146b8ba871bSPeter Wemm 		(void)setenv("TERMCAP", "emacs:", 1);
147b8ba871bSPeter Wemm 		(void)setenv("EMACS", "t", 1);
148b8ba871bSPeter Wemm 
149b8ba871bSPeter Wemm 		(void)setsid();
150b8ba871bSPeter Wemm #ifdef TIOCSCTTY
151b8ba871bSPeter Wemm 		/*
152b8ba871bSPeter Wemm 		 * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
153b8ba871bSPeter Wemm 		 * ioctl, not by opening a terminal device file.  POSIX 1003.1
154b8ba871bSPeter Wemm 		 * doesn't define a portable way to do this.  If TIOCSCTTY is
155b8ba871bSPeter Wemm 		 * not available, hope that the open does it.
156b8ba871bSPeter Wemm 		 */
157b8ba871bSPeter Wemm 		(void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
158b8ba871bSPeter Wemm #endif
159b8ba871bSPeter Wemm 		(void)close(sc->sh_master);
160b8ba871bSPeter Wemm 		(void)dup2(sc->sh_slave, STDIN_FILENO);
161b8ba871bSPeter Wemm 		(void)dup2(sc->sh_slave, STDOUT_FILENO);
162b8ba871bSPeter Wemm 		(void)dup2(sc->sh_slave, STDERR_FILENO);
163b8ba871bSPeter Wemm 		(void)close(sc->sh_slave);
164b8ba871bSPeter Wemm 
165b8ba871bSPeter Wemm 		/* Assumes that all shells have -i. */
166b8ba871bSPeter Wemm 		sh_path = O_STR(sp, O_SHELL);
167b8ba871bSPeter Wemm 		if ((sh = strrchr(sh_path, '/')) == NULL)
168b8ba871bSPeter Wemm 			sh = sh_path;
169b8ba871bSPeter Wemm 		else
170b8ba871bSPeter Wemm 			++sh;
171b8ba871bSPeter Wemm 		execl(sh_path, sh, "-i", NULL);
172b8ba871bSPeter Wemm 		msgq_str(sp, M_SYSERR, sh_path, "execl: %s");
173b8ba871bSPeter Wemm 		_exit(127);
174b8ba871bSPeter Wemm 	default:			/* Parent. */
175b8ba871bSPeter Wemm 		break;
176b8ba871bSPeter Wemm 	}
177b8ba871bSPeter Wemm 
178b8ba871bSPeter Wemm 	if (sscr_getprompt(sp))
179b8ba871bSPeter Wemm 		return (1);
180b8ba871bSPeter Wemm 
181b8ba871bSPeter Wemm 	F_SET(sp, SC_SCRIPT);
182b8ba871bSPeter Wemm 	F_SET(sp->gp, G_SCRWIN);
183b8ba871bSPeter Wemm 	return (0);
184b8ba871bSPeter Wemm }
185b8ba871bSPeter Wemm 
186b8ba871bSPeter Wemm /*
187b8ba871bSPeter Wemm  * sscr_getprompt --
188b8ba871bSPeter Wemm  *	Eat lines printed by the shell until a line with no trailing
189b8ba871bSPeter Wemm  *	carriage return comes; set the prompt from that line.
190b8ba871bSPeter Wemm  */
191b8ba871bSPeter Wemm static int
sscr_getprompt(SCR * sp)192f0957ccaSPeter Wemm sscr_getprompt(SCR *sp)
193b8ba871bSPeter Wemm {
194f0957ccaSPeter Wemm 	EX_PRIVATE *exp;
195b8ba871bSPeter Wemm 	struct timeval tv;
196f0957ccaSPeter Wemm 	char *endp, *p, *t, buf[1024];
197b8ba871bSPeter Wemm 	SCRIPT *sc;
198b8ba871bSPeter Wemm 	fd_set fdset;
199b8ba871bSPeter Wemm 	recno_t lline;
200b8ba871bSPeter Wemm 	size_t llen, len;
201b8ba871bSPeter Wemm 	int nr;
202f0957ccaSPeter Wemm 	CHAR_T *wp;
203f0957ccaSPeter Wemm 	size_t wlen;
204f0957ccaSPeter Wemm 
205f0957ccaSPeter Wemm 	exp = EXP(sp);
206b8ba871bSPeter Wemm 
207b8ba871bSPeter Wemm 	FD_ZERO(&fdset);
208b8ba871bSPeter Wemm 	endp = buf;
209b8ba871bSPeter Wemm 	len = sizeof(buf);
210b8ba871bSPeter Wemm 
211b8ba871bSPeter Wemm 	/* Wait up to a second for characters to read. */
212b8ba871bSPeter Wemm 	tv.tv_sec = 5;
213b8ba871bSPeter Wemm 	tv.tv_usec = 0;
214b8ba871bSPeter Wemm 	sc = sp->script;
215b8ba871bSPeter Wemm 	FD_SET(sc->sh_master, &fdset);
216b8ba871bSPeter Wemm 	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
217b8ba871bSPeter Wemm 	case -1:		/* Error or interrupt. */
218b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "select");
219b8ba871bSPeter Wemm 		goto prompterr;
220b8ba871bSPeter Wemm 	case  0:		/* Timeout */
221b8ba871bSPeter Wemm 		msgq(sp, M_ERR, "Error: timed out");
222b8ba871bSPeter Wemm 		goto prompterr;
223b8ba871bSPeter Wemm 	case  1:		/* Characters to read. */
224b8ba871bSPeter Wemm 		break;
225b8ba871bSPeter Wemm 	}
226b8ba871bSPeter Wemm 
227b8ba871bSPeter Wemm 	/* Read the characters. */
228b8ba871bSPeter Wemm more:	len = sizeof(buf) - (endp - buf);
229b8ba871bSPeter Wemm 	switch (nr = read(sc->sh_master, endp, len)) {
230b8ba871bSPeter Wemm 	case  0:			/* EOF. */
231b8ba871bSPeter Wemm 		msgq(sp, M_ERR, "Error: shell: EOF");
232b8ba871bSPeter Wemm 		goto prompterr;
233b8ba871bSPeter Wemm 	case -1:			/* Error or interrupt. */
234b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "shell");
235b8ba871bSPeter Wemm 		goto prompterr;
236b8ba871bSPeter Wemm 	default:
237b8ba871bSPeter Wemm 		endp += nr;
238b8ba871bSPeter Wemm 		break;
239b8ba871bSPeter Wemm 	}
240b8ba871bSPeter Wemm 
241b8ba871bSPeter Wemm 	/* If any complete lines, push them into the file. */
242b8ba871bSPeter Wemm 	for (p = t = buf; p < endp; ++p) {
243f0957ccaSPeter Wemm 		if (*p == '\r' || *p == '\n') {
244f0957ccaSPeter Wemm 			if (CHAR2INT5(sp, exp->ibcw, t, p - t, wp, wlen))
245f0957ccaSPeter Wemm 				goto conv_err;
246b8ba871bSPeter Wemm 			if (db_last(sp, &lline) ||
247f0957ccaSPeter Wemm 			    db_append(sp, 0, lline, wp, wlen))
248b8ba871bSPeter Wemm 				goto prompterr;
249b8ba871bSPeter Wemm 			t = p + 1;
250b8ba871bSPeter Wemm 		}
251b8ba871bSPeter Wemm 	}
252b8ba871bSPeter Wemm 	if (p > buf) {
253b8ba871bSPeter Wemm 		memmove(buf, t, endp - t);
254b8ba871bSPeter Wemm 		endp = buf + (endp - t);
255b8ba871bSPeter Wemm 	}
256b8ba871bSPeter Wemm 	if (endp == buf)
257b8ba871bSPeter Wemm 		goto more;
258b8ba871bSPeter Wemm 
259b8ba871bSPeter Wemm 	/* Wait up 1/10 of a second to make sure that we got it all. */
260b8ba871bSPeter Wemm 	tv.tv_sec = 0;
261b8ba871bSPeter Wemm 	tv.tv_usec = 100000;
262b8ba871bSPeter Wemm 	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
263b8ba871bSPeter Wemm 	case -1:		/* Error or interrupt. */
264b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "select");
265b8ba871bSPeter Wemm 		goto prompterr;
266b8ba871bSPeter Wemm 	case  0:		/* Timeout */
267b8ba871bSPeter Wemm 		break;
268b8ba871bSPeter Wemm 	case  1:		/* Characters to read. */
269b8ba871bSPeter Wemm 		goto more;
270b8ba871bSPeter Wemm 	}
271b8ba871bSPeter Wemm 
272b8ba871bSPeter Wemm 	/* Timed out, so theoretically we have a prompt. */
273b8ba871bSPeter Wemm 	llen = endp - buf;
274b8ba871bSPeter Wemm 	endp = buf;
275b8ba871bSPeter Wemm 
276b8ba871bSPeter Wemm 	/* Append the line into the file. */
277f0957ccaSPeter Wemm 	if (CHAR2INT5(sp, exp->ibcw, buf, llen, wp, wlen))
278f0957ccaSPeter Wemm 		goto conv_err;
279f0957ccaSPeter Wemm 	if (db_last(sp, &lline) || db_append(sp, 0, lline, wp, wlen)) {
280f0957ccaSPeter Wemm 		if (0)
281f0957ccaSPeter Wemm conv_err:		msgq(sp, M_ERR, "323|Invalid input. Truncated.");
282b8ba871bSPeter Wemm prompterr:	sscr_end(sp);
283b8ba871bSPeter Wemm 		return (1);
284b8ba871bSPeter Wemm 	}
285b8ba871bSPeter Wemm 
286b8ba871bSPeter Wemm 	return (sscr_setprompt(sp, buf, llen));
287b8ba871bSPeter Wemm }
288b8ba871bSPeter Wemm 
289b8ba871bSPeter Wemm /*
290b8ba871bSPeter Wemm  * sscr_exec --
291b8ba871bSPeter Wemm  *	Take a line and hand it off to the shell.
292b8ba871bSPeter Wemm  *
293c271fa92SBaptiste Daroussin  * PUBLIC: int sscr_exec(SCR *, recno_t);
294b8ba871bSPeter Wemm  */
295b8ba871bSPeter Wemm int
sscr_exec(SCR * sp,recno_t lno)296f0957ccaSPeter Wemm sscr_exec(SCR *sp, recno_t lno)
297b8ba871bSPeter Wemm {
298b8ba871bSPeter Wemm 	SCRIPT *sc;
299b8ba871bSPeter Wemm 	recno_t last_lno;
300b8ba871bSPeter Wemm 	size_t blen, len, last_len, tlen;
301b8ba871bSPeter Wemm 	int isempty, matchprompt, nw, rval;
302f0957ccaSPeter Wemm 	char *bp = NULL, *p;
303f0957ccaSPeter Wemm 	CHAR_T *wp;
304f0957ccaSPeter Wemm 	size_t wlen;
305b8ba871bSPeter Wemm 
306b8ba871bSPeter Wemm 	/* If there's a prompt on the last line, append the command. */
307b8ba871bSPeter Wemm 	if (db_last(sp, &last_lno))
308b8ba871bSPeter Wemm 		return (1);
309f0957ccaSPeter Wemm 	if (db_get(sp, last_lno, DBG_FATAL, &wp, &wlen))
310b8ba871bSPeter Wemm 		return (1);
311f0957ccaSPeter Wemm 	INT2CHAR(sp, wp, wlen, p, last_len);
312b8ba871bSPeter Wemm 	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
313b8ba871bSPeter Wemm 		matchprompt = 1;
314f0957ccaSPeter Wemm 		GET_SPACE_RETC(sp, bp, blen, last_len + 128);
315b8ba871bSPeter Wemm 		memmove(bp, p, last_len);
316b8ba871bSPeter Wemm 	} else
317b8ba871bSPeter Wemm 		matchprompt = 0;
318b8ba871bSPeter Wemm 
319b8ba871bSPeter Wemm 	/* Get something to execute. */
320f0957ccaSPeter Wemm 	if (db_eget(sp, lno, &wp, &wlen, &isempty)) {
321b8ba871bSPeter Wemm 		if (isempty)
322b8ba871bSPeter Wemm 			goto empty;
323b8ba871bSPeter Wemm 		goto err1;
324b8ba871bSPeter Wemm 	}
325b8ba871bSPeter Wemm 
326b8ba871bSPeter Wemm 	/* Empty lines aren't interesting. */
327f0957ccaSPeter Wemm 	if (wlen == 0)
328b8ba871bSPeter Wemm 		goto empty;
329f0957ccaSPeter Wemm 	INT2CHAR(sp, wp, wlen, p, len);
330b8ba871bSPeter Wemm 
331b8ba871bSPeter Wemm 	/* Delete any prompt. */
332b8ba871bSPeter Wemm 	if (sscr_matchprompt(sp, p, len, &tlen)) {
333b8ba871bSPeter Wemm 		if (tlen == len) {
334b8ba871bSPeter Wemm empty:			msgq(sp, M_BERR, "151|No command to execute");
335b8ba871bSPeter Wemm 			goto err1;
336b8ba871bSPeter Wemm 		}
337b8ba871bSPeter Wemm 		p += (len - tlen);
338b8ba871bSPeter Wemm 		len = tlen;
339b8ba871bSPeter Wemm 	}
340b8ba871bSPeter Wemm 
341b8ba871bSPeter Wemm 	/* Push the line to the shell. */
342b8ba871bSPeter Wemm 	sc = sp->script;
343b8ba871bSPeter Wemm 	if ((nw = write(sc->sh_master, p, len)) != len)
344b8ba871bSPeter Wemm 		goto err2;
345b8ba871bSPeter Wemm 	rval = 0;
346b8ba871bSPeter Wemm 	if (write(sc->sh_master, "\n", 1) != 1) {
347b8ba871bSPeter Wemm err2:		if (nw == 0)
348b8ba871bSPeter Wemm 			errno = EIO;
349b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "shell");
350b8ba871bSPeter Wemm 		goto err1;
351b8ba871bSPeter Wemm 	}
352b8ba871bSPeter Wemm 
353b8ba871bSPeter Wemm 	if (matchprompt) {
354f0957ccaSPeter Wemm 		ADD_SPACE_RETC(sp, bp, blen, last_len + len);
355b8ba871bSPeter Wemm 		memmove(bp + last_len, p, len);
356f0957ccaSPeter Wemm 		CHAR2INT(sp, bp, last_len + len, wp, wlen);
357f0957ccaSPeter Wemm 		if (db_set(sp, last_lno, wp, wlen))
358b8ba871bSPeter Wemm err1:			rval = 1;
359b8ba871bSPeter Wemm 	}
360b8ba871bSPeter Wemm 	if (matchprompt)
361b8ba871bSPeter Wemm 		FREE_SPACE(sp, bp, blen);
362b8ba871bSPeter Wemm 	return (rval);
363b8ba871bSPeter Wemm }
364b8ba871bSPeter Wemm 
365b8ba871bSPeter Wemm /*
366b8ba871bSPeter Wemm  * sscr_input --
367b8ba871bSPeter Wemm  *	Read any waiting shell input.
368b8ba871bSPeter Wemm  *
369c271fa92SBaptiste Daroussin  * PUBLIC: int sscr_input(SCR *);
370b8ba871bSPeter Wemm  */
371b8ba871bSPeter Wemm int
sscr_input(SCR * sp)372f0957ccaSPeter Wemm sscr_input(SCR *sp)
373b8ba871bSPeter Wemm {
374b8ba871bSPeter Wemm 	GS *gp;
375b8ba871bSPeter Wemm 	struct timeval poll;
376b8ba871bSPeter Wemm 	fd_set rdfd;
377b8ba871bSPeter Wemm 	int maxfd;
378b8ba871bSPeter Wemm 
379b8ba871bSPeter Wemm 	gp = sp->gp;
380b8ba871bSPeter Wemm 
381b8ba871bSPeter Wemm loop:	maxfd = 0;
382b8ba871bSPeter Wemm 	FD_ZERO(&rdfd);
383b8ba871bSPeter Wemm 	poll.tv_sec = 0;
384b8ba871bSPeter Wemm 	poll.tv_usec = 0;
385b8ba871bSPeter Wemm 
386b8ba871bSPeter Wemm 	/* Set up the input mask. */
387f0957ccaSPeter Wemm 	TAILQ_FOREACH(sp, gp->dq, q)
388b8ba871bSPeter Wemm 		if (F_ISSET(sp, SC_SCRIPT)) {
389b8ba871bSPeter Wemm 			FD_SET(sp->script->sh_master, &rdfd);
390b8ba871bSPeter Wemm 			if (sp->script->sh_master > maxfd)
391b8ba871bSPeter Wemm 				maxfd = sp->script->sh_master;
392b8ba871bSPeter Wemm 		}
393b8ba871bSPeter Wemm 
394b8ba871bSPeter Wemm 	/* Check for input. */
395b8ba871bSPeter Wemm 	switch (select(maxfd + 1, &rdfd, NULL, NULL, &poll)) {
396b8ba871bSPeter Wemm 	case -1:
397b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "select");
398b8ba871bSPeter Wemm 		return (1);
399b8ba871bSPeter Wemm 	case 0:
400b8ba871bSPeter Wemm 		return (0);
401b8ba871bSPeter Wemm 	default:
402b8ba871bSPeter Wemm 		break;
403b8ba871bSPeter Wemm 	}
404b8ba871bSPeter Wemm 
405b8ba871bSPeter Wemm 	/* Read the input. */
406f0957ccaSPeter Wemm 	TAILQ_FOREACH(sp, gp->dq, q)
407b8ba871bSPeter Wemm 		if (F_ISSET(sp, SC_SCRIPT) &&
408f0957ccaSPeter Wemm 		    FD_ISSET(sp->script->sh_master, &rdfd) &&
409f0957ccaSPeter Wemm 		    sscr_insert(sp))
410b8ba871bSPeter Wemm 			return (1);
411b8ba871bSPeter Wemm 	goto loop;
412b8ba871bSPeter Wemm }
413b8ba871bSPeter Wemm 
414b8ba871bSPeter Wemm /*
415b8ba871bSPeter Wemm  * sscr_insert --
416b8ba871bSPeter Wemm  *	Take a line from the shell and insert it into the file.
417b8ba871bSPeter Wemm  */
418b8ba871bSPeter Wemm static int
sscr_insert(SCR * sp)419f0957ccaSPeter Wemm sscr_insert(SCR *sp)
420b8ba871bSPeter Wemm {
421f0957ccaSPeter Wemm 	EX_PRIVATE *exp;
422b8ba871bSPeter Wemm 	struct timeval tv;
423f0957ccaSPeter Wemm 	char *endp, *p, *t;
424b8ba871bSPeter Wemm 	SCRIPT *sc;
425b8ba871bSPeter Wemm 	fd_set rdfd;
426b8ba871bSPeter Wemm 	recno_t lno;
427b8ba871bSPeter Wemm 	size_t blen, len, tlen;
428b8ba871bSPeter Wemm 	int nr, rval;
429b8ba871bSPeter Wemm 	char *bp;
430f0957ccaSPeter Wemm 	CHAR_T *wp;
431f0957ccaSPeter Wemm 	size_t wlen = 0;
432f0957ccaSPeter Wemm 
433f0957ccaSPeter Wemm 	exp = EXP(sp);
434f0957ccaSPeter Wemm 
435b8ba871bSPeter Wemm 
436b8ba871bSPeter Wemm 	/* Find out where the end of the file is. */
437b8ba871bSPeter Wemm 	if (db_last(sp, &lno))
438b8ba871bSPeter Wemm 		return (1);
439b8ba871bSPeter Wemm 
440b8ba871bSPeter Wemm #define	MINREAD	1024
441f0957ccaSPeter Wemm 	GET_SPACE_RETC(sp, bp, blen, MINREAD);
442b8ba871bSPeter Wemm 	endp = bp;
443b8ba871bSPeter Wemm 
444b8ba871bSPeter Wemm 	/* Read the characters. */
445b8ba871bSPeter Wemm 	rval = 1;
446b8ba871bSPeter Wemm 	sc = sp->script;
447b8ba871bSPeter Wemm more:	switch (nr = read(sc->sh_master, endp, MINREAD)) {
448b8ba871bSPeter Wemm 	case  0:			/* EOF; shell just exited. */
449b8ba871bSPeter Wemm 		sscr_end(sp);
450b8ba871bSPeter Wemm 		rval = 0;
451b8ba871bSPeter Wemm 		goto ret;
452b8ba871bSPeter Wemm 	case -1:			/* Error or interrupt. */
453b8ba871bSPeter Wemm 		msgq(sp, M_SYSERR, "shell");
454b8ba871bSPeter Wemm 		goto ret;
455b8ba871bSPeter Wemm 	default:
456b8ba871bSPeter Wemm 		endp += nr;
457b8ba871bSPeter Wemm 		break;
458b8ba871bSPeter Wemm 	}
459b8ba871bSPeter Wemm 
460b8ba871bSPeter Wemm 	/* Append the lines into the file. */
461b8ba871bSPeter Wemm 	for (p = t = bp; p < endp; ++p) {
462f0957ccaSPeter Wemm 		if (*p == '\r' || *p == '\n') {
463b8ba871bSPeter Wemm 			len = p - t;
464f0957ccaSPeter Wemm 			if (CHAR2INT5(sp, exp->ibcw, t, len, wp, wlen))
465f0957ccaSPeter Wemm 				goto conv_err;
466f0957ccaSPeter Wemm 			if (db_append(sp, 1, lno++, wp, wlen))
467b8ba871bSPeter Wemm 				goto ret;
468b8ba871bSPeter Wemm 			t = p + 1;
469b8ba871bSPeter Wemm 		}
470b8ba871bSPeter Wemm 	}
471b8ba871bSPeter Wemm 	if (p > t) {
472b8ba871bSPeter Wemm 		len = p - t;
473b8ba871bSPeter Wemm 		/*
474b8ba871bSPeter Wemm 		 * If the last thing from the shell isn't another prompt, wait
475b8ba871bSPeter Wemm 		 * up to 1/10 of a second for more stuff to show up, so that
476b8ba871bSPeter Wemm 		 * we don't break the output into two separate lines.  Don't
477b8ba871bSPeter Wemm 		 * want to hang indefinitely because some program is hanging,
478b8ba871bSPeter Wemm 		 * confused the shell, or whatever.
479b8ba871bSPeter Wemm 		 */
480b8ba871bSPeter Wemm 		if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
481b8ba871bSPeter Wemm 			tv.tv_sec = 0;
482b8ba871bSPeter Wemm 			tv.tv_usec = 100000;
483b8ba871bSPeter Wemm 			FD_ZERO(&rdfd);
484b8ba871bSPeter Wemm 			FD_SET(sc->sh_master, &rdfd);
485b8ba871bSPeter Wemm 			if (select(sc->sh_master + 1,
486b8ba871bSPeter Wemm 			    &rdfd, NULL, NULL, &tv) == 1) {
487b8ba871bSPeter Wemm 				memmove(bp, t, len);
488b8ba871bSPeter Wemm 				endp = bp + len;
489b8ba871bSPeter Wemm 				goto more;
490b8ba871bSPeter Wemm 			}
491b8ba871bSPeter Wemm 		}
492b8ba871bSPeter Wemm 		if (sscr_setprompt(sp, t, len))
493b8ba871bSPeter Wemm 			return (1);
494f0957ccaSPeter Wemm 		if (CHAR2INT5(sp, exp->ibcw, t, len, wp, wlen))
495f0957ccaSPeter Wemm 			goto conv_err;
496f0957ccaSPeter Wemm 		if (db_append(sp, 1, lno++, wp, wlen))
497b8ba871bSPeter Wemm 			goto ret;
498b8ba871bSPeter Wemm 	}
499b8ba871bSPeter Wemm 
500b8ba871bSPeter Wemm 	/* The cursor moves to EOF. */
501b8ba871bSPeter Wemm 	sp->lno = lno;
502f0957ccaSPeter Wemm 	sp->cno = wlen ? wlen - 1 : 0;
503b8ba871bSPeter Wemm 	rval = vs_refresh(sp, 1);
504b8ba871bSPeter Wemm 
505f0957ccaSPeter Wemm 	if (0)
506f0957ccaSPeter Wemm conv_err:	msgq(sp, M_ERR, "323|Invalid input. Truncated.");
507f0957ccaSPeter Wemm 
508b8ba871bSPeter Wemm ret:	FREE_SPACE(sp, bp, blen);
509b8ba871bSPeter Wemm 	return (rval);
510b8ba871bSPeter Wemm }
511b8ba871bSPeter Wemm 
512b8ba871bSPeter Wemm /*
513b8ba871bSPeter Wemm  * sscr_setprompt --
514b8ba871bSPeter Wemm  *
515b8ba871bSPeter Wemm  * Set the prompt to the last line we got from the shell.
516b8ba871bSPeter Wemm  *
517b8ba871bSPeter Wemm  */
518b8ba871bSPeter Wemm static int
sscr_setprompt(SCR * sp,char * buf,size_t len)519f0957ccaSPeter Wemm sscr_setprompt(SCR *sp, char *buf, size_t len)
520b8ba871bSPeter Wemm {
521b8ba871bSPeter Wemm 	SCRIPT *sc;
522b8ba871bSPeter Wemm 
523b8ba871bSPeter Wemm 	sc = sp->script;
524b8ba871bSPeter Wemm 	free(sc->sh_prompt);
525110d525eSBaptiste Daroussin 	MALLOC(sp, sc->sh_prompt, len + 1);
526b8ba871bSPeter Wemm 	if (sc->sh_prompt == NULL) {
527b8ba871bSPeter Wemm 		sscr_end(sp);
528b8ba871bSPeter Wemm 		return (1);
529b8ba871bSPeter Wemm 	}
530b8ba871bSPeter Wemm 	memmove(sc->sh_prompt, buf, len);
531b8ba871bSPeter Wemm 	sc->sh_prompt_len = len;
532b8ba871bSPeter Wemm 	sc->sh_prompt[len] = '\0';
533b8ba871bSPeter Wemm 	return (0);
534b8ba871bSPeter Wemm }
535b8ba871bSPeter Wemm 
536b8ba871bSPeter Wemm /*
537b8ba871bSPeter Wemm  * sscr_matchprompt --
538b8ba871bSPeter Wemm  *	Check to see if a line matches the prompt.  Nul's indicate
539b8ba871bSPeter Wemm  *	parts that can change, in both content and size.
540b8ba871bSPeter Wemm  */
541b8ba871bSPeter Wemm static int
sscr_matchprompt(SCR * sp,char * lp,size_t line_len,size_t * lenp)542f0957ccaSPeter Wemm sscr_matchprompt(SCR *sp, char *lp, size_t line_len, size_t *lenp)
543b8ba871bSPeter Wemm {
544b8ba871bSPeter Wemm 	SCRIPT *sc;
545b8ba871bSPeter Wemm 	size_t prompt_len;
546b8ba871bSPeter Wemm 	char *pp;
547b8ba871bSPeter Wemm 
548b8ba871bSPeter Wemm 	sc = sp->script;
549b8ba871bSPeter Wemm 	if (line_len < (prompt_len = sc->sh_prompt_len))
550b8ba871bSPeter Wemm 		return (0);
551b8ba871bSPeter Wemm 
552b8ba871bSPeter Wemm 	for (pp = sc->sh_prompt;
553b8ba871bSPeter Wemm 	    prompt_len && line_len; --prompt_len, --line_len) {
554b8ba871bSPeter Wemm 		if (*pp == '\0') {
555b8ba871bSPeter Wemm 			for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
556b8ba871bSPeter Wemm 			if (!prompt_len)
557b8ba871bSPeter Wemm 				return (0);
558b8ba871bSPeter Wemm 			for (; line_len && *lp != *pp; --line_len, ++lp);
559b8ba871bSPeter Wemm 			if (!line_len)
560b8ba871bSPeter Wemm 				return (0);
561b8ba871bSPeter Wemm 		}
562b8ba871bSPeter Wemm 		if (*pp++ != *lp++)
563b8ba871bSPeter Wemm 			break;
564b8ba871bSPeter Wemm 	}
565b8ba871bSPeter Wemm 
566b8ba871bSPeter Wemm 	if (prompt_len)
567b8ba871bSPeter Wemm 		return (0);
568b8ba871bSPeter Wemm 	if (lenp != NULL)
569b8ba871bSPeter Wemm 		*lenp = line_len;
570b8ba871bSPeter Wemm 	return (1);
571b8ba871bSPeter Wemm }
572b8ba871bSPeter Wemm 
573b8ba871bSPeter Wemm /*
574b8ba871bSPeter Wemm  * sscr_end --
575b8ba871bSPeter Wemm  *	End the pipe to a shell.
576b8ba871bSPeter Wemm  *
577c271fa92SBaptiste Daroussin  * PUBLIC: int sscr_end(SCR *);
578b8ba871bSPeter Wemm  */
579b8ba871bSPeter Wemm int
sscr_end(SCR * sp)580f0957ccaSPeter Wemm sscr_end(SCR *sp)
581b8ba871bSPeter Wemm {
582b8ba871bSPeter Wemm 	SCRIPT *sc;
583b8ba871bSPeter Wemm 
584b8ba871bSPeter Wemm 	if ((sc = sp->script) == NULL)
585b8ba871bSPeter Wemm 		return (0);
586b8ba871bSPeter Wemm 
587b8ba871bSPeter Wemm 	/* Turn off the script flags. */
588b8ba871bSPeter Wemm 	F_CLR(sp, SC_SCRIPT);
589b8ba871bSPeter Wemm 	sscr_check(sp);
590b8ba871bSPeter Wemm 
591b8ba871bSPeter Wemm 	/* Close down the parent's file descriptors. */
592b8ba871bSPeter Wemm 	if (sc->sh_master != -1)
593b8ba871bSPeter Wemm 	    (void)close(sc->sh_master);
594b8ba871bSPeter Wemm 	if (sc->sh_slave != -1)
595b8ba871bSPeter Wemm 	    (void)close(sc->sh_slave);
596b8ba871bSPeter Wemm 
597b8ba871bSPeter Wemm 	/* This should have killed the child. */
598b8ba871bSPeter Wemm 	(void)proc_wait(sp, (long)sc->sh_pid, "script-shell", 0, 0);
599b8ba871bSPeter Wemm 
600b8ba871bSPeter Wemm 	/* Free memory. */
601b8ba871bSPeter Wemm 	free(sc->sh_prompt);
602b8ba871bSPeter Wemm 	free(sc);
603b8ba871bSPeter Wemm 	sp->script = NULL;
604b8ba871bSPeter Wemm 
605b8ba871bSPeter Wemm 	return (0);
606b8ba871bSPeter Wemm }
607b8ba871bSPeter Wemm 
608b8ba871bSPeter Wemm /*
609b8ba871bSPeter Wemm  * sscr_check --
610b8ba871bSPeter Wemm  *	Set/clear the global scripting bit.
611b8ba871bSPeter Wemm  */
612b8ba871bSPeter Wemm static void
sscr_check(SCR * sp)613f0957ccaSPeter Wemm sscr_check(SCR *sp)
614b8ba871bSPeter Wemm {
615b8ba871bSPeter Wemm 	GS *gp;
616b8ba871bSPeter Wemm 
617b8ba871bSPeter Wemm 	gp = sp->gp;
618f0957ccaSPeter Wemm 	TAILQ_FOREACH(sp, gp->dq, q)
619b8ba871bSPeter Wemm 		if (F_ISSET(sp, SC_SCRIPT)) {
620b8ba871bSPeter Wemm 			F_SET(gp, G_SCRWIN);
621b8ba871bSPeter Wemm 			return;
622b8ba871bSPeter Wemm 		}
623b8ba871bSPeter Wemm 	F_CLR(gp, G_SCRWIN);
624b8ba871bSPeter Wemm }
625