fmemopen and open_memstream
Jeff Johnston
jjohnstn@redhat.com
Fri Aug 3 05:05:00 GMT 2007
Ok, but please add comment on the moving of dprintf and vdprintf in stdio.h.
-- Jeff J.
Eric Blake wrote:
>After more debugging, I decided to submit open_memstream alongside fmemopen.
>
>OK to commit?
>
>2007-07-25 Eric Blake <ebb9@byu.net>
>
> 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/include/stdio.h (fmemopen, open_memstream): Declare them.
> * 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.
>
>Index: libc/stdio/fmemopen.c
>===================================================================
>RCS file: libc/stdio/fmemopen.c
>diff -N libc/stdio/fmemopen.c
>--- /dev/null 1 Jan 1970 00:00:00 -0000
>+++ libc/stdio/fmemopen.c 26 Jul 2007 02:40:55 -0000
>@@ -0,0 +1,371 @@
>+/* Copyright (C) 2007 Eric Blake
>+ * Permission to use, copy, modify, and distribute this software
>+ * is freely granted, provided that this notice is preserved.
>+ */
>+
>+/*
>+FUNCTION
>+<<fmemopen>>---open a stream around a fixed-length string
>+
>+INDEX
>+ fmemopen
>+
>+ANSI_SYNOPSIS
>+ #include <stdio.h>
>+ FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>,
>+ const char *restrict <[mode]>);
>+
>+DESCRIPTION
>+<<fmemopen>> creates a seekable <<FILE>> stream that wraps a
>+fixed-length buffer of <[size]> bytes starting at <[buf]>. The stream
>+is opened with <[mode]> treated as in <<fopen>>, where append mode
>+starts writing at the first NUL byte. If <[buf]> is NULL, then
>+<[size]> bytes are automatically provided as if by <<malloc>>, with
>+the initial size of 0, and <[mode]> must contain <<+>> so that data
>+can be read after it is written.
>+
>+The stream maintains a current position, which moves according to
>+bytes read or written, and which can be one past the end of the array.
>+The stream also maintains a current file size, which is never greater
>+than <[size]>. If <[mode]> starts with <<r>>, the position starts at
>+<<0>>, and file size starts at <[size]> if <[buf]> was provided. If
>+<[mode]> starts with <<w>>, the position and file size start at <<0>>,
>+and if <[buf]> was provided, the first byte is set to NUL. If
>+<[mode]> starts with <<a>>, the position and file size start at the
>+location of the first NUL byte, or else <[size]> if <[buf]> was
>+provided.
>+
>+When reading, NUL bytes have no significance, and reads cannot exceed
>+the current file size. When writing, the file size can increase up to
>+<[size]> as needed, and NUL bytes may be embedded in the stream (see
>+<<open_memstream>> for an alternative that automatically enlarges the
>+buffer). When the stream is flushed or closed after a write that
>+changed the file size, a NUL byte is written at the current position
>+if there is still room; if the stream is not also open for reading, a
>+NUL byte is additionally written at the last byte of <[buf]> when the
>+stream has exceeded <[size]>, so that a write-only <[buf]> is always
>+NUL-terminated when the stream is flushed or closed (and the initial
>+<[size]> should take this into account). It is not possible to seek
>+outside the bounds of <[size]>. A 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 <[size]>
>+is zero or <[mode]> is invalid, ENOMEM if <[buf]> was NULL and 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"
>+
>+/* Describe details of an open memstream. */
>+typedef struct fmemcookie {
>+ void *storage; /* storage to free on close */
>+ char *buf; /* buffer start */
>+ size_t pos; /* current position */
>+ size_t eof; /* current file size */
>+ size_t max; /* maximum file size */
>+ char append; /* nonzero if appending */
>+ char writeonly; /* 1 if write-only */
>+ char saved; /* saved character that lived at pos before write-only NUL */
>+} fmemcookie;
>+
>+/* Read up to non-zero N bytes into BUF from stream described by
>+ COOKIE; return number of bytes read (0 on EOF). */
>+static _READ_WRITE_RETURN_TYPE
>+_DEFUN(fmemreader, (ptr, cookie, buf, n),
>+ struct _reent *ptr _AND
>+ void *cookie _AND
>+ char *buf _AND
>+ int n)
>+{
>+ fmemcookie *c = (fmemcookie *) cookie;
>+ /* Can't read beyond current size, but EOF condition is not an error. */
>+ if (c->pos > c->eof)
>+ return 0;
>+ if (n >= c->eof - c->pos)
>+ n = c->eof - c->pos;
>+ memcpy (buf, c->buf + c->pos, n);
>+ c->pos += n;
>+ return n;
>+}
>+
>+/* 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(fmemwriter, (ptr, cookie, buf, n),
>+ struct _reent *ptr _AND
>+ void *cookie _AND
>+ const char *buf _AND
>+ int n)
>+{
>+ fmemcookie *c = (fmemcookie *) cookie;
>+ int adjust = 0; /* true if at EOF, but still need to write NUL. */
>+
>+ /* Append always seeks to eof; otherwise, if we have previously done
>+ a seek beyond eof, ensure all intermediate bytes are NUL. */
>+ if (c->append)
>+ c->pos = c->eof;
>+ else if (c->pos > c->eof)
>+ memset (c->buf + c->eof, '\0', c->pos - c->eof);
>+ /* Do not write beyond EOF; saving room for NUL on write-only stream. */
>+ if (c->pos + n > c->max - c->writeonly)
>+ {
>+ adjust = c->writeonly;
>+ n = c->max - c->pos;
>+ }
>+ /* Now n is the number of bytes being modified, and adjust is 1 if
>+ the last byte is NUL instead of from buf. Write a NUL if
>+ write-only; or if read-write, eof changed, and there is still
>+ room. When we are within the file contents, remember what we
>+ overwrite so we can restore it if we seek elsewhere later. */
>+ if (c->pos + n > c->eof)
>+ {
>+ c->eof = c->pos + n;
>+ if (c->eof - adjust < c->max)
>+ c->saved = c->buf[c->eof - adjust] = '\0';
>+ }
>+ else if (c->writeonly)
>+ {
>+ if (n)
>+ {
>+ c->saved = c->buf[c->pos + n - adjust];
>+ c->buf[c->pos + n - adjust] = '\0';
>+ }
>+ else
>+ adjust = 0;
>+ }
>+ c->pos += n;
>+ if (n - adjust)
>+ memcpy (c->buf + c->pos - n, buf, n - adjust);
>+ else
>+ {
>+ ptr->_errno = ENOSPC;
>+ return EOF;
>+ }
>+ 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(fmemseeker, (ptr, cookie, pos, whence),
>+ struct _reent *ptr _AND
>+ void *cookie _AND
>+ _fpos_t pos _AND
>+ int whence)
>+{
>+ fmemcookie *c = (fmemcookie *) cookie;
>+#ifndef __LARGE64_FILES
>+ off_t offset = (off_t) pos;
>+#else /* __LARGE64_FILES */
>+ _off64_t offset = (_off64_t) pos;
>+#endif /* __LARGE64_FILES */
>+
>+ 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 (offset > c->max)
>+ {
>+ 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->writeonly && c->pos < c->eof)
>+ {
>+ c->buf[c->pos] = c->saved;
>+ c->saved = '\0';
>+ }
>+ c->pos = offset;
>+ if (c->writeonly && c->pos < c->eof)
>+ {
>+ c->saved = c->buf[c->pos];
>+ c->buf[c->pos] = '\0';
>+ }
>+ }
>+ 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(fmemseeker64, (ptr, cookie, pos, whence),
>+ struct _reent *ptr _AND
>+ void *cookie _AND
>+ _fpos64_t pos _AND
>+ int whence)
>+{
>+ _off64_t offset = (_off64_t) pos;
>+ fmemcookie *c = (fmemcookie *) 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 (offset > c->max)
>+ {
>+ ptr->_errno = ENOSPC;
>+ offset = -1;
>+ }
>+ else
>+ {
>+ if (c->writeonly && c->pos < c->eof)
>+ {
>+ c->buf[c->pos] = c->saved;
>+ c->saved = '\0';
>+ }
>+ c->pos = offset;
>+ if (c->writeonly && c->pos < c->eof)
>+ {
>+ c->saved = c->buf[c->pos];
>+ c->buf[c->pos] = '\0';
>+ }
>+ }
>+ return (_fpos64_t) offset;
>+}
>+#endif /* __LARGE64_FILES */
>+
>+/* Reclaim resources used by stream described by COOKIE. */
>+static int
>+_DEFUN(fmemcloser, (ptr, cookie),
>+ struct _reent *ptr _AND
>+ void *cookie)
>+{
>+ fmemcookie *c = (fmemcookie *) cookie;
>+ _free_r (ptr, c->storage);
>+ return 0;
>+}
>+
>+/* Open a memstream around buffer BUF of SIZE bytes, using MODE.
>+ Return the new stream, or fail with NULL. */
>+FILE *
>+_DEFUN(_fmemopen_r, (ptr, buf, size, mode),
>+ struct _reent *ptr _AND
>+ void *buf _AND
>+ size_t size _AND
>+ const char *mode)
>+{
>+ FILE *fp;
>+ fmemcookie *c;
>+ int flags;
>+ int dummy;
>+
>+ if ((flags = __sflags (ptr, mode, &dummy)) == 0)
>+ return NULL;
>+ if (!size || !(buf || flags & __SAPP))
>+ {
>+ ptr->_errno = EINVAL;
>+ return NULL;
>+ }
>+ if ((fp = __sfp (ptr)) == NULL)
>+ return NULL;
>+ if ((c = (fmemcookie *) _malloc_r (ptr, sizeof *c + (buf ? 0 : size)))
>+ == NULL)
>+ {
>+ __sfp_lock_acquire ();
>+ fp->_flags = 0; /* release */
>+#ifndef __SINGLE_THREAD__
>+ __lock_close_recursive (fp->_lock);
>+#endif
>+ __sfp_lock_release ();
>+ return NULL;
>+ }
>+
>+ c->storage = c;
>+ c->max = size;
>+ /* 9 modes to worry about. */
>+ /* w/a, buf or no buf: Guarantee a NUL after any file writes. */
>+ c->writeonly = (flags & __SWR) != 0;
>+ c->saved = '\0';
>+ if (!buf)
>+ {
>+ /* r+/w+/a+, and no buf: file starts empty. */
>+ c->buf = (char *) (c + 1);
>+ *(char *) buf = '\0';
>+ c->pos = c->eof = 0;
>+ c->append = (flags & __SAPP) != 0;
>+ }
>+ else
>+ {
>+ c->buf = (char *) buf;
>+ switch (*mode)
>+ {
>+ case 'a':
>+ /* a/a+ and buf: position and size at first NUL. */
>+ buf = memchr (c->buf, '\0', size);
>+ c->eof = c->pos = buf ? (char *) buf - c->buf : size;
>+ if (!buf && c->writeonly)
>+ /* a: guarantee a NUL within size even if no writes. */
>+ c->buf[size - 1] = '\0';
>+ c->append = 1;
>+ break;
>+ case 'r':
>+ /* r/r+ and buf: read at beginning, full size available. */
>+ c->pos = c->append = 0;
>+ c->eof = size;
>+ break;
>+ case 'w':
>+ /* w/w+ and buf: write at beginning, truncate to empty. */
>+ c->pos = c->append = c->eof = 0;
>+ *c->buf = '\0';
>+ break;
>+ default:
>+ abort ();
>+ }
>+ }
>+
>+ _flockfile (fp);
>+ fp->_file = -1;
>+ fp->_flags = flags;
>+ fp->_cookie = c;
>+ fp->_read = flags & (__SRD | __SRW) ? fmemreader : NULL;
>+ fp->_write = flags & (__SWR | __SRW) ? fmemwriter : NULL;
>+ fp->_seek = fmemseeker;
>+#ifdef __LARGE64_FILES
>+ fp->_seek64 = fmemseeker64;
>+ fp->_flags |= __SL64;
>+#endif
>+ fp->_close = fmemcloser;
>+ _funlockfile (fp);
>+ return fp;
>+}
>+
>+#ifndef _REENT_ONLY
>+FILE *
>+_DEFUN(fmemopen, (buf, size, mode),
>+ void *buf _AND
>+ size_t size _AND
>+ const char *mode)
>+{
>+ return _fmemopen_r (_REENT, buf, size, mode);
>+}
>+#endif /* !_REENT_ONLY */
>Index: libc/stdio/open_memstream.c
>===================================================================
>RCS file: libc/stdio/open_memstream.c
>diff -N libc/stdio/open_memstream.c
>--- /dev/null 1 Jan 1970 00:00:00 -0000
>+++ libc/stdio/open_memstream.c 26 Jul 2007 02:40:55 -0000
>@@ -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 */
>Index: libc/stdio/stdio.tex
>===================================================================
>RCS file: /cvs/src/src/newlib/libc/stdio/stdio.tex,v
>retrieving revision 1.9
>diff -u -p -r1.9 stdio.tex
>--- libc/stdio/stdio.tex 19 Jul 2007 03:42:21 -0000 1.9
>+++ libc/stdio/stdio.tex 26 Jul 2007 02:40:55 -0000
>@@ -37,6 +37,7 @@ structure.
> * fgetpos:: Record position in a stream or file
> * fgets:: Get character string from a file or stream
> * fileno:: Get file descriptor associated with stream
>+* fmemopen:: Open a stream around a fixed-length buffer
> * fopen:: Open a file
> * fopencookie:: Open a stream with custom callbacks
> * fputc:: Write a character on a stream or file
>@@ -57,6 +58,7 @@ structure.
> * gets:: Get character string from standard input (obsolete)
> * getw:: Get a word (int) from a file or stream
> * mktemp:: Generate unused file name
>+* open_memstream:: Open a write stream around an arbitrary-length buffer
> * perror:: Print an error message on standard error
> * putc:: Write a character on a stream or file (macro)
> * putc_unlocked:: Write a character on a stream or file (macro)
>@@ -124,6 +126,9 @@ structure.
> @include stdio/fileno.def
>
> @page
>+@include stdio/fmemopen.def
>+
>+@page
> @include stdio/fopen.def
>
> @page
>@@ -184,6 +189,9 @@ structure.
> @include stdio/mktemp.def
>
> @page
>+@include stdio/open_memstream.def
>+
>+@page
> @include stdio/perror.def
>
> @page
>Index: libc/stdio/Makefile.am
>===================================================================
>RCS file: /cvs/src/src/newlib/libc/stdio/Makefile.am,v
>retrieving revision 1.26
>diff -u -p -r1.26 Makefile.am
>--- libc/stdio/Makefile.am 13 Jul 2007 17:07:28 -0000 1.26
>+++ libc/stdio/Makefile.am 26 Jul 2007 02:40:55 -0000
>@@ -117,8 +117,10 @@ ELIX_4_SOURCES = \
> asnprintf.c \
> diprintf.c \
> dprintf.c \
>+ fmemopen.c \
> fopencookie.c \
> funopen.c \
>+ open_memstream.c \
> vasniprintf.c \
> vasnprintf.c
> endif !ELIX_LEVEL_3
>@@ -179,6 +181,7 @@ CHEWOUT_FILES = \
> fgetpos.def \
> fgets.def \
> fileno.def \
>+ fmemopen.def \
> fopen.def \
> fopencookie.def \
> fputc.def \
>@@ -199,6 +202,7 @@ CHEWOUT_FILES = \
> gets.def \
> getw.def \
> mktemp.def \
>+ open_memstream.def \
> perror.def \
> putc.def \
> putc_u.def \
>@@ -244,6 +248,7 @@ $(lpfx)fclose.$(oext): local.h
> $(lpfx)fdopen.$(oext): local.h
> $(lpfx)fflush.$(oext): local.h
> $(lpfx)findfp.$(oext): local.h
>+$(lpfx)fmemopen.$(oext): local.h
> $(lpfx)fopen.$(oext): local.h
> $(lpfx)fopencookie.$(oext): local.h
> $(lpfx)fputs.$(oext): fvwrite.h
>Index: libc/include/stdio.h
>===================================================================
>RCS file: /cvs/src/src/newlib/libc/include/stdio.h,v
>retrieving revision 1.47
>diff -u -p -r1.47 stdio.h
>--- libc/include/stdio.h 13 Jul 2007 20:37:53 -0000 1.47
>+++ libc/include/stdio.h 26 Jul 2007 02:40:55 -0000
>@@ -244,11 +244,9 @@ char * _EXFUN(asnprintf, (char *, size_t
> _ATTRIBUTE ((__format__ (__printf__, 3, 4))));
> int _EXFUN(asprintf, (char **, const char *, ...)
> _ATTRIBUTE ((__format__ (__printf__, 2, 3))));
>-#ifndef dprintf
>+#ifndef diprintf
> int _EXFUN(diprintf, (int, const char *, ...)
> _ATTRIBUTE ((__format__ (__printf__, 2, 3))));
>-int _EXFUN(dprintf, (int, const char *, ...)
>- _ATTRIBUTE ((__format__ (__printf__, 2, 3))));
> #endif
> int _EXFUN(fcloseall, (_VOID));
> int _EXFUN(fiprintf, (FILE *, const char *, ...)
>@@ -278,8 +276,6 @@ int _EXFUN(vasprintf, (char **, const ch
> _ATTRIBUTE ((__format__ (__printf__, 2, 0))));
> int _EXFUN(vdiprintf, (int, const char *, __VALIST)
> _ATTRIBUTE ((__format__ (__printf__, 2, 0))));
>-int _EXFUN(vdprintf, (int, const char *, __VALIST)
>- _ATTRIBUTE ((__format__ (__printf__, 2, 0))));
> int _EXFUN(vfiprintf, (FILE *, const char *, __VALIST)
> _ATTRIBUTE ((__format__ (__printf__, 2, 0))));
> int _EXFUN(vfiscanf, (FILE *, const char *, __VALIST)
>@@ -306,7 +302,7 @@ int _EXFUN(vsscanf, (const char *, const
> #endif /* !__STRICT_ANSI__ */
>
> /*
>- * Routines in POSIX 1003.1.
>+ * Routines in POSIX 1003.1:2001.
> */
>
> #ifndef __STRICT_ANSI__
>@@ -330,6 +326,26 @@ int _EXFUN(putchar_unlocked, (int));
> #endif /* ! __STRICT_ANSI__ */
>
> /*
>+ * Routines in POSIX 1003.1:200x.
>+ */
>+
>+#ifndef __STRICT_ANSI__
>+# ifndef _REENT_ONLY
>+# ifndef dprintf
>+int _EXFUN(dprintf, (int, const char *, ...)
>+ _ATTRIBUTE ((__format__ (__printf__, 2, 3))));
>+# endif
>+FILE * _EXFUN(fmemopen, (void *, size_t, const char *));
>+/* getdelim - see __getdelim for now */
>+/* getline - see __getline for now */
>+FILE * _EXFUN(open_memstream, (char **, size_t *));
>+/* renameat - unimplemented for now */
>+int _EXFUN(vdprintf, (int, const char *, __VALIST)
>+ _ATTRIBUTE ((__format__ (__printf__, 2, 0))));
>+# endif
>+#endif
>+
>+/*
> * Recursive versions of the above.
> */
>
>@@ -354,6 +370,7 @@ int _EXFUN(_fiprintf_r, (struct _reent *
> _ATTRIBUTE ((__format__ (__printf__, 3, 4))));
> int _EXFUN(_fiscanf_r, (struct _reent *, FILE *, const char *, ...)
> _ATTRIBUTE ((__format__ (__scanf__, 3, 4))));
>+FILE * _EXFUN(_fmemopen_r, (struct _reent *, void *, size_t, const char *));
> FILE * _EXFUN(_fopen_r, (struct _reent *, const char *, const char *));
> int _EXFUN(_fprintf_r, (struct _reent *, FILE *, const char *, ...)
> _ATTRIBUTE ((__format__ (__printf__, 3, 4))));
>@@ -376,6 +393,7 @@ int _EXFUN(_iscanf_r, (struct _reent *,
> _ATTRIBUTE ((__format__ (__scanf__, 2, 3))));
> int _EXFUN(_mkstemp_r, (struct _reent *, char *));
> char * _EXFUN(_mktemp_r, (struct _reent *, char *));
>+FILE * _EXFUN(_open_memstream_r, (struct _reent *, char **, size_t *));
> void _EXFUN(_perror_r, (struct _reent *, const char *));
> int _EXFUN(_printf_r, (struct _reent *, const char *, ...)
> _ATTRIBUTE ((__format__ (__printf__, 2, 3))));
>
>
>
>
More information about the Newlib
mailing list