home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / SH162_2S.ZIP / SH5.C < prev    next >
C/C++ Source or Header  |  1990-07-05  |  19KB  |  969 lines

  1. /* MS-DOS SHELL - Main I/O Functions
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: sh5.c 1.9 90/05/11 18:45:40 MS_user Exp $
  17.  *
  18.  *    $Log:    sh5.c $
  19.  * Revision 1.9  90/05/11  18:45:40  MS_user
  20.  * Fix problem when at end of buffer on re-load from file
  21.  *
  22.  * Revision 1.8  90/04/25  10:58:41  MS_user
  23.  * Fix re-reading re-assigned buffers correctly.
  24.  *
  25.  * Revision 1.7  90/04/25  09:20:08  MS_user
  26.  * Fix lseek problem and TAG problem on here documents
  27.  *
  28.  * Revision 1.6  90/04/09  17:04:50  MS_user
  29.  * g_tempname must check for slash or backslash
  30.  *
  31.  * Revision 1.5  90/03/21  14:03:47  MS_user
  32.  * Add new gravechar procedure for handling here documents
  33.  *
  34.  * Revision 1.4  90/03/14  19:31:28  MS_user
  35.  * Add buffered output for here document processing.
  36.  * Fix here document processing so it works correctly.
  37.  * Add missing IOTHERE (<<-) processing for here documents.
  38.  *
  39.  * Revision 1.3  90/03/06  16:49:58  MS_user
  40.  * Add disable history option
  41.  *
  42.  * Revision 1.2  90/03/05  13:51:45  MS_user
  43.  * Add functionality to readc to support dot command via run function
  44.  * Add $HOME as a temporary file directory
  45.  *
  46.  * Revision 1.1  90/01/25  13:41:50  MS_user
  47.  * Initial revision
  48.  *
  49.  */
  50.  
  51. #include <sys/types.h>
  52. #include <stdio.h>
  53. #include <signal.h>
  54. #include <errno.h>
  55. #include <setjmp.h>
  56. #include <stdlib.h>
  57. #include <string.h>
  58. #include <fcntl.h>
  59. #include <io.h>
  60. #include <limits.h>
  61. #include <unistd.h>
  62. #include "sh.h"
  63.  
  64. /*
  65.  * shell IO
  66.  */
  67.  
  68. static IO_Buf        sharedbuf = {AFID_NOBUF};
  69. static IO_Buf        mainbuf = {AFID_NOBUF};
  70. static unsigned int    bufid = AFID_ID;    /* buffer id counter */
  71.                     /* list of hear docs while parsing */
  72. static Here_D    *inhere = (Here_D *)NULL;
  73.                     /* list of active here documents */
  74. static Here_D    *acthere = (Here_D *)NULL;
  75.  
  76. static int    dol1_char (IO_State *);
  77. static void    readhere (char **, char *, int, int);
  78. static int    herechar (IO_State *);
  79.  
  80. int        Getc (ec)
  81. register int    ec;
  82. {
  83.     register int    c;
  84.  
  85.     if (e.linep > e.eline)
  86.     {
  87.     while (((c = readc ()) != NL) && c)
  88.         ;
  89.  
  90.     print_error ("sh: input line too long\n");
  91.     gflg++;
  92.     return c;
  93.     }
  94.  
  95.     c = readc();
  96.     if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE))
  97.     {
  98.     if (c == '\\')
  99.     {
  100.         if (((c = readc ()) == NL) && (ec != '\"'))
  101.         return Getc (ec);
  102.  
  103.         c |= QUOTE;
  104.     }
  105.     }
  106.  
  107.     return c;
  108. }
  109.  
  110. void    unget (c)
  111. int    c;
  112. {
  113.     if (e.iop >= e.iobase)
  114.     e.iop->peekc = c;
  115. }
  116.  
  117. int    eofc ()
  118. {
  119.     return (e.iop < e.iobase) || ((e.iop->peekc == 0) && (e.iop->prev == 0));
  120. }
  121.  
  122. /* Read the next character */
  123.  
  124. int    readc ()
  125. {
  126.     register int    c;
  127.     char        s_dflag = e.iop->dflag;
  128.  
  129. /* The dflag is transfered from the higher level to the lower level at end
  130.  * of input at the higher level.  This is part of the implementation of
  131.  * $* and $@ processing.
  132.  */
  133.  
  134.     for (; e.iop >= e.iobase; e.iop--)
  135.     {
  136.  
  137. /* Set up the current dflag */
  138.  
  139.     e.iop->dflag = s_dflag;
  140.  
  141. /* If there is an unget character, use it */
  142.  
  143.     if ((c = e.iop->peekc) != '\0')
  144.     {
  145.         e.iop->peekc = 0;
  146.         return c;
  147.     }
  148.  
  149. /* Some special processing for multi-line commands */
  150.  
  151.     else
  152.     {
  153.         if (e.iop->prev != 0)
  154.         {
  155.  
  156. /* Get the next character from the IO function */
  157.  
  158.         if ((c = (*e.iop->iofn)(e.iop)) != '\0')
  159.         {
  160.  
  161. /* End of current level, but continue at this level as another read
  162.  * function has been put on the stack
  163.  */
  164.  
  165.             if (c == -1)
  166.             {
  167.             e.iop++;
  168.             continue;
  169.             }
  170.  
  171. /* If we are at the bottom - echo the character */
  172.  
  173.             if ((e.iop == iostack) && (FL_TEST ('v')))
  174.             S_putc ((char)c);
  175.  
  176. /* Return the current character */
  177.  
  178.             return (e.iop->prev = (char)c);
  179.         }
  180.  
  181.         else if (e.iop->task == XIO && e.iop->prev != NL)
  182.         {
  183.             e.iop->prev = 0;
  184.  
  185.             if ((e.iop == iostack) && (FL_TEST ('v')))
  186.             S_putc (NL);
  187.  
  188.             return NL;
  189.         }
  190.  
  191.         else
  192.             s_dflag = e.iop->dflag;
  193.         }
  194.  
  195.         if (e.iop->task == XIO)
  196.         {
  197.         if (multiline)
  198.             return e.iop->prev = 0;
  199.  
  200.         if (talking && (e.iop == iostack + 1) && !e.eof_p)
  201.             put_prompt (ps1->value);
  202.         }
  203.     }
  204.     }
  205.  
  206. /* End of file detected.  If more data on stack and the special EOF
  207.  * processing is not enabled - return 0
  208.  */
  209.  
  210.     if ((e.iop >= iostack) && !e.eof_p)
  211.     return 0;
  212.  
  213.     leave();
  214.     /* NOTREACHED */
  215. }
  216.  
  217. /* Add an Input channel to the input stack */
  218.  
  219. void        pushio (argp, fn)
  220. IO_Args        *argp;
  221. int        (*fn)(IO_State *);
  222. {
  223.     if (++e.iop >= &iostack[NPUSH])
  224.     {
  225.     e.iop--;
  226.     print_error ("sh: Shell input nested too deeply\n");
  227.     gflg++;
  228.     return;
  229.     }
  230.  
  231.     e.iop->iofn = fn;
  232.  
  233.     if (argp->afid != AFID_NOBUF)
  234.     e.iop->argp = argp;
  235.  
  236.     else
  237.     {
  238.     e.iop->argp  = ioargstack + (e.iop - iostack);
  239.     *e.iop->argp = *argp;
  240.     e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf;
  241.  
  242.     if ((isatty (e.iop->argp->afile) == 0) &&
  243.         ((e.iop == &iostack[0]) ||
  244.          (lseek (e.iop->argp->afile, 0L, SEEK_CUR) != -1L)))
  245.     {
  246.         if (++bufid == AFID_NOBUF)
  247.         bufid = AFID_ID;
  248.  
  249.         e.iop->argp->afid  = bufid;
  250.     }
  251.     }
  252.  
  253.     e.iop->prev  = ~NL;
  254.     e.iop->peekc = 0;
  255.     e.iop->xchar = 0;
  256.     e.iop->nlcount = 0;
  257.  
  258.     if ((fn == filechar) || (fn == linechar))
  259.     e.iop->task = XIO;
  260.  
  261.     else if ((fn == gravechar) || (fn == qgravechar) || (fn == sgravechar))
  262.     e.iop->task = XGRAVE;
  263.  
  264.     else
  265.     e.iop->task = XOTHER;
  266. }
  267.  
  268. /*
  269.  * Input generating functions
  270.  */
  271.  
  272. /*
  273.  * Produce the characters of a string, then a newline, then EOF.
  274.  */
  275. int            nlchar (iop)
  276. register IO_State    *iop;
  277. {
  278.     register int    c;
  279.  
  280.     if (iop->argp->aword == (char *)NULL)
  281.     return 0;
  282.  
  283.     if ((c = *iop->argp->aword++) == 0)
  284.     {
  285.     iop->argp->aword = (char *)NULL;
  286.     return NL;
  287.     }
  288.  
  289.     return c;
  290. }
  291.  
  292. /*
  293.  * Given a list of words, produce the characters
  294.  * in them, with a space after each word.
  295.  */
  296.  
  297. int            wdchar (iop)
  298. register IO_State    *iop;
  299. {
  300.     register char    c;
  301.     register char    **wl;
  302.  
  303.     if ((wl = iop->argp->awordlist) == (char **)NULL)
  304.     return 0;
  305.  
  306.     if (*wl != (char *)NULL)
  307.     {
  308.     if ((c = *(*wl)++) != 0)
  309.         return (c & 0177);
  310.  
  311.     iop->argp->awordlist++;
  312.     return SP;
  313.     }
  314.  
  315.     iop->argp->awordlist = (char **)NULL;
  316.     return NL;
  317. }
  318.  
  319. /*
  320.  * Return the characters of a list of words, producing a space between them.
  321.  */
  322.  
  323. int            dol_char (iop)
  324. IO_State        *iop;
  325. {
  326.     register char    *wp;
  327.     char        cflag;
  328.  
  329.     if ((wp = *(iop->argp->awordlist)++) != (char *)NULL)
  330.     {
  331.     if (*iop->argp->awordlist == (char *)NULL)
  332.         iop->dflag |= DSA_END;
  333.  
  334.     cflag = iop->dflag;
  335.     PUSHIO (aword, wp, dol1_char);
  336.     e.iop->dflag = cflag;
  337.     return -1;
  338.     }
  339.  
  340.     return 0;
  341. }
  342.  
  343. /* Return next character from the word with a space at the end */
  344.  
  345. static int        dol1_char (iop)
  346. IO_State        *iop;
  347. {
  348.     register int c;
  349.  
  350.     if ((iop->dflag & DSA_MODE) == DSA_AMP)
  351.     {
  352.     if (!(iop->dflag & DSA_START))
  353.         iop->dflag |= DSA_START;
  354.  
  355. /* Has the previous word ended */
  356.  
  357.     else if (iop->dflag & DSA_START1)
  358.     {
  359.         iop->dflag &= ~DSA_START1;
  360.         return '"';
  361.     }
  362.     }
  363.  
  364.     if (iop->argp->aword == (char *)NULL)
  365.     return 0;
  366.  
  367.     if ((c = *iop->argp->aword) == '\0')
  368.     {
  369.     if ((iop->dflag & DSA_MODE) != DSA_AMP)
  370.     {
  371.         iop->argp->aword = (char *)NULL;
  372.         return (iop->dflag & DSA_END) ? 0 : SP;
  373.     }
  374.  
  375.     if (!(iop->dflag & DSA_END1))
  376.     {
  377.         iop->dflag |= DSA_END1;
  378.         return '"';
  379.     }
  380.  
  381.     iop->argp->aword = (char *)NULL;
  382.     iop->dflag &= ~DSA_END1;
  383.     iop->dflag |= DSA_START1;
  384.     return (iop->dflag & DSA_END) ? 0 : SP;
  385.     }
  386.  
  387.     iop->argp->aword++;
  388.     if ((iop->dflag != DSA_NULL) && any ((char)c, ifs->value))
  389.     c |= QUOTE;
  390.  
  391.     return c;
  392. }
  393.  
  394. /*
  395.  * Produce the characters from a single word (string).
  396.  */
  397.  
  398. int        strchar (iop)
  399. IO_State    *iop;
  400. {
  401.     register int    c;
  402.  
  403.     return ((iop->argp->aword == (char *)NULL) ||
  404.         ((c = *(iop->argp->aword++)) == 0)) ? 0 : c;
  405. }
  406.  
  407. /*
  408.  * Produce quoted characters from a single word (string).
  409.  */
  410.  
  411. int        qstrchar (iop)
  412. IO_State    *iop;
  413. {
  414.     register int    c;
  415.  
  416.     return ((iop->argp->aword == (char *)NULL) ||
  417.         ((c = *(iop->argp->aword++)) == 0)) ? 0 : (c | QUOTE);
  418. }
  419.  
  420. /*
  421.  * Return the characters from a file.
  422.  */
  423.  
  424. int        filechar (iop)
  425. IO_State    *iop;
  426. {
  427.     register IO_Args    *ap = iop->argp;
  428.     register int    i;
  429.     char        c;
  430.     IO_Buf        *bp = ap->afbuf;
  431.  
  432.     if (ap->afid != AFID_NOBUF)
  433.     {
  434.  
  435. /* When we reread a buffer, we need to check to see if we have reached the
  436.  * end.  If we have, we need to read the next buffer.  Hence, this loop for
  437.  * the second read
  438.  */
  439.  
  440.     while (((i = (ap->afid != bp->id)) || (bp->bufp == bp->ebufp)))
  441.     {
  442.  
  443. /* Are we re-reading a corrupted buffer? */
  444.  
  445.         if (i)
  446.         lseek (ap->afile, ap->afpos, SEEK_SET);
  447.  
  448. /* No, filling so set offset to zero */
  449.  
  450.         else
  451.         ap->afoff = 0;
  452.  
  453. /* Save the start of the next buffer */
  454.  
  455.         ap->afpos = lseek (ap->afile, 0L, SEEK_CUR);
  456.  
  457. /* Read in the next buffer */
  458.  
  459.         if ((i = read (ap->afile, bp->buf, sizeof (bp->buf))) <= 0)
  460.         {
  461.         if (ap->afile > STDERR_FILENO)
  462.             S_close (ap->afile, TRUE);
  463.  
  464.         return 0;
  465.         }
  466.  
  467. /* Set up buffer id, start and end */
  468.  
  469.         bp->id    = ap->afid;
  470.         bp->bufp  = bp->buf + ap->afoff;
  471.         bp->ebufp = bp->buf + i;
  472.     }
  473.  
  474. /* Return the next character from the buffer */
  475.  
  476.     ++(ap->afoff);
  477.     return *bp->bufp++ & 0177;
  478.     }
  479.  
  480. /* If this is the terminal, there is special input processing */
  481.  
  482. #ifndef NO_HISTORY
  483.     else if ((ap->afile == 0) && isatty (ap->afile))
  484.         return Get_stdin (ap);
  485. #endif
  486.  
  487.     if ((i = read (ap->afile, &c, sizeof(c))) == sizeof (c))
  488.     return (int)c & 0177;
  489.  
  490.     if (ap->afile > STDERR_FILENO)
  491.     S_close (ap->afile, TRUE);
  492.  
  493.     return 0;
  494. }
  495.  
  496. /*
  497.  * Return the characters from a here temp file.
  498.  */
  499.  
  500. static int        herechar (iop)
  501. register IO_State    *iop;
  502. {
  503.     char            c;
  504.  
  505.     if (read (iop->argp->afile, &c, sizeof(c)) != sizeof(c))
  506.     {
  507.     S_close (iop->argp->afile, TRUE);
  508.     c = 0;
  509.     }
  510.  
  511.     return c;
  512. }
  513.  
  514. /*
  515.  * Return the characters produced by a process (`...`).
  516.  * De-quote them if required.  Use in here documents.
  517.  */
  518.  
  519. int        sgravechar (iop)
  520. IO_State    *iop;
  521. {
  522.     return  qgravechar (iop) & ~QUOTE;
  523. }
  524.  
  525. /*
  526.  * Return the characters produced by a process (`...`).
  527.  * De-quote them if required, and converting NL to space.
  528.  */
  529.  
  530. int        gravechar (iop)
  531. IO_State    *iop;
  532. {
  533.     register int c;
  534.  
  535.     if ((c = qgravechar (iop) & ~QUOTE) == NL)
  536.     c = SP;
  537.  
  538.     return c;
  539. }
  540.  
  541. /*
  542.  * Process input from a `...` string
  543.  */
  544.  
  545. int        qgravechar (iop)
  546. IO_State    *iop;
  547. {
  548.     register int    c;
  549.  
  550.     if (iop->xchar)
  551.     {
  552.     if (iop->nlcount)
  553.     {
  554.         iop->nlcount--;
  555.         return (NL | QUOTE);
  556.     }
  557.  
  558.     c = iop->xchar;
  559.     iop->xchar = 0;
  560.     }
  561.  
  562.     else if ((c = filechar (iop)) == NL)
  563.     {
  564.     iop->nlcount = 1;
  565.  
  566.     while ((c = filechar (iop)) == NL)
  567.         iop->nlcount++;
  568.  
  569.     iop->xchar = (char)c;
  570.  
  571.     if (c == 0)
  572.         return c;
  573.  
  574.     iop->nlcount--;
  575.     c = NL;
  576.     }
  577.  
  578.     return (c != 0) ? (c | QUOTE) : 0;
  579. }
  580.  
  581. /*
  582.  * Return a single command (usually the first line) from a file.
  583.  */
  584.  
  585. int        linechar (iop)
  586. IO_State    *iop;
  587. {
  588.     register int    c;
  589.  
  590.     if ((c = filechar (iop)) == NL)
  591.     {
  592.     if (!multiline)
  593.     {
  594.         if (iop->argp->afile > STDERR_FILENO)
  595.         S_close (iop->argp->afile, TRUE);
  596.  
  597.         iop->argp->afile = -1;    /* illegal value */
  598.     }
  599.     }
  600.  
  601.     return c;
  602. }
  603.  
  604. void    closeall ()
  605. {
  606.     register int    u;
  607.  
  608.     for (u = NUFILE; u < NOFILE;)
  609.     S_close (u++, TRUE);
  610. }
  611.  
  612. /*
  613.  * remap fd into Shell's fd space
  614.  */
  615.  
  616. int        remap (fd)
  617. register int    fd;
  618. {
  619.     register int    i;
  620.     register int    n_io = 0;
  621.     int            map[NOFILE];
  622.     int            o_fd = fd;
  623.  
  624.     if (fd < e.iofd)
  625.     {
  626.     do
  627.     {
  628.         map[n_io++] = fd;
  629.         fd = dup (fd);
  630.  
  631.     } while ((fd >= 0) && (fd < e.iofd));
  632.  
  633.     for (i = 0; i < n_io; i++)
  634.         close (map[i]);
  635.  
  636.     S_Remap (o_fd, fd);
  637.     S_close (o_fd, TRUE);
  638.  
  639.     if (fd < 0)
  640.         print_error ("sh: too many files open\n");
  641.     }
  642.  
  643.     return fd;
  644. }
  645.  
  646. /*
  647.  * here documents
  648.  */
  649.  
  650. void        markhere (s, iop)
  651. register char    *s;
  652. IO_Actions     *iop;
  653. {
  654.     register Here_D    *h, *lh;
  655.  
  656.     if ((h = (Here_D *) space(sizeof(Here_D))) == (Here_D *)NULL)
  657.     return;
  658.  
  659.     if ((h->h_tag = evalstr (s, DOSUB)) == (char *)NULL)
  660.     return;
  661.  
  662.     h->h_iop     = iop;
  663.     iop->io_name = (char *)NULL;
  664.     h->h_next    = (Here_D *)NULL;
  665.  
  666.     if (inhere == (Here_D *)NULL)
  667.     inhere = h;
  668.  
  669.     else
  670.     {
  671.     for (lh = inhere; lh != (Here_D *)NULL; lh = lh->h_next)
  672.     {
  673.         if (lh->h_next == (Here_D *)NULL)
  674.         {
  675.         lh->h_next = h;
  676.         break;
  677.         }
  678.     }
  679.     }
  680.  
  681.     iop->io_flag |= IOHERE|IOXHERE;
  682.  
  683.     for (s = h->h_tag; *s; s++)
  684.     {
  685.     if (*s & QUOTE)
  686.     {
  687.         iop->io_flag &= ~ IOXHERE;
  688.         *s &= ~ QUOTE;
  689.     }
  690.     }
  691.  
  692.     h->h_dosub = iop->io_flag & IOXHERE;
  693. }
  694.  
  695. void    gethere ()
  696. {
  697.     register Here_D    *h, *hp;
  698.  
  699. /* Scan here files first leaving inhere list in place */
  700.  
  701.     for (hp = h = inhere; h != (Here_D *)NULL; hp = h, h = h->h_next)
  702.     readhere (&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\'',
  703.           h->h_iop->io_flag);
  704.  
  705. /* Make inhere list active - keep list intact for scraphere */
  706.  
  707.     if (hp != (Here_D *)NULL)
  708.     {
  709.     hp->h_next = acthere;
  710.     acthere    = inhere;
  711.     inhere     = (Here_D *)NULL;
  712.     }
  713. }
  714.  
  715. static void    readhere (name, s, ec, ioflag)
  716. char        **name;
  717. register char    *s;
  718. int        ec;
  719. int        ioflag;
  720. {
  721.     int            tf;
  722.     register int    c;
  723.     jmp_buf        ev;
  724.     char        *line;
  725.     char        *next;
  726.     int            stop_len;
  727.     int            c_len;
  728.     Out_Buf        *bp;
  729.  
  730. /* Create a temporary file and open it */
  731.  
  732.     *name = strsave (g_tempname (), areanum);
  733.  
  734.     if ((tf = S_open (FALSE, *name, O_CMASK | O_NOINHERIT, 0600)) < 0)
  735.     return;
  736.  
  737.     if (newenv (setjmp (errpt = ev)) == TRUE)
  738.     S_Delete (tf);
  739.  
  740.     else
  741.     {
  742.     pushio (e.iop->argp, e.iop->iofn);
  743.     e.iobase = e.iop;
  744.  
  745. /* Strip leading tabs? */
  746.  
  747.     if (ioflag & IOTHERE)
  748.     {
  749.         while (*s && (*s == '\t'))
  750.         ++s;
  751.     }
  752.  
  753. /* Open the Output buffer */
  754.  
  755.     line = space ((stop_len = strlen (s) + 2) + 1);
  756.     bp = Open_buffer (tf, TRUE);
  757.  
  758. /* Read in */
  759.  
  760.     while (1)
  761.     {
  762.         next = line;
  763.         c_len = 0;
  764.  
  765.         if (talking && e.iop <= iostack + 1)
  766.         {
  767. #ifndef NO_HISTORY
  768.         Add_History (FALSE);
  769. #endif
  770.         put_prompt (ps2->value);
  771.         }
  772.  
  773. /* Read the here document */
  774.  
  775.         while ((c = Getc (ec)) != NL && c)
  776.         {
  777.  
  778. /* Strip leading tabs? */
  779.  
  780.         if ((ioflag & IOTHERE) && (c == '\t') && (next == line))
  781.             continue;
  782.  
  783.         if (ec == '\'')
  784.             c &= ~ QUOTE;
  785.  
  786. /* If not end of search string, add to search string */
  787.  
  788.         if ((++c_len) < stop_len)
  789.             *(next++) = (char)c;
  790.  
  791. /* If one greater that search string, buffer search string */
  792.  
  793.         else
  794.         {
  795.             if (c_len == stop_len)
  796.             {
  797.             *next = 0;
  798.             Adds_buffer (line, bp);
  799.             }
  800.  
  801. /* Buffer the current character */
  802.  
  803.             Add_buffer ((char)c, bp);
  804.         }
  805.         }
  806.  
  807. /* Check for end of document */
  808.  
  809.         *next = 0;
  810.         if (strcmp (s, line) == 0 || c == 0)
  811.         break;
  812.  
  813.         if (c_len < stop_len)
  814.             Adds_buffer (line, bp);
  815.  
  816.         Add_buffer (NL, bp);
  817.     }
  818.  
  819.     Close_buffer (bp);
  820.  
  821.     if (c == 0)
  822.         print_error ("here document `%s' unclosed\n", s);
  823.  
  824.     quitenv ();
  825.     }
  826.  
  827.     S_close (tf, TRUE);
  828. }
  829.  
  830. /*
  831.  * open here temp file.
  832.  * If unquoted here, expand here temp file into second temp file.
  833.  */
  834.  
  835. int        herein (hname, xdoll)
  836. char        *hname;
  837. int        xdoll;
  838. {
  839.     register int    hf, tf;
  840.  
  841. /* If the here document is invalid or does not exist, return */
  842.  
  843.     if (hname == (char *)NULL)
  844.     return -1;
  845.  
  846.     if ((hf = S_open (FALSE, hname, O_RDONLY)) < 0)
  847.     return -1;
  848.  
  849. /* If processing for $, ` and ' is required, do it */
  850.  
  851.     if (xdoll)
  852.     {
  853.     char        c;
  854.     char        *tname = strsave (g_tempname (), areanum);
  855.     Out_Buf        *bp;
  856.     jmp_buf        ev;
  857.  
  858.     if ((tf = S_open (FALSE, tname, O_CMASK | O_NOINHERIT, 0600)) < 0)
  859.         return -1;
  860.  
  861.     if (newenv (setjmp (errpt = ev)) == FALSE)
  862.     {
  863.         bp = Open_buffer (tf, TRUE);
  864.         PUSHIO (afile, hf, herechar);
  865.         e.iobase = e.iop;
  866.  
  867.         while ((c = (char)subgetc (0, 0)) != 0)
  868.         {
  869.  
  870. /* Determine how many characters to write.  If MSB set, write \x.
  871.  * Otherwise, write x
  872.  */
  873.  
  874.         if ((c & QUOTE) && !any ((c & ~QUOTE), "$`\\"))
  875.             Add_buffer ('\\', bp);
  876.  
  877.         Add_buffer ((char)(c & (~QUOTE)), bp);
  878.         }
  879.  
  880.         quitenv ();
  881.     }
  882.  
  883.     else
  884.         S_Delete (tf);
  885.  
  886.     Close_buffer (bp);
  887.     S_close (tf, TRUE);
  888.     return S_open (TRUE, tname, O_RDONLY);
  889.     }
  890.  
  891.     else
  892.     return hf;
  893. }
  894.  
  895. void    scraphere()
  896. {
  897.     register Here_D    *h;
  898.  
  899.     for (h = inhere; h != (Here_D *)NULL; h = h->h_next)
  900.     {
  901.     if ((h->h_iop != (IO_Actions *)NULL) &&
  902.         (h->h_iop->io_name != (char *)NULL))
  903.         unlink (h->h_iop->io_name);
  904.     }
  905.  
  906.     inhere = (Here_D *)NULL;
  907. }
  908.  
  909. /* unlink here temp files before a freearea (area) */
  910.  
  911. void    freehere (area)
  912. int    area;
  913. {
  914.     register Here_D    *h;
  915.     register Here_D    *hl = (Here_D *)NULL;
  916.  
  917.     for (h = acthere; h != (Here_D *)NULL; hl = h, h = h->h_next)
  918.     {
  919.     if (getarea ((char *)h) >= area)
  920.     {
  921.         if (h->h_iop->io_name != (char *)NULL)
  922.         unlink (h->h_iop->io_name);
  923.  
  924.         if (hl == (Here_D *)NULL)
  925.         acthere = h->h_next;
  926.  
  927.         else
  928.         hl->h_next = h->h_next;
  929.     }
  930.     }
  931. }
  932.  
  933. char    *g_tempname ()
  934. {
  935.     static char    tmpfile[FFNAME_MAX];
  936.     char    *tmpdir;    /* Points to directory prefix of pipe    */
  937.     static int    temp_count = 0;
  938.     char    *sep = "/";
  939.  
  940. /* Find out where we should put temporary files */
  941.  
  942.     if (((tmpdir = lookup ("TMP", FALSE)->value) == null) &&
  943.     ((tmpdir = lookup (home, FALSE)->value) == null))
  944.     tmpdir = lookup ("TMPDIR", FALSE)->value;
  945.  
  946.     if (any (tmpdir[strlen (tmpdir) - 1], "/\\"))
  947.     sep = null;
  948.  
  949. /* Get a unique temporary file name */
  950.  
  951.     while (1)
  952.     {
  953.     sprintf (tmpfile, "%s%ssht%.5u.tmp", tmpdir, sep, temp_count++);
  954.  
  955.     if (access (tmpfile, F_OK) != 0)
  956.         break;
  957.     }
  958.  
  959.     return tmpfile;
  960. }
  961.  
  962. /* Test to see if the current Input device is the console */
  963.  
  964. bool    Interactive ()
  965. {
  966.     return (talking && (e.iop->task == XIO) && isatty (e.iop->argp->afile))
  967.        ? TRUE : FALSE;
  968. }
  969.