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