(root)/
gettext-0.22.4/
libtextstyle/
gnulib-local/
lib/
fd-ostream.oo.c
       1  /* Output stream referring to a file descriptor.
       2     Copyright (C) 2006-2007, 2019-2020 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2006.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "fd-ostream.h"
      22  
      23  #include <assert.h>
      24  #include <errno.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <unistd.h>
      28  #if HAVE_TCDRAIN
      29  # include <termios.h>
      30  #endif
      31  
      32  #include "error.h"
      33  #include "full-write.h"
      34  #include "xalloc.h"
      35  #include "gettext.h"
      36  
      37  #define _(str) gettext (str)
      38  
      39  struct fd_ostream : struct ostream
      40  {
      41  fields:
      42    int fd;
      43    char *filename;
      44    char *buffer;                 /* A buffer, or NULL.  */
      45    size_t avail;                 /* Number of bytes available in the buffer.  */
      46  };
      47  
      48  #define BUFSIZE 4096
      49  
      50  #if HAVE_TCDRAIN
      51  
      52  /* EINTR handling for tcdrain().
      53     This function can return -1/EINTR even though we don't have any
      54     signal handlers set up, namely when we get interrupted via SIGSTOP.  */
      55  
      56  static inline int
      57  nonintr_tcdrain (int fd)
      58  {
      59    int retval;
      60  
      61    do
      62      retval = tcdrain (fd);
      63    while (retval < 0 && errno == EINTR);
      64  
      65    return retval;
      66  }
      67  
      68  #endif
      69  
      70  /* Implementation of ostream_t methods.  */
      71  
      72  static void
      73  fd_ostream::write_mem (fd_ostream_t stream, const void *data, size_t len)
      74  {
      75    if (len > 0)
      76      {
      77        if (stream->buffer != NULL)
      78          {
      79            /* Buffered.  */
      80            assert (stream->avail > 0);
      81            #if 0 /* unoptimized */
      82            do
      83              {
      84                size_t n = (len <= stream->avail ? len : stream->avail);
      85                if (n > 0)
      86                  {
      87                    memcpy (stream->buffer + BUFSIZE - stream->avail, data, n);
      88                    data = (const char *) data + n;
      89                    stream->avail -= n;
      90                    len -= n;
      91                  }
      92                if (stream->avail == 0)
      93                  {
      94                    if (full_write (stream->fd, stream->buffer, BUFSIZE) < BUFSIZE)
      95                      error (EXIT_FAILURE, errno, _("error writing to %s"),
      96                             stream->filename);
      97                    stream->avail = BUFSIZE;
      98                  }
      99              }
     100            while (len > 0);
     101            #else /* optimized */
     102            if (len < stream->avail)
     103              {
     104                /* Move the data into the buffer.  */
     105                memcpy (stream->buffer + BUFSIZE - stream->avail, data, len);
     106                stream->avail -= len;
     107              }
     108            else
     109              {
     110                /* Split the data into:
     111                     - a first chunk, which is added to the buffer and output,
     112                     - a series of chunks of size BUFSIZE, which can be output
     113                       directly, without going through the buffer, and
     114                     - a last chunk, which is copied to the buffer.  */
     115                size_t n = stream->avail;
     116                memcpy (stream->buffer + BUFSIZE - stream->avail, data, n);
     117                data = (const char *) data + n;
     118                len -= n;
     119                if (full_write (stream->fd, stream->buffer, BUFSIZE) < BUFSIZE)
     120                  error (EXIT_FAILURE, errno, _("error writing to %s"),
     121                         stream->filename);
     122  
     123                while (len >= BUFSIZE)
     124                  {
     125                    if (full_write (stream->fd, data, BUFSIZE) < BUFSIZE)
     126                      error (EXIT_FAILURE, errno, _("error writing to %s"),
     127                             stream->filename);
     128                    data = (const char *) data + BUFSIZE;
     129                    len -= BUFSIZE;
     130                  }
     131  
     132                if (len > 0)
     133                  memcpy (stream->buffer, data, len);
     134                stream->avail = BUFSIZE - len;
     135              }
     136            #endif
     137            assert (stream->avail > 0);
     138          }
     139        else
     140          {
     141            /* Unbuffered.  */
     142            if (full_write (stream->fd, data, len) < len)
     143              error (EXIT_FAILURE, errno, _("error writing to %s"),
     144                     stream->filename);
     145          }
     146      }
     147  }
     148  
     149  static void
     150  fd_ostream::flush (fd_ostream_t stream, ostream_flush_scope_t scope)
     151  {
     152    if (stream->buffer != NULL && stream->avail < BUFSIZE)
     153      {
     154        size_t filled = BUFSIZE - stream->avail;
     155        if (full_write (stream->fd, stream->buffer, filled) < filled)
     156          error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename);
     157        stream->avail = BUFSIZE;
     158      }
     159    if (scope == FLUSH_ALL)
     160      {
     161        /* For streams connected to a disk file:  */
     162        fsync (stream->fd);
     163        #if HAVE_TCDRAIN
     164        /* For streams connected to a terminal:  */
     165        nonintr_tcdrain (stream->fd);
     166        #endif
     167      }
     168  }
     169  
     170  static void
     171  fd_ostream::free (fd_ostream_t stream)
     172  {
     173    fd_ostream_flush (stream, FLUSH_THIS_STREAM);
     174    free (stream->filename);
     175    free (stream);
     176  }
     177  
     178  /* Constructor.  */
     179  
     180  fd_ostream_t
     181  fd_ostream_create (int fd, const char *filename, bool buffered)
     182  {
     183    fd_ostream_t stream =
     184      (struct fd_ostream_representation *)
     185      xmalloc (sizeof (struct fd_ostream_representation)
     186               + (buffered ? BUFSIZE : 0));
     187  
     188    stream->base.vtable = &fd_ostream_vtable;
     189    stream->fd = fd;
     190    stream->filename = xstrdup (filename);
     191    if (buffered)
     192      {
     193        stream->buffer =
     194          (char *) (void *) stream + sizeof (struct fd_ostream_representation);
     195        stream->avail = BUFSIZE;
     196      }
     197    else
     198      stream->buffer = NULL;
     199  
     200    return stream;
     201  }
     202  
     203  /* Accessors.  */
     204  
     205  static int
     206  fd_ostream::get_descriptor (fd_ostream_t stream)
     207  {
     208    return stream->fd;
     209  }
     210  
     211  static const char *
     212  fd_ostream::get_filename (fd_ostream_t stream)
     213  {
     214    return stream->filename;
     215  }
     216  
     217  static bool
     218  fd_ostream::is_buffered (fd_ostream_t stream)
     219  {
     220    return stream->buffer != NULL;
     221  }
     222  
     223  /* Instanceof test.  */
     224  
     225  bool
     226  is_instance_of_fd_ostream (ostream_t stream)
     227  {
     228    return IS_INSTANCE (stream, ostream, fd_ostream);
     229  }