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