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