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