/** * \file output.c * \brief Generic stdio-like output interface * \author Abramo Bagnara * \date 2000 * * Generic stdio-like output interface */ /* * Output object * Copyright (c) 2000 by Abramo Bagnara * * * 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 #include #include #include #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; 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; 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 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; }