home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 11 / AUCD11B.iso / LANGUAGES / WraithSet / AwkStuff / MawkSrc / c / files < prev    next >
Encoding:
Text File  |  1996-01-14  |  11.8 KB  |  622 lines

  1.  
  2. /********************************************
  3. files.c
  4. copyright 1991-94.  Michael D. Brennan
  5.  
  6. This is a source file for mawk, an implementation of
  7. the AWK programming language.
  8.  
  9. Mawk is distributed without warranty under the terms of
  10. the GNU General Public License, version 2, 1991.
  11. ********************************************/
  12.  
  13. /*$Log: files.c,v $
  14.  * Revision 1.9  1996/01/14  17:14:10  mike
  15.  * flush_all_output()
  16.  *
  17.  * Revision 1.8  1995/06/06  00:18:27  mike
  18.  * change mawk_exit(1) to mawk_exit(2)
  19.  *
  20.  * Revision 1.7  1994/12/11  20:48:50  mike
  21.  * fflush builtin
  22.  *
  23.  * Revision 1.6  1994/10/08  19:15:40  mike
  24.  * remove SM_DOS
  25.  *
  26.  * Revision 1.5  1994/04/17  20:01:37  mike
  27.  * recognize filename "/dev/stdout"
  28.  *
  29.  * Revision 1.4  1994/02/21  00:11:07  mike
  30.  * code cleanup
  31.  *
  32.  * Revision 1.3  1993/07/16  01:00:36  mike
  33.  * cleanup and indent
  34.  *
  35.  * Revision 5.5     1992/12/17  02:48:01  mike
  36.  * 1.1.2d changes for DOS
  37.  *
  38.  * Revision 5.4     1992/07/10  16:10:30  brennan
  39.  * patch2
  40.  * MsDOS: remove useless NO_BINMODE macro
  41.  * get process exit code on in pipes
  42.  *
  43.  * Revision 5.3     1992/04/07  20:21:17  brennan
  44.  * patch 2
  45.  * unbuffered output to a tty
  46.  *
  47.  * Revision 5.2     1992/04/07  16:03:08  brennan
  48.  * patch 2
  49.  * allow same filename for output and input, but use different descriptors
  50.  * E.g. < "/dev/tty" and > "/dev/tty"
  51.  *
  52.  * Revision 5.1     91/12/05  07:56:00  brennan
  53.  * 1.1 pre-release
  54.  *
  55. */
  56.  
  57. /* files.c */
  58.  
  59. #include "mawk.h"
  60. #include "files.h"
  61. #include "memory.h"
  62. #include "fin.h"
  63.  
  64. static FILE *PROTO(tfopen, (char *, char *)) ;
  65. static void PROTO(efflush, (FILE*)) ;
  66. static void PROTO(add_to_child_list, (int, int)) ;
  67. static struct child *PROTO(remove_from_child_list, (int)) ;
  68. extern int PROTO(isatty, (int)) ;
  69.  
  70. #ifdef    V7
  71. #include  <sgtty.h>        /* defines FIOCLEX */
  72. #endif
  73.  
  74.  
  75. #ifndef     NO_FCNTL_H
  76.  
  77. #include <fcntl.h>
  78. #define     CLOSE_ON_EXEC(fd)    fcntl(fd, F_SETFD, 1)
  79.  
  80. #else
  81. #define     CLOSE_ON_EXEC(fd) ioctl(fd, FIOCLEX, (PTR) 0)
  82. #endif
  83.  
  84.  
  85. /* We store dynamically created files on a linked linear
  86.    list with move to the front (big surprise)  */
  87.  
  88. typedef struct file
  89. {
  90.    struct file *link ;
  91.    STRING *name ;
  92.    short type ;
  93.    int pid ;             /* we need to wait() when we close a pipe */
  94.    /* holds temp file index under MSDOS */
  95.  
  96. #if  HAVE_FAKE_PIPES
  97.    int inpipe_exit ;
  98. #endif
  99.  
  100.    PTR ptr ;             /* FIN*   or  FILE*   */
  101. }
  102. FILE_NODE ;
  103.  
  104. static FILE_NODE *file_list ;
  105.  
  106.  
  107. /* find a file on file_list */
  108. PTR
  109. file_find(sval, type)
  110.    STRING *sval ;
  111.    int type ;
  112. {
  113.    register FILE_NODE *p = file_list ;
  114.    FILE_NODE *q = (FILE_NODE *) 0 ;
  115.    char *name = sval->str ;
  116.    char *ostr ;
  117.  
  118.    while (1)
  119.    {
  120.       if (!p)
  121.       {
  122.      /* open a new one */
  123.      p = ZMALLOC(FILE_NODE) ;
  124.  
  125.      switch (p->type = type)
  126.      {
  127.         case F_TRUNC:
  128. #if MSDOS
  129.            ostr = (binmode() & 2) ? "wb" : "w" ;
  130. #else
  131.            ostr = "w" ;
  132. #endif
  133.            if (!(p->ptr = (PTR) tfopen(name, ostr)))
  134.           goto out_failure ;
  135.            break ;
  136.  
  137.         case F_APPEND:
  138. #if MSDOS
  139.            ostr = (binmode() & 2) ? "ab" : "a" ;
  140. #else
  141.            ostr = "a" ;
  142. #endif
  143.            if (!(p->ptr = (PTR) tfopen(name, ostr)))
  144.           goto out_failure ;
  145.            break ;
  146.  
  147.         case F_IN:
  148.            if (!(p->ptr = (PTR) FINopen(name, 0)))
  149.            {
  150.           zfree(p, sizeof(FILE_NODE)) ;
  151.           return (PTR) 0 ;
  152.            }
  153.            break ;
  154.  
  155.         case PIPE_OUT:
  156.         case PIPE_IN:
  157.  
  158. #if    HAVE_REAL_PIPES || HAVE_FAKE_PIPES
  159.  
  160.            if (!(p->ptr = get_pipe(name, type, &p->pid)))
  161.            {
  162.           if (type == PIPE_OUT)     goto out_failure ;
  163.           else
  164.           {
  165.              zfree(p, sizeof(FILE_NODE)) ;
  166.              return (PTR) 0 ;
  167.           }
  168.            }
  169. #else
  170.            rt_error("pipes not supported") ;
  171. #endif
  172.            break ;
  173.  
  174. #ifdef    DEBUG
  175.         default:
  176.            bozo("bad file type") ;
  177. #endif
  178.      }
  179.      /* successful open */
  180.      p->name = sval ;
  181.      sval->ref_cnt++ ;
  182.      break ;         /* while loop */
  183.       }
  184.  
  185.       /* search is by name and type */
  186.       if (strcmp(name, p->name->str) == 0 &&
  187.       (p->type == type ||
  188.       /* no distinction between F_APPEND and F_TRUNC here */
  189.        p->type >= F_APPEND && type >= F_APPEND))
  190.  
  191.       {
  192.      /* found */
  193.      if (!q)        /*at front of list */
  194.         return p->ptr ;
  195.      /* delete from list for move to front */
  196.      q->link = p->link ;
  197.      break ;         /* while loop */
  198.       }
  199.  
  200.       q = p ; p = p->link ;
  201.    }                /* end while loop */
  202.  
  203.    /* put p at the front of the list */
  204.    p->link = file_list ;
  205.    return (PTR) (file_list = p)->ptr ;
  206.  
  207. out_failure:
  208.    errmsg(errno, "cannot open \"%s\" for output", name) ;
  209.    mawk_exit(2) ;
  210.  
  211. }
  212.  
  213.  
  214. /* Close a file and delete it's node from the file_list.
  215.    Walk the whole list, in case a name has two nodes,
  216.    e.g. < "/dev/tty" and > "/dev/tty"
  217. */
  218.  
  219. int
  220. file_close(sval)
  221.    STRING *sval ;
  222. {
  223.    FILE_NODE dummy ;
  224.    register FILE_NODE *p ;
  225.    FILE_NODE *q = &dummy ;     /* trails p */
  226.    FILE_NODE *hold ;
  227.    char *name = sval->str ;
  228.    int retval = -1 ;
  229.  
  230.    dummy.link = p = file_list ;
  231.    while (p)
  232.    {
  233.       if (strcmp(name, p->name->str) == 0)
  234.       {
  235.      /* found */
  236.      switch (p->type)
  237.      {
  238.         case F_TRUNC:
  239.         case F_APPEND:
  240.            fclose((FILE *) p->ptr) ;
  241.            retval = 0 ;
  242.            break ;
  243.  
  244.         case PIPE_OUT:
  245.            fclose((FILE *) p->ptr) ;
  246.  
  247. #if  HAVE_REAL_PIPES
  248.            retval = wait_for(p->pid) ;
  249. #endif
  250. #if  HAVE_FAKE_PIPES
  251.            retval = close_fake_outpipe(p->name->str, p->pid) ;
  252. #endif
  253.            break ;
  254.  
  255.         case F_IN:
  256.            FINclose((FIN *) p->ptr) ;
  257.            retval = 0 ;
  258.            break ;
  259.  
  260.         case PIPE_IN:
  261.            FINclose((FIN *) p->ptr) ;
  262.  
  263. #if  HAVE_REAL_PIPES
  264.            retval = wait_for(p->pid) ;
  265. #endif
  266. #if  HAVE_FAKE_PIPES
  267.            {
  268.           char xbuff[100] ;
  269.           unlink(tmp_file_name(p->pid, xbuff)) ;
  270.           retval = p->inpipe_exit ;
  271.            }
  272. #endif
  273.            break ;
  274.      }
  275.  
  276.      free_STRING(p->name) ;
  277.      hold = p ;
  278.      q->link = p = p->link ;
  279.      ZFREE(hold) ;
  280.       }
  281.       else
  282.       {
  283.      q = p ; p = p->link ; 
  284.       }
  285.    }
  286.  
  287.    file_list = dummy.link ;
  288.    return retval ;
  289. }
  290.  
  291. /*
  292. find an output file with name == sval and fflush it
  293. */
  294.  
  295. int
  296. file_flush(sval)
  297.    STRING *sval ;
  298. {
  299.    int ret = -1 ;
  300.    register FILE_NODE *p = file_list ;
  301.    unsigned len = sval->len ;
  302.    char *str = sval->str ;
  303.  
  304.    if (len==0) 
  305.    {
  306.       /* for consistency with gawk */
  307.       flush_all_output() ;
  308.       return 0 ;
  309.    }
  310.       
  311.    while( p )
  312.    {
  313.       if ( IS_OUTPUT(p->type) &&
  314.        len == p->name->len &&
  315.        strcmp(str,p->name->str) == 0 )
  316.       {
  317.      ret = 0 ;
  318.      efflush((FILE*)p->ptr) ;
  319.          /* it's possible for a command and a file to have the same
  320.         name -- so keep looking */
  321.       }
  322.       p = p->link ;
  323.    }
  324.    return ret ;
  325. }
  326.  
  327. void
  328. flush_all_output() 
  329. {
  330.    FILE_NODE *p ;
  331.  
  332.    for(p=file_list; p ; p = p->link)
  333.       if (IS_OUTPUT(p->type)) efflush((FILE*)p->ptr) ;
  334. }
  335.  
  336. static void
  337. efflush(fp)
  338.    FILE *fp ;
  339. {
  340.    if (fflush(fp) < 0)
  341.    {
  342.       errmsg(errno, "unexpected write error") ;
  343.       mawk_exit(2) ;
  344.    }
  345. }
  346.  
  347.  
  348. /* When we exit, we need to close and wait for all output pipes */
  349.  
  350. #if   HAVE_REAL_PIPES
  351.  
  352. /* work around for bug in AIX 4.1 -- If there are exactly 16 or 
  353.    32 or 48 ..., open files then the last one doesn't get flushed on
  354.    exit.  So the following is now a misnomer as we'll really close
  355.    all output.
  356. */
  357.  
  358. void
  359. close_out_pipes()
  360. {
  361.    register FILE_NODE *p = file_list ;
  362.  
  363.    while (p)
  364.    {
  365.       if (IS_OUTPUT(p->type))
  366.       {
  367.      fclose((FILE *) p->ptr) ;   
  368.      if (p->type == PIPE_OUT) wait_for(p->pid) ; 
  369.       }
  370.  
  371.       p = p->link ;
  372.    }
  373. }
  374.  
  375. #else
  376. #if  HAVE_FAKE_PIPES        /* pipes are faked with temp files */
  377.  
  378. void
  379. close_fake_pipes()
  380. {
  381.    register FILE_NODE *p = file_list ;
  382.    char xbuff[100] ;
  383.  
  384.    /* close input pipes first to free descriptors for children */
  385.    while (p)
  386.    {
  387.       if (p->type == PIPE_IN)
  388.       {
  389.      FINclose((FIN *) p->ptr) ;
  390.      unlink(tmp_file_name(p->pid, xbuff)) ;
  391.       }
  392.       p = p->link ;
  393.    }
  394.    /* doit again */
  395.    p = file_list ;
  396.    while (p)
  397.    {
  398.       if (p->type == PIPE_OUT)
  399.       {
  400.      fclose(p->ptr) ;
  401.      close_fake_outpipe(p->name->str, p->pid) ;
  402.       }
  403.       p = p->link ;
  404.    }
  405. }
  406. #endif /* HAVE_FAKE_PIPES */
  407. #endif /* ! HAVE_REAL_PIPES */
  408.  
  409. /* hardwire to /bin/sh for portability of programs */
  410. char *shell = "/bin/sh" ;
  411.  
  412. #if  HAVE_REAL_PIPES
  413.  
  414. PTR
  415. get_pipe(name, type, pid_ptr)
  416.    char *name ;
  417.    int type ;
  418.    int *pid_ptr ;
  419. {
  420.    int the_pipe[2], local_fd, remote_fd ;
  421.  
  422.    if (pipe(the_pipe) == -1)  return (PTR) 0 ;
  423.    local_fd = the_pipe[type == PIPE_OUT] ;
  424.    remote_fd = the_pipe[type == PIPE_IN] ;
  425.    /* to keep output ordered correctly */
  426.    fflush(stdout) ; fflush(stderr) ;
  427.  
  428.    switch (*pid_ptr = fork())
  429.    {
  430.       case -1:
  431.      close(local_fd) ;
  432.      close(remote_fd) ;
  433.      return (PTR) 0 ;
  434.  
  435.       case 0:
  436.      close(local_fd) ;
  437.      close(type == PIPE_IN) ;
  438.      dup(remote_fd) ;
  439.      close(remote_fd) ;
  440.      execl(shell, shell, "-c", name, (char *) 0) ;
  441.      errmsg(errno, "failed to exec %s -c %s", shell, name) ;
  442.      fflush(stderr) ;
  443.      _exit(128) ;
  444.  
  445.       default:
  446.      close(remote_fd) ;
  447.      /* we could deadlock if future child inherit the local fd ,
  448.        set close on exec flag */
  449.      CLOSE_ON_EXEC(local_fd) ;
  450.      break ;
  451.    }
  452.  
  453.    return type == PIPE_IN ? (PTR) FINdopen(local_fd, 0) :
  454.       (PTR) fdopen(local_fd, "w") ;
  455. }
  456.  
  457.  
  458.  
  459. /*------------ children ------------------*/
  460.  
  461. /* we need to wait for children at the end of output pipes to
  462.    complete so we know any files they have created are complete */
  463.  
  464. /* dead children are kept on this list */
  465.  
  466. static struct child
  467. {
  468.    int pid ;
  469.    int exit_status ;
  470.    struct child *link ;
  471. } *child_list ;
  472.  
  473. static void
  474. add_to_child_list(pid, exit_status)
  475.    int pid, exit_status ;
  476. {
  477.    register struct child *p = ZMALLOC(struct child) ;
  478.  
  479.    p->pid = pid ; p->exit_status = exit_status ;
  480.    p->link = child_list ; child_list = p ;
  481. }
  482.  
  483. static struct child *
  484. remove_from_child_list(pid)
  485.    int pid ;
  486. {
  487.    struct child dummy ;
  488.    register struct child *p ;
  489.    struct child *q = &dummy ;
  490.  
  491.    dummy.link = p = child_list ;
  492.    while (p)
  493.    {
  494.       if (p->pid == pid)
  495.       {
  496.      q->link = p->link ;
  497.      break ;
  498.       }
  499.       else
  500.       {
  501.      q = p ; p = p->link ; 
  502.       }
  503.    }
  504.  
  505.    child_list = dummy.link ;
  506.    return p ;    
  507.    /* null return if not in the list */
  508. }
  509.  
  510.  
  511. /* wait for a specific child to complete and return its
  512.    exit status
  513.  
  514.    If pid is zero, wait for any single child and
  515.    put it on the dead children list
  516. */
  517.  
  518. int
  519. wait_for(pid)
  520.    int pid ;
  521. {
  522.    int exit_status ;
  523.    struct child *p ;
  524.    int id ;
  525.  
  526.    if (pid == 0)
  527.    {
  528.       id = wait(&exit_status) ;
  529.       add_to_child_list(id, exit_status) ;
  530.    }
  531.    /* see if an earlier wait() caught our child */
  532.    else if (p = remove_from_child_list(pid))
  533.    {
  534.       exit_status = p->exit_status ;
  535.       ZFREE(p) ;
  536.    }
  537.    else
  538.    {
  539.       /* need to really wait */
  540.       while ((id = wait(&exit_status)) != pid)
  541.       {
  542.      if (id == -1)        /* can't happen */
  543.         bozo("wait_for") ;
  544.      else
  545.      {
  546.         /* we got the exit status of another child
  547.         put it on the child list and try again */
  548.         add_to_child_list(id, exit_status) ;
  549.      }
  550.       }
  551.    }
  552.  
  553.    if (exit_status & 0xff)  exit_status = 128 + (exit_status & 0xff) ;
  554.    else     exit_status = (exit_status & 0xff00) >> 8 ;
  555.  
  556.    return exit_status ;
  557. }
  558.  
  559. #endif /* HAVE_REAL_PIPES */
  560.  
  561.  
  562. void
  563. set_stderr()   /* and stdout */
  564. {
  565.    FILE_NODE *p, *q ; 
  566.    
  567.    p = ZMALLOC(FILE_NODE) ;
  568.    p->link = (FILE_NODE*) 0 ;
  569.    p->type = F_TRUNC ;
  570.    p->name = new_STRING("/dev/stdout") ;
  571.    p->ptr = (PTR) stdout ;
  572.    q = ZMALLOC(FILE_NODE);
  573.    q->link = p ;
  574.    q->type = F_TRUNC ;
  575.    q->name = new_STRING("/dev/stderr") ;
  576.    q->ptr = (PTR) stderr ;
  577.    file_list = q ;
  578. }
  579.  
  580. /* fopen() but no buffering to ttys */
  581. static FILE *
  582. tfopen(name, mode)
  583.    char *name, *mode ;
  584. {
  585.    FILE *retval = fopen(name, mode) ;
  586.  
  587.    if (retval)
  588.    {
  589.       if (isatty(fileno(retval)))  setbuf(retval, (char *) 0) ;
  590.       else
  591.       {
  592. #ifdef MSDOS
  593.      enlarge_output_buffer(retval) ;
  594. #endif
  595.       }
  596.    }
  597.    return retval ;
  598. }
  599.  
  600. #ifdef  MSDOS
  601. void
  602. enlarge_output_buffer(fp)
  603.    FILE *fp ;
  604. {
  605.    if (setvbuf(fp, (char *) 0, _IOFBF, BUFFSZ) < 0)
  606.    {
  607.       errmsg(errno, "setvbuf failed on fileno %d", fileno(fp)) ;
  608.       mawk_exit(2) ;
  609.    }
  610. }
  611.  
  612. void
  613. stdout_init()
  614. {
  615.    if (!isatty(1))  enlarge_output_buffer(stdout) ;
  616.    if (binmode() & 2)
  617.    {
  618.       setmode(1,O_BINARY) ; setmode(2,O_BINARY) ; 
  619.    }
  620. }
  621. #endif /* MSDOS */
  622.