| /** | |
| * \file output.c | |
| * \brief Generic stdio-like output interface | |
| * \author Abramo Bagnara <abramo@alsa-project.org> | |
| * \date 2000 | |
| * | |
| * Generic stdio-like output interface | |
| */ | |
| /* | |
| * Output object | |
| * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | |
| * | |
| * | |
| * This library is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as | |
| * published by the Free Software Foundation; either version 2.1 of | |
| * the License, or (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| * | |
| */ | |
| #include "local.h" | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #ifndef DOC_HIDDEN | |
| typedef struct _snd_output_ops { | |
| int (*close)(snd_output_t *output); | |
| int (*print)(snd_output_t *output, const char *format, va_list args); | |
| int (*puts)(snd_output_t *output, const char *str); | |
| int (*putch)(snd_output_t *output, int c); | |
| int (*flush)(snd_output_t *output); | |
| } snd_output_ops_t; | |
| struct _snd_output { | |
| snd_output_type_t type; | |
| const snd_output_ops_t *ops; | |
| void *private_data; | |
| }; | |
| #endif | |
| /** | |
| * \brief Closes an output handle. | |
| * \param output The output handle to be closed. | |
| * \return Zero if successful, otherwise a negative error code. | |
| */ | |
| int snd_output_close(snd_output_t *output) | |
| { | |
| int err = output->ops->close(output); | |
| free(output); | |
| return err; | |
| } | |
| /** | |
| * \brief Writes formatted output (like \c fprintf(3)) to an output handle. | |
| * \param output The output handle. | |
| * \param format Format string in \c fprintf format. | |
| * \param ... Other \c fprintf arguments. | |
| * \return The number of characters written, or a negative error code. | |
| */ | |
| int snd_output_printf(snd_output_t *output, const char *format, ...) | |
| { | |
| int result; | |
| va_list args; | |
| va_start(args, format); | |
| result = output->ops->print(output, format, args); | |
| va_end(args); | |
| return result; | |
| } | |
| /** | |
| * \brief Writes formatted output (like \c fprintf(3)) to an output handle. | |
| * \param output The output handle. | |
| * \param format Format string in \c fprintf format. | |
| * \param args Other \c fprintf arguments. | |
| * \return The number of characters written, or a negative error code. | |
| */ | |
| int snd_output_vprintf(snd_output_t *output, const char *format, va_list args) | |
| { | |
| return output->ops->print(output, format, args); | |
| } | |
| /** | |
| * \brief Writes a string to an output handle (like \c fputs(3)). | |
| * \param output The output handle. | |
| * \param str Pointer to the string. | |
| * \return Zero if successful, otherwise a negative error code or \c EOF. | |
| */ | |
| int snd_output_puts(snd_output_t *output, const char *str) | |
| { | |
| return output->ops->puts(output, str); | |
| } | |
| /** | |
| * \brief Writes a character to an output handle (like \c putc(3)). | |
| * \param output The output handle. | |
| * \param c The character. | |
| * \return Zero if successful, otherwise a negative error code or \c EOF. | |
| */ | |
| int snd_output_putc(snd_output_t *output, int c) | |
| { | |
| return output->ops->putch(output, c); | |
| } | |
| /** | |
| * \brief Flushes an output handle (like fflush(3)). | |
| * \param output The output handle. | |
| * \return Zero if successful, otherwise \c EOF. | |
| * | |
| * If the underlying destination is a stdio stream, this function calls | |
| * \c fflush. If the underlying destination is a memory buffer, the write | |
| * position is reset to the beginning of the buffer. \c =:-o | |
| */ | |
| int snd_output_flush(snd_output_t *output) | |
| { | |
| return output->ops->flush(output); | |
| } | |
| #ifndef DOC_HIDDEN | |
| typedef struct _snd_output_stdio { | |
| int close; | |
| FILE *fp; | |
| } snd_output_stdio_t; | |
| static int snd_output_stdio_close(snd_output_t *output) | |
| { | |
| snd_output_stdio_t *stdio = output->private_data; | |
| if (stdio->close) | |
| fclose(stdio->fp); | |
| free(stdio); | |
| return 0; | |
| } | |
| static int snd_output_stdio_print(snd_output_t *output, const char *format, va_list args) | |
| { | |
| snd_output_stdio_t *stdio = output->private_data; | |
| return vfprintf(stdio->fp, format, args); | |
| } | |
| static int snd_output_stdio_puts(snd_output_t *output, const char *str) | |
| { | |
| snd_output_stdio_t *stdio = output->private_data; | |
| return fputs(str, stdio->fp); | |
| } | |
| static int snd_output_stdio_putc(snd_output_t *output, int c) | |
| { | |
| snd_output_stdio_t *stdio = output->private_data; | |
| return putc(c, stdio->fp); | |
| } | |
| static int snd_output_stdio_flush(snd_output_t *output) | |
| { | |
| snd_output_stdio_t *stdio = output->private_data; | |
| return fflush(stdio->fp); | |
| } | |
| static const snd_output_ops_t snd_output_stdio_ops = { | |
| .close = snd_output_stdio_close, | |
| .print = snd_output_stdio_print, | |
| .puts = snd_output_stdio_puts, | |
| .putch = snd_output_stdio_putc, | |
| .flush = snd_output_stdio_flush, | |
| }; | |
| #endif | |
| /** | |
| * \brief Creates a new output object using an existing stdio \c FILE pointer. | |
| * \param outputp The function puts the pointer to the new output object | |
| * at the address specified by \p outputp. | |
| * \param fp The \c FILE pointer to write to. Characters are written | |
| * to the file starting at the current file position. | |
| * \param _close Close flag. Set this to 1 if #snd_output_close should close | |
| * \p fp by calling \c fclose. | |
| * \return Zero if successful, otherwise a negative error code. | |
| */ | |
| int snd_output_stdio_attach(snd_output_t **outputp, FILE *fp, int _close) | |
| { | |
| snd_output_t *output; | |
| snd_output_stdio_t *stdio; | |
| assert(outputp && fp); | |
| stdio = calloc(1, sizeof(*stdio)); | |
| if (!stdio) | |
| return -ENOMEM; | |
| output = calloc(1, sizeof(*output)); | |
| if (!output) { | |
| free(stdio); | |
| return -ENOMEM; | |
| } | |
| stdio->fp = fp; | |
| stdio->close = _close; | |
| output->type = SND_OUTPUT_STDIO; | |
| output->ops = &snd_output_stdio_ops; | |
| output->private_data = stdio; | |
| *outputp = output; | |
| return 0; | |
| } | |
| /** | |
| * \brief Creates a new output object writing to a file. | |
| * \param outputp The function puts the pointer to the new output object | |
| * at the address specified by \p outputp. | |
| * \param file The name of the file to open. | |
| * \param mode The open mode, like \c fopen(3). | |
| * \return Zero if successful, otherwise a negative error code. | |
| */ | |
| int snd_output_stdio_open(snd_output_t **outputp, const char *file, const char *mode) | |
| { | |
| int err; | |
| FILE *fp = fopen(file, mode); | |
| if (!fp) { | |
| //SYSERR("fopen"); | |
| return -errno; | |
| } | |
| err = snd_output_stdio_attach(outputp, fp, 1); | |
| if (err < 0) | |
| fclose(fp); | |
| return err; | |
| } | |
| #ifndef DOC_HIDDEN | |
| typedef struct _snd_output_buffer { | |
| unsigned char *buf; | |
| size_t alloc; | |
| size_t size; | |
| } snd_output_buffer_t; | |
| static int snd_output_buffer_close(snd_output_t *output) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| free(buffer->buf); | |
| free(buffer); | |
| return 0; | |
| } | |
| static int snd_output_buffer_need(snd_output_t *output, size_t size) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| size_t _free = buffer->alloc - buffer->size; | |
| size_t alloc; | |
| unsigned char *buf; | |
| /* use 'size++' to allow to add the '\0' string terminator */ | |
| /* without reallocation */ | |
| size++; | |
| if (_free >= size) | |
| return _free; | |
| if (buffer->alloc == 0) | |
| alloc = 256; | |
| else | |
| alloc = buffer->alloc; | |
| while (alloc < buffer->size + size) | |
| alloc *= 2; | |
| buf = realloc(buffer->buf, alloc); | |
| if (!buf) | |
| return -ENOMEM; | |
| buffer->buf = buf; | |
| buffer->alloc = alloc; | |
| return buffer->alloc - buffer->size; | |
| } | |
| static int snd_output_buffer_print(snd_output_t *output, const char *format, va_list args) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| size_t size = 256; | |
| int result; | |
| result = snd_output_buffer_need(output, size); | |
| if (result < 0) | |
| return result; | |
| result = vsnprintf((char *)buffer->buf + buffer->size, size, format, args); | |
| assert(result >= 0); | |
| if ((size_t)result <= size) { | |
| buffer->size += result; | |
| return result; | |
| } | |
| size = result; | |
| result = snd_output_buffer_need(output, size); | |
| if (result < 0) | |
| return result; | |
| result = vsnprintf((char *)buffer->buf + buffer->size, result, format, args); | |
| assert(result == (int)size); | |
| buffer->size += result; | |
| return result; | |
| } | |
| static int snd_output_buffer_puts(snd_output_t *output, const char *str) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| size_t size = strlen(str); | |
| int err; | |
| err = snd_output_buffer_need(output, size); | |
| if (err < 0) | |
| return err; | |
| memcpy(buffer->buf + buffer->size, str, size); | |
| buffer->size += size; | |
| return size; | |
| } | |
| static int snd_output_buffer_putc(snd_output_t *output, int c) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| int err; | |
| err = snd_output_buffer_need(output, 1); | |
| if (err < 0) | |
| return err; | |
| buffer->buf[buffer->size++] = c; | |
| return 0; | |
| } | |
| static int snd_output_buffer_flush(snd_output_t *output ATTRIBUTE_UNUSED) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| buffer->size = 0; | |
| return 0; | |
| } | |
| static const snd_output_ops_t snd_output_buffer_ops = { | |
| .close = snd_output_buffer_close, | |
| .print = snd_output_buffer_print, | |
| .puts = snd_output_buffer_puts, | |
| .putch = snd_output_buffer_putc, | |
| .flush = snd_output_buffer_flush, | |
| }; | |
| #endif | |
| /** | |
| * \brief Returns the address of the buffer of a #SND_OUTPUT_BUFFER output handle. | |
| * \param output The output handle. | |
| * \param buf The functions puts the current address of the buffer at the | |
| * address specified by \p buf. | |
| * \return The current size of valid data in the buffer. | |
| * | |
| * The address of the buffer may become invalid when output functions or | |
| * #snd_output_close are called. | |
| */ | |
| size_t snd_output_buffer_string(snd_output_t *output, char **buf) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| *buf = (char *)buffer->buf; | |
| return buffer->size; | |
| } | |
| /** | |
| * \brief Returns the address of the buffer of a #SND_OUTPUT_BUFFER output handle. | |
| * \param output The output handle. | |
| * \param buf The functions puts the current address of the buffer at the | |
| * address specified by \p buf. | |
| * \return The current size of valid data in the buffer. | |
| * | |
| * The internal buffer is empty after this call. The caller has the responsibility | |
| * to clean the buffer using the free() call. | |
| */ | |
| size_t snd_output_buffer_steal(snd_output_t *output, char **buf) | |
| { | |
| snd_output_buffer_t *buffer = output->private_data; | |
| size_t size; | |
| *buf = (char *)buffer->buf; | |
| size = buffer->size; | |
| buffer->buf = NULL; | |
| buffer->alloc = 0; | |
| buffer->size = 0; | |
| return size; | |
| } | |
| /** | |
| * \brief Creates a new output object with an auto-extending memory buffer. | |
| * \param outputp The function puts the pointer to the new output object | |
| * at the address specified by \p outputp. | |
| * \return Zero if successful, otherwise a negative error code. | |
| */ | |
| int snd_output_buffer_open(snd_output_t **outputp) | |
| { | |
| snd_output_t *output; | |
| snd_output_buffer_t *buffer; | |
| assert(outputp); | |
| buffer = calloc(1, sizeof(*buffer)); | |
| if (!buffer) | |
| return -ENOMEM; | |
| output = calloc(1, sizeof(*output)); | |
| if (!output) { | |
| free(buffer); | |
| return -ENOMEM; | |
| } | |
| buffer->buf = NULL; | |
| buffer->alloc = 0; | |
| buffer->size = 0; | |
| output->type = SND_OUTPUT_BUFFER; | |
| output->ops = &snd_output_buffer_ops; | |
| output->private_data = buffer; | |
| *outputp = output; | |
| return 0; | |
| } |