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