Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <eblake@redhat.com>2007-08-03 00:23:06 +0400
committerEric Blake <eblake@redhat.com>2007-08-03 00:23:06 +0400
commit6ddcdb9da510ddf2b6309006ded0827f467e23b4 (patch)
treef0e349d3cc7ad6083b3a652e688d385215a41dc8 /newlib/libc/stdio/open_memstream.c
parent191814588af712faa0093d21a08c51c97abae3f2 (diff)
Implement fmemopen and open_memstream.
* libc/stdio/fmemopen.c (_fmemopen_r, fmemopen): New file. * libc/stdio/open_memstream.c (_open_memstream_r, open_memstream): New file. * libc/stdio/fopencookie.c (fcwriter): Minor optimization. * libc/include/stdio.h (dprintf, vdprintf): Group all POSIX 200x functions together. (fmemopen, open_memstream): Declare new functions. * libc/stdio/stdio.tex: Document them. * libc/stdio/Makefile.am (ELIX_4_SOURCES, CHEWOUT_FILES): Add fmemopen and open_memstream. * libc/stdio/Makefile.in: Regenerate.
Diffstat (limited to 'newlib/libc/stdio/open_memstream.c')
-rw-r--r--newlib/libc/stdio/open_memstream.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/newlib/libc/stdio/open_memstream.c b/newlib/libc/stdio/open_memstream.c
new file mode 100644
index 000000000..6a0680fbd
--- /dev/null
+++ b/newlib/libc/stdio/open_memstream.c
@@ -0,0 +1,330 @@
+/* Copyright (C) 2007 Eric Blake
+ * Permission to use, copy, modify, and distribute this software
+ * is freely granted, provided that this notice is preserved.
+ */
+
+/*
+FUNCTION
+<<open_memstream>>---open a write stream around an arbitrary-length string
+
+INDEX
+ open_memstream
+
+ANSI_SYNOPSIS
+ #include <stdio.h>
+ FILE *open_memstream(char **restrict <[buf]>,
+ size_t *restrict <[size]>);
+
+DESCRIPTION
+<<open_memstream>> creates a seekable <<FILE>> stream that wraps an
+arbitrary-length buffer, created as if by <<malloc>>. The current
+contents of *<[buf]> are ignored; this implementation uses *<[size]>
+as a hint of the maximum size expected, but does not fail if the hint
+was wrong. The parameters <[buf]> and <[size]> are later stored
+through following any call to <<fflush>> or <<fclose>>, set to the
+current address and usable size of the allocated string; although
+after fflush, the pointer is only valid until another stream operation
+that results in a write. Behavior is undefined if the user alters
+either *<[buf]> or *<[size]> prior to <<fclose>>.
+
+The stream is write-only, since the user can directly read *<[buf]>
+after a flush; see <<fmemopen>> for a way to wrap a string with a
+readable stream. The user is responsible for calling <<free>> on
+the final *<[buf]> after <<fclose>>.
+
+Any time the stream is flushed, a NUL byte is written at the current
+position (but is not counted in the buffer length), so that the string
+is always NUL-terminated after at most *<[size]> bytes. However, data
+previously written beyond the current stream offset is not lost, and
+the NUL byte written during a flush is restored to its previous value
+when seeking elsewhere in the string.
+
+RETURNS
+The return value is an open FILE pointer on success. On error,
+<<NULL>> is returned, and <<errno>> will be set to EINVAL if <[buf]>
+or <[size]> is NULL, ENOMEM if memory could not be allocated, or
+EMFILE if too many streams are already open.
+
+PORTABILITY
+This function is being added to POSIX 200x, but is not in POSIX 2001.
+
+Supporting OS subroutines required: <<sbrk>>.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/lock.h>
+#include "local.h"
+
+#ifndef __LARGE64_FILES
+# define OFF_T off_t
+#else
+# define OFF_T _off64_t
+#endif
+
+/* Describe details of an open memstream. */
+typedef struct memstream {
+ void *storage; /* storage to free on close */
+ char **pbuf; /* pointer to the current buffer */
+ size_t *psize; /* pointer to the current size, smaller of pos or eof */
+ size_t pos; /* current position */
+ size_t eof; /* current file size */
+ size_t max; /* current malloc buffer size, always > eof */
+ char saved; /* saved character that lived at *psize before NUL */
+} memstream;
+
+/* Write up to non-zero N bytes of BUF into the stream described by COOKIE,
+ returning the number of bytes written or EOF on failure. */
+static _READ_WRITE_RETURN_TYPE
+_DEFUN(memwriter, (ptr, cookie, buf, n),
+ struct _reent *ptr _AND
+ void *cookie _AND
+ const char *buf _AND
+ int n)
+{
+ memstream *c = (memstream *) cookie;
+ char *cbuf = *c->pbuf;
+
+ /* size_t is unsigned, but off_t is signed. Don't let stream get so
+ big that user cannot do ftello. */
+ if (sizeof (OFF_T) == sizeof (size_t) && (ssize_t) (c->pos + n) < 0)
+ {
+ ptr->_errno = EFBIG;
+ return EOF;
+ }
+ /* Grow the buffer, if necessary. Choose a geometric growth factor
+ to avoid quadratic realloc behavior, but use a rate less than
+ (1+sqrt(5))/2 to accomodate malloc overhead. Overallocate, so
+ that we can add a trailing \0 without reallocating. The new
+ allocation should thus be max(prev_size*1.5, c->pos+n+1). */
+ if (c->pos + n >= c->max)
+ {
+ size_t newsize = c->max * 3 / 2;
+ if (newsize < c->pos + n + 1)
+ newsize = c->pos + n + 1;
+ cbuf = _realloc_r (ptr, cbuf, newsize);
+ if (! cbuf)
+ return EOF; /* errno already set to ENOMEM */
+ *c->pbuf = cbuf;
+ c->max = newsize;
+ }
+ /* If we have previously done a seek beyond eof, ensure all
+ intermediate bytes are NUL. */
+ if (c->pos > c->eof)
+ memset (cbuf + c->eof, '\0', c->pos - c->eof);
+ memcpy (cbuf + c->pos, buf, n);
+ c->pos += n;
+ /* If the user has previously written further, remember what the
+ trailing NUL is overwriting. Otherwise, extend the stream. */
+ if (c->pos > c->eof)
+ c->eof = c->pos;
+ else
+ c->saved = cbuf[c->pos];
+ cbuf[c->pos] = '\0';
+ *c->psize = c->pos;
+ return n;
+}
+
+/* Seek to position POS relative to WHENCE within stream described by
+ COOKIE; return resulting position or fail with EOF. */
+static _fpos_t
+_DEFUN(memseeker, (ptr, cookie, pos, whence),
+ struct _reent *ptr _AND
+ void *cookie _AND
+ _fpos_t pos _AND
+ int whence)
+{
+ memstream *c = (memstream *) cookie;
+ OFF_T offset = (OFF_T) pos;
+
+ if (whence == SEEK_CUR)
+ offset += c->pos;
+ else if (whence == SEEK_END)
+ offset += c->eof;
+ if (offset < 0)
+ {
+ ptr->_errno = EINVAL;
+ offset = -1;
+ }
+ else if ((size_t) offset != offset)
+ {
+ ptr->_errno = ENOSPC;
+ offset = -1;
+ }
+#ifdef __LARGE64_FILES
+ else if ((_fpos_t) offset != offset)
+ {
+ ptr->_errno = EOVERFLOW;
+ offset = -1;
+ }
+#endif /* __LARGE64_FILES */
+ else
+ {
+ if (c->pos < c->eof)
+ {
+ (*c->pbuf)[c->pos] = c->saved;
+ c->saved = '\0';
+ }
+ c->pos = offset;
+ if (c->pos < c->eof)
+ {
+ c->saved = (*c->pbuf)[c->pos];
+ (*c->pbuf)[c->pos] = '\0';
+ *c->psize = c->pos;
+ }
+ else
+ *c->psize = c->eof;
+ }
+ return (_fpos_t) offset;
+}
+
+/* Seek to position POS relative to WHENCE within stream described by
+ COOKIE; return resulting position or fail with EOF. */
+#ifdef __LARGE64_FILES
+static _fpos64_t
+_DEFUN(memseeker64, (ptr, cookie, pos, whence),
+ struct _reent *ptr _AND
+ void *cookie _AND
+ _fpos64_t pos _AND
+ int whence)
+{
+ _off64_t offset = (_off64_t) pos;
+ memstream *c = (memstream *) cookie;
+
+ if (whence == SEEK_CUR)
+ offset += c->pos;
+ else if (whence == SEEK_END)
+ offset += c->eof;
+ if (offset < 0)
+ {
+ ptr->_errno = EINVAL;
+ offset = -1;
+ }
+ else if ((size_t) offset != offset)
+ {
+ ptr->_errno = ENOSPC;
+ offset = -1;
+ }
+ else
+ {
+ if (c->pos < c->eof)
+ {
+ (*c->pbuf)[c->pos] = c->saved;
+ c->saved = '\0';
+ }
+ c->pos = offset;
+ if (c->pos < c->eof)
+ {
+ c->saved = (*c->pbuf)[c->pos];
+ (*c->pbuf)[c->pos] = '\0';
+ *c->psize = c->pos;
+ }
+ else
+ *c->psize = c->eof;
+ }
+ return (_fpos64_t) offset;
+}
+#endif /* __LARGE64_FILES */
+
+/* Reclaim resources used by stream described by COOKIE. */
+static int
+_DEFUN(memcloser, (ptr, cookie),
+ struct _reent *ptr _AND
+ void *cookie)
+{
+ memstream *c = (memstream *) cookie;
+ char *buf;
+
+ /* Be nice and try to reduce any unused memory. */
+ buf = _realloc_r (ptr, *c->pbuf, *c->psize + 1);
+ if (buf)
+ *c->pbuf = buf;
+ _free_r (ptr, c->storage);
+ return 0;
+}
+
+/* Open a memstream that tracks a dynamic buffer in BUF and SIZE.
+ Return the new stream, or fail with NULL. */
+FILE *
+_DEFUN(_open_memstream_r, (ptr, buf, size),
+ struct _reent *ptr _AND
+ char **buf _AND
+ size_t *size)
+{
+ FILE *fp;
+ memstream *c;
+ int flags;
+
+ if (!buf || !size)
+ {
+ ptr->_errno = EINVAL;
+ return NULL;
+ }
+ if ((fp = __sfp (ptr)) == NULL)
+ return NULL;
+ if ((c = (memstream *) _malloc_r (ptr, sizeof *c)) == NULL)
+ {
+ __sfp_lock_acquire ();
+ fp->_flags = 0; /* release */
+#ifndef __SINGLE_THREAD__
+ __lock_close_recursive (fp->_lock);
+#endif
+ __sfp_lock_release ();
+ return NULL;
+ }
+ /* Use *size as a hint for initial sizing, but bound the initial
+ malloc between 64 bytes (same as asprintf, to avoid frequent
+ mallocs on small strings) and 64k bytes (to avoid overusing the
+ heap if *size was garbage). */
+ c->max = *size;
+ if (c->max < 64)
+ c->max = 64;
+ else if (c->max > 64 * 1024)
+ c->max = 64 * 1024;
+ *size = 0;
+ *buf = _malloc_r (ptr, c->max);
+ if (!*buf)
+ {
+ __sfp_lock_acquire ();
+ fp->_flags = 0; /* release */
+#ifndef __SINGLE_THREAD__
+ __lock_close_recursive (fp->_lock);
+#endif
+ __sfp_lock_release ();
+ _free_r (ptr, c);
+ return NULL;
+ }
+ **buf = '\0';
+
+ c->storage = c;
+ c->pbuf = buf;
+ c->psize = size;
+ c->eof = 0;
+ c->saved = '\0';
+
+ _flockfile (fp);
+ fp->_file = -1;
+ fp->_flags = __SWR;
+ fp->_cookie = c;
+ fp->_read = NULL;
+ fp->_write = memwriter;
+ fp->_seek = memseeker;
+#ifdef __LARGE64_FILES
+ fp->_seek64 = memseeker64;
+ fp->_flags |= __SL64;
+#endif
+ fp->_close = memcloser;
+ _funlockfile (fp);
+ return fp;
+}
+
+#ifndef _REENT_ONLY
+FILE *
+_DEFUN(open_memstream, (buf, size),
+ char **buf _AND
+ size_t *size)
+{
+ return _open_memstream_r (_REENT, buf, size);
+}
+#endif /* !_REENT_ONLY */