diff options
Diffstat (limited to 'lib/libsalsa/output.c')
-rw-r--r-- | lib/libsalsa/output.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/lib/libsalsa/output.c b/lib/libsalsa/output.c new file mode 100644 index 0000000..0369a81 --- /dev/null +++ b/lib/libsalsa/output.c @@ -0,0 +1,411 @@ +/** + * \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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "local.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; + 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 ATTRIBUTE_UNUSED) +{ + 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 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 ATTRIBUTE_UNUSED) +{ + 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; + + if (_free >= size) + return _free; + if (buffer->alloc == 0) + alloc = 256; + else + alloc = buffer->alloc; + while (alloc < 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 (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 (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 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_TYPE_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 = buffer->buf; + return buffer->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; +} |