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