1/* Output stream referring to a file descriptor. 2 Copyright (C) 2006-2007 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 <http://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 28#include "error.h" 29#include "full-write.h" 30#include "xalloc.h" 31#include "gettext.h" 32 33#define _(str) gettext (str) 34 35struct fd_ostream : struct ostream 36{ 37fields: 38 int fd; 39 char *filename; 40 char *buffer; /* A buffer, or NULL. */ 41 size_t avail; /* Number of bytes available in the buffer. */ 42}; 43 44#define BUFSIZE 4096 45 46/* Implementation of ostream_t methods. */ 47 48static void 49fd_ostream::write_mem (fd_ostream_t stream, const void *data, size_t len) 50{ 51 if (len > 0) 52 { 53 if (stream->buffer != NULL) 54 { 55 /* Buffered. */ 56 assert (stream->avail > 0); 57 #if 0 /* unoptimized */ 58 do 59 { 60 size_t n = (len <= stream->avail ? len : stream->avail); 61 if (n > 0) 62 { 63 memcpy (stream->buffer + BUFSIZE - stream->avail, data, n); 64 data = (char *) data + n; 65 stream->avail -= n; 66 len -= n; 67 } 68 if (stream->avail == 0) 69 { 70 if (full_write (stream->fd, stream->buffer, BUFSIZE) < BUFSIZE) 71 error (EXIT_FAILURE, errno, _("error writing to %s"), 72 stream->filename); 73 stream->avail = BUFSIZE; 74 } 75 } 76 while (len > 0); 77 #else /* optimized */ 78 if (len < stream->avail) 79 { 80 /* Move the data into the buffer. */ 81 memcpy (stream->buffer + BUFSIZE - stream->avail, data, len); 82 stream->avail -= len; 83 } 84 else 85 { 86 /* Split the data into: 87 - a first chunk, which is added to the buffer and output, 88 - a series of chunks of size BUFSIZE, which can be output 89 directly, without going through the buffer, and 90 - a last chunk, which is copied to the buffer. */ 91 size_t n = stream->avail; 92 memcpy (stream->buffer + BUFSIZE - stream->avail, data, n); 93 data = (char *) data + n; 94 len -= n; 95 if (full_write (stream->fd, stream->buffer, BUFSIZE) < BUFSIZE) 96 error (EXIT_FAILURE, errno, _("error writing to %s"), 97 stream->filename); 98 99 while (len >= BUFSIZE) 100 { 101 if (full_write (stream->fd, data, BUFSIZE) < BUFSIZE) 102 error (EXIT_FAILURE, errno, _("error writing to %s"), 103 stream->filename); 104 data = (char *) data + BUFSIZE; 105 len -= BUFSIZE; 106 } 107 108 if (len > 0) 109 memcpy (stream->buffer, data, len); 110 stream->avail = BUFSIZE - len; 111 } 112 #endif 113 assert (stream->avail > 0); 114 } 115 else 116 { 117 /* Unbuffered. */ 118 if (full_write (stream->fd, data, len) < len) 119 error (EXIT_FAILURE, errno, _("error writing to %s"), 120 stream->filename); 121 } 122 } 123} 124 125static void 126fd_ostream::flush (fd_ostream_t stream) 127{ 128 if (stream->buffer != NULL && stream->avail < BUFSIZE) 129 { 130 size_t filled = BUFSIZE - stream->avail; 131 if (full_write (stream->fd, stream->buffer, filled) < filled) 132 error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename); 133 stream->avail = BUFSIZE; 134 } 135} 136 137static void 138fd_ostream::free (fd_ostream_t stream) 139{ 140 fd_ostream_flush (stream); 141 free (stream->filename); 142 free (stream); 143} 144 145/* Constructor. */ 146 147fd_ostream_t 148fd_ostream_create (int fd, const char *filename, bool buffered) 149{ 150 fd_ostream_t stream = 151 (struct fd_ostream_representation *) 152 xmalloc (sizeof (struct fd_ostream_representation) 153 + (buffered ? BUFSIZE : 0)); 154 155 stream->base.vtable = &fd_ostream_vtable; 156 stream->fd = fd; 157 stream->filename = xstrdup (filename); 158 if (buffered) 159 { 160 stream->buffer = 161 (char *) (void *) stream + sizeof (struct fd_ostream_representation); 162 stream->avail = BUFSIZE; 163 } 164 else 165 stream->buffer = NULL; 166 167 return stream; 168} 169