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