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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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