1 /**
2 * \file output.c
3 * \brief Generic stdio-like output interface
4 * \author Abramo Bagnara <abramo@alsa-project.org>
5 * \date 2000
6 *
7 * Generic stdio-like output interface
8 */
9 /*
10 * Output object
11 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12 *
13 *
14 * This library is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License as
16 * published by the Free Software Foundation; either version 2.1 of
17 * the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 *
28 */
29
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include "local.h"
35
36 #ifndef DOC_HIDDEN
37 typedef struct _snd_output_ops {
38 int (*close)(snd_output_t *output);
39 int (*print)(snd_output_t *output, const char *format, va_list args);
40 int (*puts)(snd_output_t *output, const char *str);
41 int (*putch)(snd_output_t *output, int c);
42 int (*flush)(snd_output_t *output);
43 } snd_output_ops_t;
44
45 struct _snd_output {
46 snd_output_type_t type;
47 const snd_output_ops_t *ops;
48 void *private_data;
49 };
50 #endif
51
52 /**
53 * \brief Closes an output handle.
54 * \param output The output handle to be closed.
55 * \return Zero if successful, otherwise a negative error code.
56 */
snd_output_close(snd_output_t * output)57 int snd_output_close(snd_output_t *output)
58 {
59 int err = output->ops->close(output);
60 free(output);
61 return err;
62 }
63
64 /**
65 * \brief Writes formatted output (like \c fprintf(3)) to an output handle.
66 * \param output The output handle.
67 * \param format Format string in \c fprintf format.
68 * \param ... Other \c fprintf arguments.
69 * \return The number of characters written, or a negative error code.
70 */
snd_output_printf(snd_output_t * output,const char * format,...)71 int snd_output_printf(snd_output_t *output, const char *format, ...)
72 {
73 int result;
74 va_list args;
75 va_start(args, format);
76 result = output->ops->print(output, format, args);
77 va_end(args);
78 return result;
79 }
80
81 /**
82 * \brief Writes formatted output (like \c fprintf(3)) to an output handle.
83 * \param output The output handle.
84 * \param format Format string in \c fprintf format.
85 * \param args Other \c fprintf arguments.
86 * \return The number of characters written, or a negative error code.
87 */
snd_output_vprintf(snd_output_t * output,const char * format,va_list args)88 int snd_output_vprintf(snd_output_t *output, const char *format, va_list args)
89 {
90 return output->ops->print(output, format, args);
91 }
92
93 /**
94 * \brief Writes a string to an output handle (like \c fputs(3)).
95 * \param output The output handle.
96 * \param str Pointer to the string.
97 * \return Zero if successful, otherwise a negative error code or \c EOF.
98 */
snd_output_puts(snd_output_t * output,const char * str)99 int snd_output_puts(snd_output_t *output, const char *str)
100 {
101 return output->ops->puts(output, str);
102 }
103
104 /**
105 * \brief Writes a character to an output handle (like \c putc(3)).
106 * \param output The output handle.
107 * \param c The character.
108 * \return Zero if successful, otherwise a negative error code or \c EOF.
109 */
snd_output_putc(snd_output_t * output,int c)110 int snd_output_putc(snd_output_t *output, int c)
111 {
112 return output->ops->putch(output, c);
113 }
114
115 /**
116 * \brief Flushes an output handle (like fflush(3)).
117 * \param output The output handle.
118 * \return Zero if successful, otherwise \c EOF.
119 *
120 * If the underlying destination is a stdio stream, this function calls
121 * \c fflush. If the underlying destination is a memory buffer, the write
122 * position is reset to the beginning of the buffer. \c =:-o
123 */
snd_output_flush(snd_output_t * output)124 int snd_output_flush(snd_output_t *output)
125 {
126 return output->ops->flush(output);
127 }
128
129 #ifndef DOC_HIDDEN
130 typedef struct _snd_output_stdio {
131 int close;
132 FILE *fp;
133 } snd_output_stdio_t;
134
snd_output_stdio_close(snd_output_t * output)135 static int snd_output_stdio_close(snd_output_t *output)
136 {
137 snd_output_stdio_t *stdio = output->private_data;
138 if (stdio->close)
139 fclose(stdio->fp);
140 free(stdio);
141 return 0;
142 }
143
snd_output_stdio_print(snd_output_t * output,const char * format,va_list args)144 static int snd_output_stdio_print(snd_output_t *output, const char *format, va_list args)
145 {
146 snd_output_stdio_t *stdio = output->private_data;
147 return vfprintf(stdio->fp, format, args);
148 }
149
snd_output_stdio_puts(snd_output_t * output,const char * str)150 static int snd_output_stdio_puts(snd_output_t *output, const char *str)
151 {
152 snd_output_stdio_t *stdio = output->private_data;
153 return fputs(str, stdio->fp);
154 }
155
snd_output_stdio_putc(snd_output_t * output,int c)156 static int snd_output_stdio_putc(snd_output_t *output, int c)
157 {
158 snd_output_stdio_t *stdio = output->private_data;
159 return putc(c, stdio->fp);
160 }
161
snd_output_stdio_flush(snd_output_t * output)162 static int snd_output_stdio_flush(snd_output_t *output)
163 {
164 snd_output_stdio_t *stdio = output->private_data;
165 return fflush(stdio->fp);
166 }
167
168 static const snd_output_ops_t snd_output_stdio_ops = {
169 .close = snd_output_stdio_close,
170 .print = snd_output_stdio_print,
171 .puts = snd_output_stdio_puts,
172 .putch = snd_output_stdio_putc,
173 .flush = snd_output_stdio_flush,
174 };
175
176 #endif
177
178 /**
179 * \brief Creates a new output object using an existing stdio \c FILE pointer.
180 * \param outputp The function puts the pointer to the new output object
181 * at the address specified by \p outputp.
182 * \param fp The \c FILE pointer to write to. Characters are written
183 * to the file starting at the current file position.
184 * \param _close Close flag. Set this to 1 if #snd_output_close should close
185 * \p fp by calling \c fclose.
186 * \return Zero if successful, otherwise a negative error code.
187 */
snd_output_stdio_attach(snd_output_t ** outputp,FILE * fp,int _close)188 int snd_output_stdio_attach(snd_output_t **outputp, FILE *fp, int _close)
189 {
190 snd_output_t *output;
191 snd_output_stdio_t *stdio;
192 assert(outputp && fp);
193 stdio = calloc(1, sizeof(*stdio));
194 if (!stdio)
195 return -ENOMEM;
196 output = calloc(1, sizeof(*output));
197 if (!output) {
198 free(stdio);
199 return -ENOMEM;
200 }
201 stdio->fp = fp;
202 stdio->close = _close;
203 output->type = SND_OUTPUT_STDIO;
204 output->ops = &snd_output_stdio_ops;
205 output->private_data = stdio;
206 *outputp = output;
207 return 0;
208 }
209
210 /**
211 * \brief Creates a new output object writing to a file.
212 * \param outputp The function puts the pointer to the new output object
213 * at the address specified by \p outputp.
214 * \param file The name of the file to open.
215 * \param mode The open mode, like \c fopen(3).
216 * \return Zero if successful, otherwise a negative error code.
217 */
snd_output_stdio_open(snd_output_t ** outputp,const char * file,const char * mode)218 int snd_output_stdio_open(snd_output_t **outputp, const char *file, const char *mode)
219 {
220 int err;
221 FILE *fp = fopen(file, mode);
222 if (!fp) {
223 //SYSERR("fopen");
224 return -errno;
225 }
226 err = snd_output_stdio_attach(outputp, fp, 1);
227 if (err < 0)
228 fclose(fp);
229 return err;
230 }
231
232 #ifndef DOC_HIDDEN
233
234 typedef struct _snd_output_buffer {
235 unsigned char *buf;
236 size_t alloc;
237 size_t size;
238 } snd_output_buffer_t;
239
snd_output_buffer_close(snd_output_t * output)240 static int snd_output_buffer_close(snd_output_t *output)
241 {
242 snd_output_buffer_t *buffer = output->private_data;
243 free(buffer->buf);
244 free(buffer);
245 return 0;
246 }
247
snd_output_buffer_need(snd_output_t * output,size_t size)248 static int snd_output_buffer_need(snd_output_t *output, size_t size)
249 {
250 snd_output_buffer_t *buffer = output->private_data;
251 size_t _free = buffer->alloc - buffer->size;
252 size_t alloc;
253 unsigned char *buf;
254
255 /* use 'size++' to allow to add the '\0' string terminator */
256 /* without reallocation */
257 size++;
258 if (_free >= size)
259 return _free;
260 if (buffer->alloc == 0)
261 alloc = 256;
262 else
263 alloc = buffer->alloc;
264 while (alloc < buffer->size + size)
265 alloc *= 2;
266 buf = realloc(buffer->buf, alloc);
267 if (!buf)
268 return -ENOMEM;
269 buffer->buf = buf;
270 buffer->alloc = alloc;
271 return buffer->alloc - buffer->size;
272 }
273
snd_output_buffer_print(snd_output_t * output,const char * format,va_list args)274 static int snd_output_buffer_print(snd_output_t *output, const char *format, va_list args)
275 {
276 snd_output_buffer_t *buffer = output->private_data;
277 size_t size = 256;
278 int result;
279 result = snd_output_buffer_need(output, size);
280 if (result < 0)
281 return result;
282 result = vsnprintf((char *)buffer->buf + buffer->size, size, format, args);
283 assert(result >= 0);
284 if ((size_t)result <= size) {
285 buffer->size += result;
286 return result;
287 }
288 size = result;
289 result = snd_output_buffer_need(output, size);
290 if (result < 0)
291 return result;
292 result = vsnprintf((char *)buffer->buf + buffer->size, result, format, args);
293 assert(result == (int)size);
294 buffer->size += result;
295 return result;
296 }
297
snd_output_buffer_puts(snd_output_t * output,const char * str)298 static int snd_output_buffer_puts(snd_output_t *output, const char *str)
299 {
300 snd_output_buffer_t *buffer = output->private_data;
301 size_t size = strlen(str);
302 int err;
303 err = snd_output_buffer_need(output, size);
304 if (err < 0)
305 return err;
306 memcpy(buffer->buf + buffer->size, str, size);
307 buffer->size += size;
308 return size;
309 }
310
snd_output_buffer_putc(snd_output_t * output,int c)311 static int snd_output_buffer_putc(snd_output_t *output, int c)
312 {
313 snd_output_buffer_t *buffer = output->private_data;
314 int err;
315 err = snd_output_buffer_need(output, 1);
316 if (err < 0)
317 return err;
318 buffer->buf[buffer->size++] = c;
319 return 0;
320 }
321
snd_output_buffer_flush(snd_output_t * output ATTRIBUTE_UNUSED)322 static int snd_output_buffer_flush(snd_output_t *output ATTRIBUTE_UNUSED)
323 {
324 snd_output_buffer_t *buffer = output->private_data;
325 buffer->size = 0;
326 return 0;
327 }
328
329 static const snd_output_ops_t snd_output_buffer_ops = {
330 .close = snd_output_buffer_close,
331 .print = snd_output_buffer_print,
332 .puts = snd_output_buffer_puts,
333 .putch = snd_output_buffer_putc,
334 .flush = snd_output_buffer_flush,
335 };
336 #endif
337
338 /**
339 * \brief Returns the address of the buffer of a #SND_OUTPUT_BUFFER output handle.
340 * \param output The output handle.
341 * \param buf The functions puts the current address of the buffer at the
342 * address specified by \p buf.
343 * \return The current size of valid data in the buffer.
344 *
345 * The address of the buffer may become invalid when output functions or
346 * #snd_output_close are called.
347 */
snd_output_buffer_string(snd_output_t * output,char ** buf)348 size_t snd_output_buffer_string(snd_output_t *output, char **buf)
349 {
350 snd_output_buffer_t *buffer = output->private_data;
351 *buf = (char *)buffer->buf;
352 return buffer->size;
353 }
354
355 /**
356 * \brief Returns the address of the buffer of a #SND_OUTPUT_BUFFER output handle.
357 * \param output The output handle.
358 * \param buf The functions puts the current address of the buffer at the
359 * address specified by \p buf.
360 * \return The current size of valid data in the buffer.
361 *
362 * The internal buffer is empty after this call. The caller has the responsibility
363 * to clean the buffer using the free() call.
364 */
snd_output_buffer_steal(snd_output_t * output,char ** buf)365 size_t snd_output_buffer_steal(snd_output_t *output, char **buf)
366 {
367 snd_output_buffer_t *buffer = output->private_data;
368 size_t size;
369 *buf = (char *)buffer->buf;
370 size = buffer->size;
371 buffer->buf = NULL;
372 buffer->alloc = 0;
373 buffer->size = 0;
374 return size;
375 }
376
377 /**
378 * \brief Creates a new output object with an auto-extending memory buffer.
379 * \param outputp The function puts the pointer to the new output object
380 * at the address specified by \p outputp.
381 * \return Zero if successful, otherwise a negative error code.
382 */
snd_output_buffer_open(snd_output_t ** outputp)383 int snd_output_buffer_open(snd_output_t **outputp)
384 {
385 snd_output_t *output;
386 snd_output_buffer_t *buffer;
387 assert(outputp);
388 buffer = calloc(1, sizeof(*buffer));
389 if (!buffer)
390 return -ENOMEM;
391 output = calloc(1, sizeof(*output));
392 if (!output) {
393 free(buffer);
394 return -ENOMEM;
395 }
396 buffer->buf = NULL;
397 buffer->alloc = 0;
398 buffer->size = 0;
399 output->type = SND_OUTPUT_BUFFER;
400 output->ops = &snd_output_buffer_ops;
401 output->private_data = buffer;
402 *outputp = output;
403 return 0;
404 }
405