xref: /netbsd/external/bsd/nvi/dist/ex/ex_script.c (revision 1427020c)
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
ex_script(SCR * sp,EXCMD * cmdp)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
sscr_init(SCR * sp)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
sscr_getprompt(SCR * sp)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
sscr_exec(SCR * sp,db_recno_t lno)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
sscr_check_input(SCR * sp,fd_set * fdset,int maxfd)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
sscr_input(SCR * sp)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
sscr_insert(SCR * sp)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
sscr_setprompt(SCR * sp,char * buf,size_t len)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
sscr_end(SCR * sp)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
sscr_check(SCR * sp)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
sscr_pty(int * amaster,int * aslave,char * name,struct termios * termp,void * winp)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
ptym_open(char * pts_name)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
ptys_open(int fdm,char * pts_name)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
sscr_pty(amaster,aslave,name,termp,winp)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