home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex_script.c < prev    next >
C/C++ Source or Header  |  1997-06-29  |  20KB  |  929 lines

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