• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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