head	1.12;
access;
symbols
	perseant-exfatfs-base-20250801:1.12
	perseant-exfatfs-base-20240630:1.12
	perseant-exfatfs:1.12.0.36
	perseant-exfatfs-base:1.12
	cjep_sun2x:1.12.0.34
	cjep_sun2x-base:1.12
	cjep_staticlib_x-base1:1.12
	cjep_staticlib_x:1.12.0.32
	cjep_staticlib_x-base:1.12
	phil-wifi-20200421:1.12
	phil-wifi-20200411:1.12
	phil-wifi-20200406:1.12
	pgoyette-compat-merge-20190127:1.12
	pgoyette-compat-20190127:1.12
	pgoyette-compat-20190118:1.12
	pgoyette-compat-1226:1.12
	pgoyette-compat-1126:1.12
	pgoyette-compat-1020:1.12
	pgoyette-compat-0930:1.12
	pgoyette-compat-0906:1.12
	pgoyette-compat-0728:1.12
	pgoyette-compat-0625:1.12
	pgoyette-compat-0521:1.12
	pgoyette-compat-0502:1.12
	pgoyette-compat-0422:1.12
	pgoyette-compat-0415:1.12
	pgoyette-compat-0407:1.12
	pgoyette-compat-0330:1.12
	pgoyette-compat-0322:1.12
	pgoyette-compat-0315:1.12
	pgoyette-compat:1.12.0.30
	pgoyette-compat-base:1.12
	perseant-stdc-iso10646:1.12.0.28
	perseant-stdc-iso10646-base:1.12
	prg-localcount2-base3:1.12
	prg-localcount2-base2:1.12
	prg-localcount2-base1:1.12
	prg-localcount2:1.12.0.26
	prg-localcount2-base:1.12
	pgoyette-localcount-20170426:1.12
	bouyer-socketcan-base1:1.12
	pgoyette-localcount-20170320:1.12
	bouyer-socketcan:1.12.0.24
	bouyer-socketcan-base:1.12
	pgoyette-localcount-20170107:1.12
	pgoyette-localcount-20161104:1.12
	localcount-20160914:1.12
	pgoyette-localcount-20160806:1.12
	pgoyette-localcount-20160726:1.12
	pgoyette-localcount:1.12.0.22
	pgoyette-localcount-base:1.12
	netbsd-5-2-3-RELEASE:1.12
	netbsd-5-1-5-RELEASE:1.12
	yamt-pagecache-base9:1.12
	yamt-pagecache-tag8:1.12
	tls-earlyentropy:1.12.0.18
	tls-earlyentropy-base:1.12
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.12
	riastradh-drm2-base3:1.12
	netbsd-5-2-2-RELEASE:1.12
	netbsd-5-1-4-RELEASE:1.12
	netbsd-5-2-1-RELEASE:1.12
	netbsd-5-1-3-RELEASE:1.12
	agc-symver:1.12.0.20
	agc-symver-base:1.12
	tls-maxphys-base:1.12
	yamt-pagecache-base8:1.12
	netbsd-5-2:1.12.0.16
	yamt-pagecache-base7:1.12
	netbsd-5-2-RELEASE:1.12
	netbsd-5-2-RC1:1.12
	yamt-pagecache-base6:1.12
	yamt-pagecache-base5:1.12
	yamt-pagecache-base4:1.12
	netbsd-5-1-2-RELEASE:1.12
	netbsd-5-1-1-RELEASE:1.12
	yamt-pagecache-base3:1.12
	yamt-pagecache-base2:1.12
	yamt-pagecache:1.12.0.14
	yamt-pagecache-base:1.12
	bouyer-quota2-nbase:1.12
	bouyer-quota2:1.12.0.12
	bouyer-quota2-base:1.12
	matt-nb5-pq3:1.12.0.10
	matt-nb5-pq3-base:1.12
	netbsd-5-1:1.12.0.8
	netbsd-5-1-RELEASE:1.12
	netbsd-5-1-RC4:1.12
	netbsd-5-1-RC3:1.12
	netbsd-5-1-RC2:1.12
	netbsd-5-1-RC1:1.12
	netbsd-5-0-2-RELEASE:1.12
	netbsd-5-0-1-RELEASE:1.12
	jym-xensuspend-nbase:1.12
	netbsd-5-0:1.12.0.6
	netbsd-5-0-RELEASE:1.12
	netbsd-5-0-RC4:1.12
	netbsd-5-0-RC3:1.12
	netbsd-5-0-RC2:1.12
	jym-xensuspend:1.12.0.4
	jym-xensuspend-base:1.12
	netbsd-5-0-RC1:1.12
	netbsd-5:1.12.0.2
	netbsd-5-base:1.12
	matt-mips64-base2:1.11
	matt-mips64:1.11.0.26
	mjf-devfs2:1.11.0.24
	mjf-devfs2-base:1.11
	netbsd-4-0-1-RELEASE:1.11
	wrstuden-revivesa-base-3:1.11
	wrstuden-revivesa-base-2:1.11
	wrstuden-fixsa-newbase:1.11
	wrstuden-revivesa-base-1:1.11
	yamt-pf42-base4:1.11
	yamt-pf42-base3:1.11
	hpcarm-cleanup-nbase:1.11
	yamt-pf42-baseX:1.11
	yamt-pf42-base2:1.11
	wrstuden-revivesa:1.11.0.22
	wrstuden-revivesa-base:1.11
	yamt-pf42:1.11.0.20
	yamt-pf42-base:1.11
	keiichi-mipv6-nbase:1.11
	keiichi-mipv6:1.11.0.18
	keiichi-mipv6-base:1.11
	matt-armv6-nbase:1.11
	matt-armv6-prevmlocking:1.11
	wrstuden-fixsa-base-1:1.11
	netbsd-4-0:1.11.0.16
	netbsd-4-0-RELEASE:1.11
	cube-autoconf:1.11.0.14
	cube-autoconf-base:1.11
	netbsd-4-0-RC5:1.11
	netbsd-4-0-RC4:1.11
	netbsd-4-0-RC3:1.11
	netbsd-4-0-RC2:1.11
	netbsd-4-0-RC1:1.11
	matt-armv6:1.11.0.12
	matt-armv6-base:1.11
	matt-mips64-base:1.11
	hpcarm-cleanup:1.11.0.10
	hpcarm-cleanup-base:1.11
	netbsd-3-1-1-RELEASE:1.10
	netbsd-3-0-3-RELEASE:1.10
	wrstuden-fixsa:1.11.0.8
	wrstuden-fixsa-base:1.11
	abandoned-netbsd-4-base:1.11
	abandoned-netbsd-4:1.11.0.4
	netbsd-3-1:1.10.0.6
	netbsd-3-1-RELEASE:1.10
	netbsd-3-0-2-RELEASE:1.10
	netbsd-3-1-RC4:1.10
	netbsd-3-1-RC3:1.10
	netbsd-3-1-RC2:1.10
	netbsd-3-1-RC1:1.10
	netbsd-4:1.11.0.6
	netbsd-4-base:1.11
	chap-midi-nbase:1.11
	netbsd-3-0-1-RELEASE:1.10
	chap-midi:1.11.0.2
	chap-midi-base:1.11
	netbsd-3-0:1.10.0.4
	netbsd-3-0-RELEASE:1.10
	netbsd-3-0-RC6:1.10
	netbsd-3-0-RC5:1.10
	netbsd-3-0-RC4:1.10
	netbsd-3-0-RC3:1.10
	netbsd-3-0-RC2:1.10
	netbsd-3-0-RC1:1.10
	netbsd-2-0-3-RELEASE:1.9.4.2
	netbsd-2-1:1.9.6.1.0.2
	netbsd-2-1-RELEASE:1.9.6.1
	netbsd-2-1-RC6:1.9.6.1
	netbsd-2-1-RC5:1.9.6.1
	netbsd-2-1-RC4:1.9.6.1
	netbsd-2-1-RC3:1.9.6.1
	netbsd-2-1-RC2:1.9.6.1
	netbsd-2-1-RC1:1.9.6.1
	netbsd-2-0-2-RELEASE:1.9.4.2
	netbsd-3:1.10.0.2
	netbsd-3-base:1.10
	netbsd-2-0-1-RELEASE:1.9
	netbsd-2:1.9.0.6
	netbsd-2-base:1.9
	netbsd-2-0-RELEASE:1.9
	netbsd-2-0-RC5:1.9
	netbsd-2-0-RC4:1.9
	netbsd-2-0-RC3:1.9
	netbsd-2-0-RC2:1.9
	netbsd-2-0-RC1:1.9
	netbsd-2-0:1.9.0.4
	netbsd-2-0-base:1.9
	netbsd-1-6-PATCH002-RELEASE:1.9
	netbsd-1-6-PATCH002:1.9
	netbsd-1-6-PATCH002-RC4:1.9
	netbsd-1-6-PATCH002-RC3:1.9
	netbsd-1-6-PATCH002-RC2:1.9
	netbsd-1-6-PATCH002-RC1:1.9
	netbsd-1-6-PATCH001:1.9
	netbsd-1-6-PATCH001-RELEASE:1.9
	netbsd-1-6-PATCH001-RC3:1.9
	netbsd-1-6-PATCH001-RC2:1.9
	netbsd-1-6-PATCH001-RC1:1.9
	fvdl_fs64_base:1.9
	netbsd-1-6-RELEASE:1.9
	netbsd-1-6-RC3:1.9
	netbsd-1-6-RC2:1.9
	netbsd-1-6-RC1:1.9
	netbsd-1-6:1.9.0.2
	netbsd-1-6-base:1.9
	netbsd-1-5-PATCH003:1.7
	netbsd-1-5-PATCH002:1.7
	netbsd-1-5-PATCH001:1.7
	nvi_1_79:1.1.1.6
	netbsd-1-5-RELEASE:1.7
	netbsd-1-5-BETA2:1.7
	netbsd-1-5-BETA:1.7
	netbsd-1-4-PATCH003:1.7
	netbsd-1-5-ALPHA2:1.7
	netbsd-1-5:1.7.0.10
	netbsd-1-5-base:1.7
	minoura-xpg4dl-base:1.7
	minoura-xpg4dl:1.7.0.8
	netbsd-1-4-PATCH002:1.7
	wrstuden-devbsize-19991221:1.7
	wrstuden-devbsize:1.7.0.6
	wrstuden-devbsize-base:1.7
	comdex-fall-1999:1.7.0.4
	comdex-fall-1999-base:1.7
	netbsd-1-4-PATCH001:1.7
	netbsd-1-4-RELEASE:1.7
	netbsd-1-4:1.7.0.2
	netbsd-1-4-base:1.7
	netbsd-1-3-PATCH003:1.6
	netbsd-1-3-PATCH003-CANDIDATE2:1.6
	netbsd-1-3-PATCH003-CANDIDATE1:1.6
	netbsd-1-3-PATCH003-CANDIDATE0:1.6
	netbsd-1-3-PATCH002:1.6
	netbsd-1-3-PATCH001:1.6
	netbsd-1-3-RELEASE:1.6
	netbsd-1-3-BETA:1.6
	netbsd-1-3:1.6.0.2
	netbsd-1-3-base:1.6
	netbsd-1-2-PATCH001:1.6
	netbsd-1-2-RELEASE:1.6
	netbsd-1-2-BETA:1.6
	netbsd-1-2:1.6.0.4
	netbsd-1-2-base:1.6
	nvi_1_66:1.1.1.5
	BOSTIC:1.1.1
	netbsd-1-1-PATCH001:1.5
	netbsd-1-1-RELEASE:1.5
	netbsd-1-1:1.5.0.4
	netbsd-1-1-base:1.5
	netbsd-1-0-PATCH06:1.5
	netbsd-1-0-PATCH05:1.5
	netbsd-1-0-PATCH04:1.5
	netbsd-1-0-PATCH03:1.5
	netbsd-1-0-PATCH02:1.5
	netbsd-1-0-PATCH1:1.5
	netbsd-1-0-PATCH0:1.5
	netbsd-1-0-RELEASE:1.5
	netbsd-1-0:1.5.0.2
	nvi-1-34b:1.1.1.4
	nvi-1-33b:1.1.1.3
	netbsd-1-0-base:1.3
	nvi-1-11b:1.1.1.2
	nvi-1-03:1.1.1.1
	bostic-nvi:1.1.1;
locks; strict;
comment	@ * @;


1.12
date	2008.10.29.16.49.37;	author christos;	state dead;
branches;
next	1.11;

1.11
date	2005.06.02.04.25.16;	author lukem;	state Exp;
branches;
next	1.10;

1.10
date	2005.02.12.12.53.23;	author aymeric;	state Exp;
branches;
next	1.9;

1.9
date	2002.04.09.01.47.33;	author thorpej;	state Exp;
branches
	1.9.4.1
	1.9.6.1;
next	1.8;

1.8
date	2001.03.31.11.37.50;	author aymeric;	state Exp;
branches;
next	1.7;

1.7
date	98.01.09.08.08.01;	author perry;	state Exp;
branches;
next	1.6;

1.6
date	96.05.20.03.47.53;	author mrg;	state Exp;
branches;
next	1.5;

1.5
date	94.08.17.20.12.51;	author cgd;	state Exp;
branches
	1.5.2.1;
next	1.4;

1.4
date	94.08.17.16.36.10;	author cgd;	state Exp;
branches;
next	1.3;

1.3
date	94.03.28.04.29.38;	author cgd;	state Exp;
branches;
next	1.2;

1.2
date	94.01.24.06.40.35;	author cgd;	state Exp;
branches;
next	1.1;

1.1
date	94.01.24.05.53.04;	author cgd;	state Exp;
branches
	1.1.1.1;
next	;

1.9.4.1
date	2005.02.12.12.24.13;	author aymeric;	state Exp;
branches;
next	1.9.4.2;

1.9.4.2
date	2005.02.12.12.46.26;	author aymeric;	state Exp;
branches;
next	;

1.9.6.1
date	2005.05.06.14.42.03;	author riz;	state Exp;
branches;
next	;

1.5.2.1
date	94.08.17.20.12.51;	author cgd;	state dead;
branches;
next	1.5.2.2;

1.5.2.2
date	94.08.17.20.12.52;	author cgd;	state Exp;
branches;
next	;

1.1.1.1
date	94.01.24.05.53.05;	author cgd;	state Exp;
branches;
next	1.1.1.2;

1.1.1.2
date	94.03.28.02.55.17;	author cgd;	state Exp;
branches;
next	1.1.1.3;

1.1.1.3
date	94.08.17.16.16.53;	author cgd;	state Exp;
branches;
next	1.1.1.4;

1.1.1.4
date	94.08.17.19.18.33;	author cgd;	state Exp;
branches;
next	1.1.1.5;

1.1.1.5
date	96.05.20.01.56.21;	author mrg;	state Exp;
branches;
next	1.1.1.6;

1.1.1.6
date	2001.03.31.11.29.46;	author aymeric;	state Exp;
branches;
next	;


desc
@@


1.12
log
@bye old vi!
@
text
@/*	$NetBSD: ex_script.c,v 1.11 2005/06/02 04:25:16 lukem Exp $	*/

/*-
 * Copyright (c) 1992, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1992, 1993, 1994, 1995, 1996
 *	Keith Bostic.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Brian Hirt.
 *
 * See the LICENSE file for redistribution information.
 */

#include "config.h"

#include <sys/cdefs.h>
#ifndef lint
#if 0
static const char sccsid[] = "@@(#)ex_script.c	10.30 (Berkeley) 9/24/96";
#else
__RCSID("$NetBSD: ex_script.c,v 1.11 2005/06/02 04:25:16 lukem Exp $");
#endif
#endif /* not lint */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_SYS5_PTY
#include <sys/stropts.h>
#endif
#include <sys/time.h>
#include <sys/wait.h>

#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>		/* XXX: OSF/1 bug: include before <grp.h> */
#include <grp.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#include "../common/common.h"
#include "../vi/vi.h"
#include "script.h"
#include "pathnames.h"

static void	sscr_check __P((SCR *));
static int	sscr_getprompt __P((SCR *));
static int	sscr_init __P((SCR *));
static int	sscr_insert __P((SCR *));
static int	sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
static int	sscr_pty __P((int *, int *, char *, struct termios *, void *));
static int	sscr_setprompt __P((SCR *, char *, size_t));

/*
 * ex_script -- : sc[ript][!] [file]
 *	Switch to script mode.
 *
 * PUBLIC: int ex_script __P((SCR *, EXCMD *));
 */
int
ex_script(sp, cmdp)
	SCR *sp;
	EXCMD *cmdp;
{
	/* Vi only command. */
	if (!F_ISSET(sp, SC_VI)) {
		msgq(sp, M_ERR,
		    "150|The script command is only available in vi mode");
		return (1);
	}

	/* Switch to the new file. */
	if (cmdp->argc != 0 && ex_edit(sp, cmdp))
		return (1);

	/* Create the shell, figure out the prompt. */
	if (sscr_init(sp))
		return (1);

	return (0);
}

/*
 * sscr_init --
 *	Create a pty setup for a shell.
 */
static int
sscr_init(sp)
	SCR *sp;
{
	SCRIPT *sc;
	char *sh, *sh_path;

	/* We're going to need a shell. */
	if (opts_empty(sp, O_SHELL, 0))
		return (1);

	MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT));
	sp->script = sc;
	sc->sh_prompt = NULL;
	sc->sh_prompt_len = 0;

	/*
	 * There are two different processes running through this code.
	 * They are the shell and the parent.
	 */
	sc->sh_master = sc->sh_slave = -1;

	if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}

	/*
	 * Turn off output postprocessing and echo.
	 */
	sc->sh_term.c_oflag &= ~OPOST;
	sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);

#ifdef TIOCGWINSZ
	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}

	if (sscr_pty(&sc->sh_master,
	    &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
		msgq(sp, M_SYSERR, "pty");
		goto err;
	}
#else
	if (sscr_pty(&sc->sh_master,
	    &sc->sh_slave, sc->sh_name, &sc->sh_term, NULL) == -1) {
		msgq(sp, M_SYSERR, "pty");
		goto err;
	}
#endif

	/*
	 * __TK__ huh?
	 * Don't use vfork() here, because the signal semantics differ from
	 * implementation to implementation.
	 */
	switch (sc->sh_pid = fork()) {
	case -1:			/* Error. */
		msgq(sp, M_SYSERR, "fork");
err:		if (sc->sh_master != -1)
			(void)close(sc->sh_master);
		if (sc->sh_slave != -1)
			(void)close(sc->sh_slave);
		return (1);
	case 0:				/* Utility. */
		/*
		 * XXX
		 * So that shells that do command line editing turn it off.
		 */
		(void)setenv("TERM", "emacs", 1);
		(void)setenv("TERMCAP", "emacs:", 1);
		(void)setenv("EMACS", "t", 1);

		(void)setsid();
#ifdef TIOCSCTTY
		/*
		 * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
		 * ioctl, not by opening a terminal device file.  POSIX 1003.1
		 * doesn't define a portable way to do this.  If TIOCSCTTY is
		 * not available, hope that the open does it.
		 */
		(void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
#endif
		(void)close(sc->sh_master);
		(void)dup2(sc->sh_slave, STDIN_FILENO);
		(void)dup2(sc->sh_slave, STDOUT_FILENO);
		(void)dup2(sc->sh_slave, STDERR_FILENO);
		(void)close(sc->sh_slave);

		/* Assumes that all shells have -i. */
		sh_path = O_STR(sp, O_SHELL);
		if ((sh = strrchr(sh_path, '/')) == NULL)
			sh = sh_path;
		else
			++sh;
		execl(sh_path, sh, "-i", NULL);
		msgq_str(sp, M_SYSERR, sh_path, "execl: %s");
		_exit(127);
	default:			/* Parent. */
		break;
	}

	if (sscr_getprompt(sp))
		return (1);

	F_SET(sp, SC_SCRIPT);
	F_SET(sp->gp, G_SCRWIN);
	return (0);
}

/*
 * sscr_getprompt --
 *	Eat lines printed by the shell until a line with no trailing
 *	carriage return comes; set the prompt from that line.
 */
static int
sscr_getprompt(sp)
	SCR *sp;
{
	struct timeval tv;
	CHAR_T *endp, *p, *t, buf[1024];
	SCRIPT *sc;
	fd_set fdset;
	recno_t lline;
	size_t llen, len;
	u_int value;
	int nr;

	FD_ZERO(&fdset);
	endp = buf;
	len = sizeof(buf);

	/* Wait up to a second for characters to read. */
	tv.tv_sec = 5;
	tv.tv_usec = 0;
	sc = sp->script;
	FD_SET(sc->sh_master, &fdset);
	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "select");
		goto prompterr;
	case  0:		/* Timeout */
		msgq(sp, M_ERR, "Error: timed out");
		goto prompterr;
	case  1:		/* Characters to read. */
		break;
	}

	/* Read the characters. */
more:	len = sizeof(buf) - (endp - buf);
	switch (nr = read(sc->sh_master, endp, len)) {
	case  0:			/* EOF. */
		msgq(sp, M_ERR, "Error: shell: EOF");
		goto prompterr;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto prompterr;
	default:
		endp += nr;
		break;
	}

	/* If any complete lines, push them into the file. */
	for (p = t = buf; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			if (db_last(sp, &lline) ||
			    db_append(sp, 0, lline, t, p - t))
				goto prompterr;
			t = p + 1;
		}
	}
	if (p > buf) {
		memmove(buf, t, endp - t);
		endp = buf + (endp - t);
	}
	if (endp == buf)
		goto more;

	/* Wait up 1/10 of a second to make sure that we got it all. */
	tv.tv_sec = 0;
	tv.tv_usec = 100000;
	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "select");
		goto prompterr;
	case  0:		/* Timeout */
		break;
	case  1:		/* Characters to read. */
		goto more;
	}

	/* Timed out, so theoretically we have a prompt. */
	llen = endp - buf;
	endp = buf;

	/* Append the line into the file. */
	if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) {
prompterr:	sscr_end(sp);
		return (1);
	}

	return (sscr_setprompt(sp, buf, llen));
}

/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec __P((SCR *, recno_t));
 */
int
sscr_exec(sp, lno)
	SCR *sp;
	recno_t lno;
{
	SCRIPT *sc;
	recno_t last_lno;
	size_t blen, len, last_len, tlen;
	int isempty, matchprompt, nw, rval;
	char *bp, *p;

	bp = NULL;	/* XXXGCC -Wuninitialized */
	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
		return (1);
	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
		matchprompt = 1;
		GET_SPACE_RET(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (len == 0)
		goto empty;

	/* Delete any prompt. */
	if (sscr_matchprompt(sp, p, len, &tlen)) {
		if (tlen == len) {
empty:			msgq(sp, M_BERR, "151|No command to execute");
			goto err1;
		}
		p += (len - tlen);
		len = tlen;
	}

	/* Push the line to the shell. */
	sc = sp->script;
	if ((nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_RET(sp, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		if (db_set(sp, last_lno, bp, last_len + len))
err1:			rval = 1;
	}
	if (matchprompt)
		FREE_SPACE(sp, bp, blen);
	return (rval);
}

/*
 * sscr_input --
 *	Read any waiting shell input.
 *
 * PUBLIC: int sscr_input __P((SCR *));
 */
int
sscr_input(sp)
	SCR *sp;
{
	GS *gp;
	struct timeval poll;
	fd_set rdfd;
	int maxfd;

	gp = sp->gp;

loop:	maxfd = 0;
	FD_ZERO(&rdfd);
	poll.tv_sec = 0;
	poll.tv_usec = 0;

	/* Set up the input mask. */
	for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
		if (F_ISSET(sp, SC_SCRIPT)) {
			FD_SET(sp->script->sh_master, &rdfd);
			if (sp->script->sh_master > maxfd)
				maxfd = sp->script->sh_master;
		}

	/* Check for input. */
	switch (select(maxfd + 1, &rdfd, NULL, NULL, &poll)) {
	case -1:
		msgq(sp, M_SYSERR, "select");
		return (1);
	case 0:
		return (0);
	default:
		break;
	}

	/* Read the input. */
	for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
		if (F_ISSET(sp, SC_SCRIPT) &&
		    FD_ISSET(sp->script->sh_master, &rdfd) && sscr_insert(sp))
			return (1);
	goto loop;
}

/*
 * sscr_insert --
 *	Take a line from the shell and insert it into the file.
 */
static int
sscr_insert(sp)
	SCR *sp;
{
	struct timeval tv;
	CHAR_T *endp, *p, *t;
	SCRIPT *sc;
	fd_set rdfd;
	recno_t lno;
	size_t blen, len, tlen;
	u_int value;
	int nr, rval;
	char *bp;

	len = 0;	/* XXXGCC -Wuninitialized */
	/* Find out where the end of the file is. */
	if (db_last(sp, &lno))
		return (1);

#define	MINREAD	1024
	GET_SPACE_RET(sp, bp, blen, MINREAD);
	endp = bp;

	/* Read the characters. */
	rval = 1;
	sc = sp->script;
more:	switch (nr = read(sc->sh_master, endp, MINREAD)) {
	case  0:			/* EOF; shell just exited. */
		sscr_end(sp);
		rval = 0;
		goto ret;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto ret;
	default:
		endp += nr;
		break;
	}

	/* Append the lines into the file. */
	for (p = t = bp; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			len = p - t;
			if (db_append(sp, 1, lno++, t, len))
				goto ret;
			t = p + 1;
		}
	}
	if (p > t) {
		len = p - t;
		/*
		 * If the last thing from the shell isn't another prompt, wait
		 * up to 1/10 of a second for more stuff to show up, so that
		 * we don't break the output into two separate lines.  Don't
		 * want to hang indefinitely because some program is hanging,
		 * confused the shell, or whatever.
		 */
		if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
			tv.tv_sec = 0;
			tv.tv_usec = 100000;
			FD_ZERO(&rdfd);
			FD_SET(sc->sh_master, &rdfd);
			if (select(sc->sh_master + 1,
			    &rdfd, NULL, NULL, &tv) == 1) {
				memmove(bp, t, len);
				endp = bp + len;
				goto more;
			}
		}
		if (sscr_setprompt(sp, t, len))
			return (1);
		if (db_append(sp, 1, lno++, t, len))
			goto ret;
	}

	/* The cursor moves to EOF. */
	sp->lno = lno;
	sp->cno = len ? len - 1 : 0;
	rval = vs_refresh(sp, 1);

ret:	FREE_SPACE(sp, bp, blen);
	return (rval);
}

/*
 * sscr_setprompt --
 *
 * Set the prompt to the last line we got from the shell.
 *
 */
static int
sscr_setprompt(sp, buf, len)
	SCR *sp;
	char *buf;
	size_t len;
{
	SCRIPT *sc;

	sc = sp->script;
	if (sc->sh_prompt)
		free(sc->sh_prompt);
	MALLOC(sp, sc->sh_prompt, char *, len + 1);
	if (sc->sh_prompt == NULL) {
		sscr_end(sp);
		return (1);
	}
	memmove(sc->sh_prompt, buf, len);
	sc->sh_prompt_len = len;
	sc->sh_prompt[len] = '\0';
	return (0);
}

/*
 * sscr_matchprompt --
 *	Check to see if a line matches the prompt.  Nul's indicate
 *	parts that can change, in both content and size.
 */
static int
sscr_matchprompt(sp, lp, line_len, lenp)
	SCR *sp;
	char *lp;
	size_t line_len, *lenp;
{
	SCRIPT *sc;
	size_t prompt_len;
	char *pp;

	sc = sp->script;
	if (line_len < (prompt_len = sc->sh_prompt_len))
		return (0);

	for (pp = sc->sh_prompt;
	    prompt_len && line_len; --prompt_len, --line_len) {
		if (*pp == '\0') {
			for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
			if (!prompt_len)
				return (0);
			for (; line_len && *lp != *pp; --line_len, ++lp);
			if (!line_len)
				return (0);
		}
		if (*pp++ != *lp++)
			break;
	}

	if (prompt_len)
		return (0);
	if (lenp != NULL)
		*lenp = line_len;
	return (1);
}

/*
 * sscr_end --
 *	End the pipe to a shell.
 *
 * PUBLIC: int sscr_end __P((SCR *));
 */
int
sscr_end(sp)
	SCR *sp;
{
	SCRIPT *sc;

	if ((sc = sp->script) == NULL)
		return (0);

	/* Turn off the script flags. */
	F_CLR(sp, SC_SCRIPT);
	sscr_check(sp);

	/* Close down the parent's file descriptors. */
	if (sc->sh_master != -1)
	    (void)close(sc->sh_master);
	if (sc->sh_slave != -1)
	    (void)close(sc->sh_slave);

	/* This should have killed the child. */
	(void)proc_wait(sp, (long)sc->sh_pid, "script-shell", 0, 0);

	/* Free memory. */
	free(sc->sh_prompt);
	free(sc);
	sp->script = NULL;

	return (0);
}

/*
 * sscr_check --
 *	Set/clear the global scripting bit.
 */
static void
sscr_check(sp)
	SCR *sp;
{
	GS *gp;

	gp = sp->gp;
	for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
		if (F_ISSET(sp, SC_SCRIPT)) {
			F_SET(gp, G_SCRWIN);
			return;
		}
	F_CLR(gp, G_SCRWIN);
}

#ifdef HAVE_SYS5_PTY
static int ptys_open __P((int, char *));
static int ptym_open __P((char *));

static int
sscr_pty(amaster, aslave, name, termp, winp)
	int *amaster, *aslave;
	char *name;
	struct termios *termp;
	void *winp;
{
	int master, slave, ttygid;

	/* open master terminal */
	if ((master = ptym_open(name)) < 0)  {
		errno = ENOENT;	/* out of ptys */
		return (-1);
	}

	/* open slave terminal */
	if ((slave = ptys_open(master, name)) >= 0) {
		*amaster = master;
		*aslave = slave;
	} else {
		errno = ENOENT;	/* out of ptys */
		return (-1);
	}

	if (termp)
		(void) tcsetattr(slave, TCSAFLUSH, termp);
#ifdef TIOCSWINSZ
	if (winp != NULL)
		(void) ioctl(slave, TIOCSWINSZ, (struct winsize *)winp);
#endif
	return (0);
}

/*
 * ptym_open --
 *	This function opens a master pty and returns the file descriptor
 *	to it.  pts_name is also returned which is the name of the slave.
 */
static int
ptym_open(pts_name)
	char *pts_name;
{
	int fdm;
	char *ptr, *ptsname();

	strcpy(pts_name, _PATH_SYSV_PTY);
	if ((fdm = open(pts_name, O_RDWR)) < 0 )
		return (-1);

	if (grantpt(fdm) < 0) {
		close(fdm);
		return (-2);
	}

	if (unlockpt(fdm) < 0) {
		close(fdm);
		return (-3);
	}

	if (unlockpt(fdm) < 0) {
		close(fdm);
		return (-3);
	}

	/* get slave's name */
	if ((ptr = ptsname(fdm)) == NULL) {
		close(fdm);
		return (-3);
	}
	strcpy(pts_name, ptr);
	return (fdm);
}

/*
 * ptys_open --
 *	This function opens the slave pty.
 */
static int
ptys_open(fdm, pts_name)
	int fdm;
	char *pts_name;
{
	int fds;

	if ((fds = open(pts_name, O_RDWR)) < 0) {
		close(fdm);
		return (-5);
	}

	if (ioctl(fds, I_PUSH, "ptem") < 0) {
		close(fds);
		close(fdm);
		return (-6);
	}

	if (ioctl(fds, I_PUSH, "ldterm") < 0) {
		close(fds);
		close(fdm);
		return (-7);
	}

	if (ioctl(fds, I_PUSH, "ttcompat") < 0) {
		close(fds);
		close(fdm);
		return (-8);
	}

	return (fds);
}

#else /* !HAVE_SYS5_PTY */

static int
sscr_pty(amaster, aslave, name, termp, winp)
	int *amaster, *aslave;
	char *name;
	struct termios *termp;
	void *winp;
{
	static char line[] = "/dev/ptyXX";
	register char *cp1, *cp2;
	register int master, slave, ttygid;
	struct group *gr;

	if ((gr = getgrnam("tty")) != NULL)
		ttygid = gr->gr_gid;
	else
		ttygid = -1;

	for (cp1 = "pqrs"; *cp1; cp1++) {
		line[8] = *cp1;
		for (cp2 = "0123456789abcdef"; *cp2; cp2++) {
			line[5] = 'p';
			line[9] = *cp2;
			if ((master = open(line, O_RDWR, 0)) == -1) {
				if (errno == ENOENT)
					return (-1);	/* out of ptys */
			} else {
				line[5] = 't';
				(void) chown(line, getuid(), ttygid);
				(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
#ifdef HAVE_REVOKE
				(void) revoke(line);
#endif
				if ((slave = open(line, O_RDWR, 0)) != -1) {
					*amaster = master;
					*aslave = slave;
					if (name)
						strcpy(name, line);
					if (termp)
						(void) tcsetattr(slave, 
							TCSAFLUSH, termp);
#ifdef TIOCSWINSZ
					if (winp)
						(void) ioctl(slave, TIOCSWINSZ, 
							(char *)winp);
#endif
					return (0);
				}
				(void) close(master);
			}
		}
	}
	errno = ENOENT;	/* out of ptys */
	return (-1);
}
#endif /* HAVE_SYS5_PTY */
@


1.11
log
@appease gcc -Wuninitialized.
Each is marked with XXXGCC, because in some cases it looks like gcc
isn't correctly detecting that
	for ( ; xxx ; yyy)
		foo = 0;
always sets foo ...
@
text
@d1 1
a1 1
/*	$NetBSD: ex_script.c,v 1.10 2005/02/12 12:53:23 aymeric Exp $	*/
d22 1
a22 1
__RCSID("$NetBSD: ex_script.c,v 1.10 2005/02/12 12:53:23 aymeric Exp $");
@


1.10
log
@Fix the RCSID's to be $NetBSD$ instead of $NetBSD
@
text
@d1 1
a1 1
/*	$NetBSD: ex_script.c,v 1.9 2002/04/09 01:47:33 thorpej Exp $	*/
d22 1
a22 1
__RCSID("$NetBSD$");
d319 1
d443 1
@


1.9
log
@Use __RCSID() and __COPYRIGHT().
@
text
@d1 1
a1 1
/*	$NetBSD: ex_script.c,v 1.8 2001/03/31 11:37:50 aymeric Exp $	*/
d22 1
a22 1
__RCSID("$NetBSD");
@


1.9.6.1
log
@Pull up revision 1.10 (requested by aymeric in ticket #1195):
Fix the RCSID's to be $NetBSD$ instead of $NetBSD
@
text
@d1 1
a1 1
/*	$NetBSD: ex_script.c,v 1.9 2002/04/09 01:47:33 thorpej Exp $	*/
d22 1
a22 1
__RCSID("$NetBSD$");
@


1.9.4.1
log
@Fix the RCSID's to be $NetBSD$ instead of $NetBSD
@
text
@d1 1
a1 1
/*	$NetBSD: ex_script.c,v 1.9 2002/04/09 01:47:33 thorpej Exp $	*/
d22 1
a22 1
__RCSID("$NetBSD$");
@


1.9.4.2
log
@Backout previous. Sorry.
@
text
@d22 1
a22 1
__RCSID("$NetBSD");
@


1.8
log
@merge changes after import of nvi 1.79
@
text
@d1 1
a1 1
/*	$NetBSD: ex_script.c,v 1.7 1998/01/09 08:08:01 perry Exp $	*/
d17 1
d19 1
d21 3
@


1.7
log
@RCS Id Police.
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
d18 1
a18 1
static const char sccsid[] = "@@(#)ex_script.c	10.29 (Berkeley) 5/12/96";
d198 1
a198 1
	F_SET(sp->gp, G_SCRIPT);
d624 1
a624 1
			F_SET(gp, G_SCRIPT);
d627 1
a627 1
	F_CLR(gp, G_SCRIPT);
@


1.6
log
@merge in nvi 1.66
@
text
@d1 2
@


1.5
log
@clean up import.
@
text
@d4 2
d7 2
a8 15
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
d10 1
a10 11
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
d13 2
d16 1
a16 1
static char sccsid[] = "@@(#)ex_script.c	8.19 (Berkeley) 8/17/94";
d22 7
d34 3
a37 2
#include <signal.h>
#include <stdio.h>
d43 2
a44 6
#include "compat.h"
#include <db.h>
#include <regex.h>

#include "vi.h"
#include "excmd.h"
d46 1
d48 7
a54 9
/*
 * XXX
 */
int openpty __P((int *, int *, char *, struct termios *, struct winsize *));

static int sscr_getprompt __P((SCR *, EXF *));
static int sscr_init __P((SCR *, EXF *));
static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
static int sscr_setprompt __P((SCR *, char *, size_t));
d58 1
d60 1
a60 1
 *	Switch to script mode.
d63 1
a63 1
ex_script(sp, ep, cmdp)
d65 1
a65 2
	EXF *ep;
	EXCMDARG *cmdp;
d68 1
a68 1
	if (!IN_VI_MODE(sp)) {
d70 1
a70 1
		    "The script command is only available in vi mode");
d75 1
a75 1
	if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp))
d78 2
a79 7
	/*
	 * Create the shell, figure out the prompt.
	 *
	 * !!!
	 * The files just switched, use sp->ep.
	 */
	if (sscr_init(sp, sp->ep))
d90 1
a90 1
sscr_init(sp, ep)
a91 1
	EXF *ep;
d96 4
d122 1
d128 1
a128 1
	if (openpty(&sc->sh_master,
d130 1
a130 1
		msgq(sp, M_SYSERR, "openpty");
d133 7
d142 1
a145 1
	SIGBLOCK(sp->gp);
a147 2
		SIGUNBLOCK(sp->gp);

a154 3
		/* The utility has default signal behavior. */
		sig_end();

d159 3
a161 3
		(void)putenv("TERM=emacs");
		(void)putenv("TERMCAP=emacs:");
		(void)putenv("EMACS=t");
d186 1
a186 2
		msgq(sp, M_ERR,
		    "Error: execl: %s: %s", sh_path, strerror(errno));
a188 1
		SIGUNBLOCK(sp->gp);
d192 1
a192 1
	if (sscr_getprompt(sp, ep))
d195 2
a196 1
	F_SET(sp, S_REDRAW | S_SCRIPT);
a197 1

d206 1
a206 1
sscr_getprompt(sp, ep)
a207 1
	EXF *ep;
d256 2
a257 2
			if (file_lline(sp, ep, &lline) ||
			    file_aline(sp, ep, 0, lline, t, p - t))
d287 1
a287 2
	if (file_lline(sp, ep, &lline) ||
	    file_aline(sp, ep, 0, lline, buf, llen)) {
d298 2
d302 1
a302 1
sscr_exec(sp, ep, lno)
a303 1
	EXF *ep;
d309 1
a309 1
	int matchprompt, nw, rval;
d313 1
a313 1
	if (file_lline(sp, ep, &last_lno))
d315 1
a315 2
	if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) {
		GETLINE_ERR(sp, last_lno);
a316 1
	}
d325 2
a326 4
	if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
		if (file_lline(sp, ep, &lno))
			goto err1;
		if (lno == 0)
a327 2
		else
			GETLINE_ERR(sp, lno);
d338 1
a338 1
empty:			msgq(sp, M_BERR, "Nothing to execute");
d360 1
a360 1
		if (file_sline(sp, ep, last_lno, bp, last_len + len))
d370 3
a372 1
 *	Take a line from the shell and insert it into the file.
d378 47
a426 1
	EXF *ep;
d428 1
d436 1
a436 2
	ep = sp->ep;
	if (file_lline(sp, ep, &lno))
a448 1
		F_CLR(sp, S_SCRIPT);
d464 1
a464 1
			if (file_aline(sp, ep, 1, lno++, t, len))
d481 2
a482 2
			FD_SET(sc->sh_master, &sp->rdfd);
			FD_CLR(STDIN_FILENO, &sp->rdfd);
d484 1
a484 1
			    &sp->rdfd, NULL, NULL, &tv) == 1) {
d492 1
a492 1
		if (file_aline(sp, ep, 1, lno++, t, len))
d499 1
a499 1
	rval = sp->s_refresh(sp, ep);
d514 1
a514 1
	char* buf;
d521 1
a521 1
		FREE(sc->sh_prompt, sc->sh_prompt_len);
d576 2
a583 1
	int rval;
d588 3
a590 2
	/* Turn off the script flag. */
	F_CLR(sp, S_SCRIPT);
d599 1
a599 1
	rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0);
d602 2
a603 2
	FREE(sc->sh_prompt, sc->sh_prompt_len);
	FREE(sc, sizeof(SCRIPT));
d606 191
a796 1
	return (rval);
d798 1
@


1.5.2.1
log
@file ex_script.c was added on branch netbsd-1-0 on 1994-08-17 20:12:52 +0000
@
text
@d1 582
@


1.5.2.2
log
@clean up import.
@
text
@a0 582
/*-
 * Copyright (c) 1992, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static char sccsid[] = "@@(#)ex_script.c	8.19 (Berkeley) 8/17/94";
#endif /* not lint */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#include "compat.h"
#include <db.h>
#include <regex.h>

#include "vi.h"
#include "excmd.h"
#include "script.h"

/*
 * XXX
 */
int openpty __P((int *, int *, char *, struct termios *, struct winsize *));

static int sscr_getprompt __P((SCR *, EXF *));
static int sscr_init __P((SCR *, EXF *));
static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
static int sscr_setprompt __P((SCR *, char *, size_t));

/*
 * ex_script -- : sc[ript][!] [file]
 *
 *	Switch to script mode.
 */
int
ex_script(sp, ep, cmdp)
	SCR *sp;
	EXF *ep;
	EXCMDARG *cmdp;
{
	/* Vi only command. */
	if (!IN_VI_MODE(sp)) {
		msgq(sp, M_ERR,
		    "The script command is only available in vi mode");
		return (1);
	}

	/* Switch to the new file. */
	if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp))
		return (1);

	/*
	 * Create the shell, figure out the prompt.
	 *
	 * !!!
	 * The files just switched, use sp->ep.
	 */
	if (sscr_init(sp, sp->ep))
		return (1);

	return (0);
}

/*
 * sscr_init --
 *	Create a pty setup for a shell.
 */
static int
sscr_init(sp, ep)
	SCR *sp;
	EXF *ep;
{
	SCRIPT *sc;
	char *sh, *sh_path;

	MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT));
	sp->script = sc;
	sc->sh_prompt = NULL;
	sc->sh_prompt_len = 0;

	/*
	 * There are two different processes running through this code.
	 * They are the shell and the parent.
	 */
	sc->sh_master = sc->sh_slave = -1;

	if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}

	/*
	 * Turn off output postprocessing and echo.
	 */
	sc->sh_term.c_oflag &= ~OPOST;
	sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);

	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}

	if (openpty(&sc->sh_master,
	    &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
		msgq(sp, M_SYSERR, "openpty");
		goto err;
	}

	/*
	 * Don't use vfork() here, because the signal semantics differ from
	 * implementation to implementation.
	 */
	SIGBLOCK(sp->gp);
	switch (sc->sh_pid = fork()) {
	case -1:			/* Error. */
		SIGUNBLOCK(sp->gp);

		msgq(sp, M_SYSERR, "fork");
err:		if (sc->sh_master != -1)
			(void)close(sc->sh_master);
		if (sc->sh_slave != -1)
			(void)close(sc->sh_slave);
		return (1);
	case 0:				/* Utility. */
		/* The utility has default signal behavior. */
		sig_end();

		/*
		 * XXX
		 * So that shells that do command line editing turn it off.
		 */
		(void)putenv("TERM=emacs");
		(void)putenv("TERMCAP=emacs:");
		(void)putenv("EMACS=t");

		(void)setsid();
#ifdef TIOCSCTTY
		/*
		 * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
		 * ioctl, not by opening a terminal device file.  POSIX 1003.1
		 * doesn't define a portable way to do this.  If TIOCSCTTY is
		 * not available, hope that the open does it.
		 */
		(void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
#endif
		(void)close(sc->sh_master);
		(void)dup2(sc->sh_slave, STDIN_FILENO);
		(void)dup2(sc->sh_slave, STDOUT_FILENO);
		(void)dup2(sc->sh_slave, STDERR_FILENO);
		(void)close(sc->sh_slave);

		/* Assumes that all shells have -i. */
		sh_path = O_STR(sp, O_SHELL);
		if ((sh = strrchr(sh_path, '/')) == NULL)
			sh = sh_path;
		else
			++sh;
		execl(sh_path, sh, "-i", NULL);
		msgq(sp, M_ERR,
		    "Error: execl: %s: %s", sh_path, strerror(errno));
		_exit(127);
	default:			/* Parent. */
		SIGUNBLOCK(sp->gp);
		break;
	}

	if (sscr_getprompt(sp, ep))
		return (1);

	F_SET(sp, S_REDRAW | S_SCRIPT);
	return (0);

}

/*
 * sscr_getprompt --
 *	Eat lines printed by the shell until a line with no trailing
 *	carriage return comes; set the prompt from that line.
 */
static int
sscr_getprompt(sp, ep)
	SCR *sp;
	EXF *ep;
{
	struct timeval tv;
	CHAR_T *endp, *p, *t, buf[1024];
	SCRIPT *sc;
	fd_set fdset;
	recno_t lline;
	size_t llen, len;
	u_int value;
	int nr;

	FD_ZERO(&fdset);
	endp = buf;
	len = sizeof(buf);

	/* Wait up to a second for characters to read. */
	tv.tv_sec = 5;
	tv.tv_usec = 0;
	sc = sp->script;
	FD_SET(sc->sh_master, &fdset);
	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "select");
		goto prompterr;
	case  0:		/* Timeout */
		msgq(sp, M_ERR, "Error: timed out");
		goto prompterr;
	case  1:		/* Characters to read. */
		break;
	}

	/* Read the characters. */
more:	len = sizeof(buf) - (endp - buf);
	switch (nr = read(sc->sh_master, endp, len)) {
	case  0:			/* EOF. */
		msgq(sp, M_ERR, "Error: shell: EOF");
		goto prompterr;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto prompterr;
	default:
		endp += nr;
		break;
	}

	/* If any complete lines, push them into the file. */
	for (p = t = buf; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			if (file_lline(sp, ep, &lline) ||
			    file_aline(sp, ep, 0, lline, t, p - t))
				goto prompterr;
			t = p + 1;
		}
	}
	if (p > buf) {
		memmove(buf, t, endp - t);
		endp = buf + (endp - t);
	}
	if (endp == buf)
		goto more;

	/* Wait up 1/10 of a second to make sure that we got it all. */
	tv.tv_sec = 0;
	tv.tv_usec = 100000;
	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "select");
		goto prompterr;
	case  0:		/* Timeout */
		break;
	case  1:		/* Characters to read. */
		goto more;
	}

	/* Timed out, so theoretically we have a prompt. */
	llen = endp - buf;
	endp = buf;

	/* Append the line into the file. */
	if (file_lline(sp, ep, &lline) ||
	    file_aline(sp, ep, 0, lline, buf, llen)) {
prompterr:	sscr_end(sp);
		return (1);
	}

	return (sscr_setprompt(sp, buf, llen));
}

/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 */
int
sscr_exec(sp, ep, lno)
	SCR *sp;
	EXF *ep;
	recno_t lno;
{
	SCRIPT *sc;
	recno_t last_lno;
	size_t blen, len, last_len, tlen;
	int matchprompt, nw, rval;
	char *bp, *p;

	/* If there's a prompt on the last line, append the command. */
	if (file_lline(sp, ep, &last_lno))
		return (1);
	if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) {
		GETLINE_ERR(sp, last_lno);
		return (1);
	}
	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
		matchprompt = 1;
		GET_SPACE_RET(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
		if (file_lline(sp, ep, &lno))
			goto err1;
		if (lno == 0)
			goto empty;
		else
			GETLINE_ERR(sp, lno);
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (len == 0)
		goto empty;

	/* Delete any prompt. */
	if (sscr_matchprompt(sp, p, len, &tlen)) {
		if (tlen == len) {
empty:			msgq(sp, M_BERR, "Nothing to execute");
			goto err1;
		}
		p += (len - tlen);
		len = tlen;
	}

	/* Push the line to the shell. */
	sc = sp->script;
	if ((nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_RET(sp, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		if (file_sline(sp, ep, last_lno, bp, last_len + len))
err1:			rval = 1;
	}
	if (matchprompt)
		FREE_SPACE(sp, bp, blen);
	return (rval);
}

/*
 * sscr_input --
 *	Take a line from the shell and insert it into the file.
 */
int
sscr_input(sp)
	SCR *sp;
{
	struct timeval tv;
	CHAR_T *endp, *p, *t;
	EXF *ep;
	SCRIPT *sc;
	recno_t lno;
	size_t blen, len, tlen;
	u_int value;
	int nr, rval;
	char *bp;

	/* Find out where the end of the file is. */
	ep = sp->ep;
	if (file_lline(sp, ep, &lno))
		return (1);

#define	MINREAD	1024
	GET_SPACE_RET(sp, bp, blen, MINREAD);
	endp = bp;

	/* Read the characters. */
	rval = 1;
	sc = sp->script;
more:	switch (nr = read(sc->sh_master, endp, MINREAD)) {
	case  0:			/* EOF; shell just exited. */
		sscr_end(sp);
		F_CLR(sp, S_SCRIPT);
		rval = 0;
		goto ret;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto ret;
	default:
		endp += nr;
		break;
	}

	/* Append the lines into the file. */
	for (p = t = bp; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			len = p - t;
			if (file_aline(sp, ep, 1, lno++, t, len))
				goto ret;
			t = p + 1;
		}
	}
	if (p > t) {
		len = p - t;
		/*
		 * If the last thing from the shell isn't another prompt, wait
		 * up to 1/10 of a second for more stuff to show up, so that
		 * we don't break the output into two separate lines.  Don't
		 * want to hang indefinitely because some program is hanging,
		 * confused the shell, or whatever.
		 */
		if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
			tv.tv_sec = 0;
			tv.tv_usec = 100000;
			FD_SET(sc->sh_master, &sp->rdfd);
			FD_CLR(STDIN_FILENO, &sp->rdfd);
			if (select(sc->sh_master + 1,
			    &sp->rdfd, NULL, NULL, &tv) == 1) {
				memmove(bp, t, len);
				endp = bp + len;
				goto more;
			}
		}
		if (sscr_setprompt(sp, t, len))
			return (1);
		if (file_aline(sp, ep, 1, lno++, t, len))
			goto ret;
	}

	/* The cursor moves to EOF. */
	sp->lno = lno;
	sp->cno = len ? len - 1 : 0;
	rval = sp->s_refresh(sp, ep);

ret:	FREE_SPACE(sp, bp, blen);
	return (rval);
}

/*
 * sscr_setprompt --
 *
 * Set the prompt to the last line we got from the shell.
 *
 */
static int
sscr_setprompt(sp, buf, len)
	SCR *sp;
	char* buf;
	size_t len;
{
	SCRIPT *sc;

	sc = sp->script;
	if (sc->sh_prompt)
		FREE(sc->sh_prompt, sc->sh_prompt_len);
	MALLOC(sp, sc->sh_prompt, char *, len + 1);
	if (sc->sh_prompt == NULL) {
		sscr_end(sp);
		return (1);
	}
	memmove(sc->sh_prompt, buf, len);
	sc->sh_prompt_len = len;
	sc->sh_prompt[len] = '\0';
	return (0);
}

/*
 * sscr_matchprompt --
 *	Check to see if a line matches the prompt.  Nul's indicate
 *	parts that can change, in both content and size.
 */
static int
sscr_matchprompt(sp, lp, line_len, lenp)
	SCR *sp;
	char *lp;
	size_t line_len, *lenp;
{
	SCRIPT *sc;
	size_t prompt_len;
	char *pp;

	sc = sp->script;
	if (line_len < (prompt_len = sc->sh_prompt_len))
		return (0);

	for (pp = sc->sh_prompt;
	    prompt_len && line_len; --prompt_len, --line_len) {
		if (*pp == '\0') {
			for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
			if (!prompt_len)
				return (0);
			for (; line_len && *lp != *pp; --line_len, ++lp);
			if (!line_len)
				return (0);
		}
		if (*pp++ != *lp++)
			break;
	}

	if (prompt_len)
		return (0);
	if (lenp != NULL)
		*lenp = line_len;
	return (1);
}

/*
 * sscr_end --
 *	End the pipe to a shell.
 */
int
sscr_end(sp)
	SCR *sp;
{
	SCRIPT *sc;
	int rval;

	if ((sc = sp->script) == NULL)
		return (0);

	/* Turn off the script flag. */
	F_CLR(sp, S_SCRIPT);

	/* Close down the parent's file descriptors. */
	if (sc->sh_master != -1)
	    (void)close(sc->sh_master);
	if (sc->sh_slave != -1)
	    (void)close(sc->sh_slave);

	/* This should have killed the child. */
	rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0);

	/* Free memory. */
	FREE(sc->sh_prompt, sc->sh_prompt_len);
	FREE(sc, sizeof(SCRIPT));
	sp->script = NULL;

	return (rval);
}
@


1.4
log
@clean up import.  still have to hack some things.
@
text
@d35 1
a35 1
static const char sccsid[] = "@@(#)ex_script.c	8.18 (Berkeley) 8/17/94";
@


1.3
log
@nvi 1.11(beta) from bostic.  reconcile conflicts/kill rcsids.
@
text
@d35 1
a35 1
static char sccsid[] = "@@(#)ex_script.c	8.12 (Berkeley) 3/14/94";
d86 1
a86 1
		    "The script command is only available in vi mode.");
d152 2
a153 2
	 * Don't use vfork() here, because the signal semantics
	 * differ from implementation to implementation.
d155 1
d158 2
d167 2
a168 6
		/*
		 * The utility has default signal behavior.  Don't bother
		 * using sigaction(2) 'cause we want the default behavior.
		 */
		(void)signal(SIGINT, SIG_DFL);
		(void)signal(SIGQUIT, SIG_DFL);
d204 2
a205 1
	default:
d228 1
a234 1
	char *endp, *p, *t, buf[1024];
d250 1
a250 1
		msgq(sp, M_ERR, "Error: timed out.");
d272 1
a272 1
		value = term_key_val(sp, *p);
d362 1
a362 1
empty:			msgq(sp, M_BERR, "Nothing to execute.");
d401 2
a403 1
	EXF *ep;
d408 1
a408 1
	char *bp, *endp, *p, *t;
d438 1
a438 1
		value = term_key_val(sp, *p);
@


1.2
log
@more Ids than you'll ever want.
@
text
@d2 1
a2 1
 * Copyright (c) 1992, 1993
d35 1
a35 2
/* from: static char sccsid[] = "@@(#)ex_script.c	8.10 (Berkeley) 12/22/93"; */
static char *rcsid = "$Id$";
d40 2
d44 1
d46 3
d51 1
d54 4
d485 1
a485 1
 * 
d565 1
a565 1
	
@


1.1
log
@Initial revision
@
text
@d35 2
a36 1
static char sccsid[] = "@@(#)ex_script.c	8.10 (Berkeley) 12/22/93";
@


1.1.1.1
log
@nvi 1.03, from ftp.cs.berkeley.edu, per keith bostic's permission.
@
text
@@


1.1.1.2
log
@nvi/nex 1.11beta from bostic.
@
text
@d2 1
a2 1
 * Copyright (c) 1992, 1993, 1994
d35 1
a35 1
static char sccsid[] = "@@(#)ex_script.c	8.12 (Berkeley) 3/14/94";
a39 2
#include <sys/queue.h>
#include <sys/time.h>
a41 1
#include <bitstring.h>
a42 3
#include <limits.h>
#include <signal.h>
#include <stdio.h>
a44 1
#include <termios.h>
a46 4
#include "compat.h"
#include <db.h>
#include <regex.h>

d474 1
a474 1
 *
d554 1
a554 1

@


1.1.1.3
log
@new public version of nvi
@
text
@d35 1
a35 1
static const char sccsid[] = "@@(#)ex_script.c	8.18 (Berkeley) 8/17/94";
d86 1
a86 1
		    "The script command is only available in vi mode");
d152 2
a153 2
	 * Don't use vfork() here, because the signal semantics differ from
	 * implementation to implementation.
a154 1
	SIGBLOCK(sp->gp);
a156 2
		SIGUNBLOCK(sp->gp);

d164 6
a169 2
		/* The utility has default signal behavior. */
		sig_end();
d205 1
a205 2
	default:			/* Parent. */
		SIGUNBLOCK(sp->gp);
a227 1
	CHAR_T *endp, *p, *t, buf[1024];
d234 1
d250 1
a250 1
		msgq(sp, M_ERR, "Error: timed out");
d272 1
a272 1
		value = KEY_VAL(sp, *p);
d362 1
a362 1
empty:			msgq(sp, M_BERR, "Nothing to execute");
d401 1
a401 1
	CHAR_T *endp, *p, *t;
a402 1
	SCRIPT *sc;
d407 1
a407 1
	char *bp;
d437 1
a437 1
		value = KEY_VAL(sp, *p);
@


1.1.1.4
log
@new public version of nvi
@
text
@d35 1
a35 1
static char sccsid[] = "@@(#)ex_script.c	8.19 (Berkeley) 8/17/94";
@


1.1.1.5
log
@import of nvi 1.66
@
text
@a3 2
 * Copyright (c) 1992, 1993, 1994, 1995, 1996
 *	Keith Bostic.  All rights reserved.
d5 15
a19 2
 * This code is derived from software contributed to Berkeley by
 * Brian Hirt.
d21 11
a31 1
 * See the LICENSE file for redistribution information.
a33 2
#include "config.h"

d35 1
a35 1
static const char sccsid[] = "@@(#)ex_script.c	10.29 (Berkeley) 5/12/96";
a40 7
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_SYS5_PTY
#include <sys/stropts.h>
#endif
a45 3
#include <fcntl.h>
#include <stdio.h>		/* XXX: OSF/1 bug: include before <grp.h> */
#include <grp.h>
d47 2
d54 6
a59 2
#include "../common/common.h"
#include "../vi/vi.h"
a60 1
#include "pathnames.h"
d62 9
a70 7
static void	sscr_check __P((SCR *));
static int	sscr_getprompt __P((SCR *));
static int	sscr_init __P((SCR *));
static int	sscr_insert __P((SCR *));
static int	sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
static int	sscr_pty __P((int *, int *, char *, struct termios *, void *));
static int	sscr_setprompt __P((SCR *, char *, size_t));
d74 1
a75 2
 *
 * PUBLIC: int ex_script __P((SCR *, EXCMD *));
d78 1
a78 1
ex_script(sp, cmdp)
d80 2
a81 1
	EXCMD *cmdp;
d84 1
a84 1
	if (!F_ISSET(sp, SC_VI)) {
d86 1
a86 1
		    "150|The script command is only available in vi mode");
d91 1
a91 1
	if (cmdp->argc != 0 && ex_edit(sp, cmdp))
d94 7
a100 2
	/* Create the shell, figure out the prompt. */
	if (sscr_init(sp))
d111 1
a111 1
sscr_init(sp)
d113 1
a117 4
	/* We're going to need a shell. */
	if (opts_empty(sp, O_SHELL, 0))
		return (1);

a139 1
#ifdef TIOCGWINSZ
d145 1
a145 1
	if (sscr_pty(&sc->sh_master,
d147 1
a147 1
		msgq(sp, M_SYSERR, "pty");
a149 7
#else
	if (sscr_pty(&sc->sh_master,
	    &sc->sh_slave, sc->sh_name, &sc->sh_term, NULL) == -1) {
		msgq(sp, M_SYSERR, "pty");
		goto err;
	}
#endif
a151 1
	 * __TK__ huh?
d155 1
d158 2
d167 3
d174 3
a176 3
		(void)setenv("TERM", "emacs", 1);
		(void)setenv("TERMCAP", "emacs:", 1);
		(void)setenv("EMACS", "t", 1);
d201 2
a202 1
		msgq_str(sp, M_SYSERR, sh_path, "execl: %s");
d205 1
d209 1
a209 1
	if (sscr_getprompt(sp))
d212 1
a212 2
	F_SET(sp, SC_SCRIPT);
	F_SET(sp->gp, G_SCRIPT);
d214 1
d223 1
a223 1
sscr_getprompt(sp)
d225 1
d274 2
a275 2
			if (db_last(sp, &lline) ||
			    db_append(sp, 0, lline, t, p - t))
d305 2
a306 1
	if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) {
a316 2
 *
 * PUBLIC: int sscr_exec __P((SCR *, recno_t));
d319 1
a319 1
sscr_exec(sp, lno)
d321 1
d327 1
a327 1
	int isempty, matchprompt, nw, rval;
d331 1
a331 1
	if (db_last(sp, &last_lno))
d333 2
a334 1
	if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
d336 1
d345 4
a348 2
	if (db_eget(sp, lno, &p, &len, &isempty)) {
		if (isempty)
d350 2
d362 1
a362 1
empty:			msgq(sp, M_BERR, "151|No command to execute");
d384 1
a384 1
		if (db_set(sp, last_lno, bp, last_len + len))
d394 1
a394 3
 *	Read any waiting shell input.
 *
 * PUBLIC: int sscr_input __P((SCR *));
a399 47
	GS *gp;
	struct timeval poll;
	fd_set rdfd;
	int maxfd;

	gp = sp->gp;

loop:	maxfd = 0;
	FD_ZERO(&rdfd);
	poll.tv_sec = 0;
	poll.tv_usec = 0;

	/* Set up the input mask. */
	for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
		if (F_ISSET(sp, SC_SCRIPT)) {
			FD_SET(sp->script->sh_master, &rdfd);
			if (sp->script->sh_master > maxfd)
				maxfd = sp->script->sh_master;
		}

	/* Check for input. */
	switch (select(maxfd + 1, &rdfd, NULL, NULL, &poll)) {
	case -1:
		msgq(sp, M_SYSERR, "select");
		return (1);
	case 0:
		return (0);
	default:
		break;
	}

	/* Read the input. */
	for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
		if (F_ISSET(sp, SC_SCRIPT) &&
		    FD_ISSET(sp->script->sh_master, &rdfd) && sscr_insert(sp))
			return (1);
	goto loop;
}

/*
 * sscr_insert --
 *	Take a line from the shell and insert it into the file.
 */
static int
sscr_insert(sp)
	SCR *sp;
{
d402 1
a403 1
	fd_set rdfd;
d411 2
a412 1
	if (db_last(sp, &lno))
d425 1
d441 1
a441 1
			if (db_append(sp, 1, lno++, t, len))
d458 2
a459 2
			FD_ZERO(&rdfd);
			FD_SET(sc->sh_master, &rdfd);
d461 1
a461 1
			    &rdfd, NULL, NULL, &tv) == 1) {
d469 1
a469 1
		if (db_append(sp, 1, lno++, t, len))
d476 1
a476 1
	rval = vs_refresh(sp, 1);
d491 1
a491 1
	char *buf;
d498 1
a498 1
		free(sc->sh_prompt);
a552 2
 *
 * PUBLIC: int sscr_end __P((SCR *));
d559 1
d564 2
a565 3
	/* Turn off the script flags. */
	F_CLR(sp, SC_SCRIPT);
	sscr_check(sp);
d574 1
a574 1
	(void)proc_wait(sp, (long)sc->sh_pid, "script-shell", 0, 0);
d577 2
a578 2
	free(sc->sh_prompt);
	free(sc);
d581 1
a581 191
	return (0);
}

/*
 * sscr_check --
 *	Set/clear the global scripting bit.
 */
static void
sscr_check(sp)
	SCR *sp;
{
	GS *gp;

	gp = sp->gp;
	for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
		if (F_ISSET(sp, SC_SCRIPT)) {
			F_SET(gp, G_SCRIPT);
			return;
		}
	F_CLR(gp, G_SCRIPT);
}

#ifdef HAVE_SYS5_PTY
static int ptys_open __P((int, char *));
static int ptym_open __P((char *));

static int
sscr_pty(amaster, aslave, name, termp, winp)
	int *amaster, *aslave;
	char *name;
	struct termios *termp;
	void *winp;
{
	int master, slave, ttygid;

	/* open master terminal */
	if ((master = ptym_open(name)) < 0)  {
		errno = ENOENT;	/* out of ptys */
		return (-1);
	}

	/* open slave terminal */
	if ((slave = ptys_open(master, name)) >= 0) {
		*amaster = master;
		*aslave = slave;
	} else {
		errno = ENOENT;	/* out of ptys */
		return (-1);
	}

	if (termp)
		(void) tcsetattr(slave, TCSAFLUSH, termp);
#ifdef TIOCSWINSZ
	if (winp != NULL)
		(void) ioctl(slave, TIOCSWINSZ, (struct winsize *)winp);
#endif
	return (0);
}

/*
 * ptym_open --
 *	This function opens a master pty and returns the file descriptor
 *	to it.  pts_name is also returned which is the name of the slave.
 */
static int
ptym_open(pts_name)
	char *pts_name;
{
	int fdm;
	char *ptr, *ptsname();

	strcpy(pts_name, _PATH_SYSV_PTY);
	if ((fdm = open(pts_name, O_RDWR)) < 0 )
		return (-1);

	if (grantpt(fdm) < 0) {
		close(fdm);
		return (-2);
	}

	if (unlockpt(fdm) < 0) {
		close(fdm);
		return (-3);
	}

	if (unlockpt(fdm) < 0) {
		close(fdm);
		return (-3);
	}

	/* get slave's name */
	if ((ptr = ptsname(fdm)) == NULL) {
		close(fdm);
		return (-3);
	}
	strcpy(pts_name, ptr);
	return (fdm);
}

/*
 * ptys_open --
 *	This function opens the slave pty.
 */
static int
ptys_open(fdm, pts_name)
	int fdm;
	char *pts_name;
{
	int fds;

	if ((fds = open(pts_name, O_RDWR)) < 0) {
		close(fdm);
		return (-5);
	}

	if (ioctl(fds, I_PUSH, "ptem") < 0) {
		close(fds);
		close(fdm);
		return (-6);
	}

	if (ioctl(fds, I_PUSH, "ldterm") < 0) {
		close(fds);
		close(fdm);
		return (-7);
	}

	if (ioctl(fds, I_PUSH, "ttcompat") < 0) {
		close(fds);
		close(fdm);
		return (-8);
	}

	return (fds);
}

#else /* !HAVE_SYS5_PTY */

static int
sscr_pty(amaster, aslave, name, termp, winp)
	int *amaster, *aslave;
	char *name;
	struct termios *termp;
	void *winp;
{
	static char line[] = "/dev/ptyXX";
	register char *cp1, *cp2;
	register int master, slave, ttygid;
	struct group *gr;

	if ((gr = getgrnam("tty")) != NULL)
		ttygid = gr->gr_gid;
	else
		ttygid = -1;

	for (cp1 = "pqrs"; *cp1; cp1++) {
		line[8] = *cp1;
		for (cp2 = "0123456789abcdef"; *cp2; cp2++) {
			line[5] = 'p';
			line[9] = *cp2;
			if ((master = open(line, O_RDWR, 0)) == -1) {
				if (errno == ENOENT)
					return (-1);	/* out of ptys */
			} else {
				line[5] = 't';
				(void) chown(line, getuid(), ttygid);
				(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
#ifdef HAVE_REVOKE
				(void) revoke(line);
#endif
				if ((slave = open(line, O_RDWR, 0)) != -1) {
					*amaster = master;
					*aslave = slave;
					if (name)
						strcpy(name, line);
					if (termp)
						(void) tcsetattr(slave, 
							TCSAFLUSH, termp);
#ifdef TIOCSWINSZ
					if (winp)
						(void) ioctl(slave, TIOCSWINSZ, 
							(char *)winp);
#endif
					return (0);
				}
				(void) close(master);
			}
		}
	}
	errno = ENOENT;	/* out of ptys */
	return (-1);
a582 1
#endif /* HAVE_SYS5_PTY */
@


1.1.1.6
log
@import of nvi 1.79
@
text
@d16 1
a16 1
static const char sccsid[] = "@@(#)ex_script.c	10.30 (Berkeley) 9/24/96";
d196 1
a196 1
	F_SET(sp->gp, G_SCRWIN);
d622 1
a622 1
			F_SET(gp, G_SCRWIN);
d625 1
a625 1
	F_CLR(gp, G_SCRWIN);
@

