From 6ddcdb9da510ddf2b6309006ded0827f467e23b4 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 2 Aug 2007 20:23:06 +0000 Subject: [PATCH] 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. --- newlib/ChangeLog | 15 ++ newlib/libc/include/stdio.h | 30 ++- newlib/libc/stdio/Makefile.am | 6 + newlib/libc/stdio/Makefile.in | 22 ++ newlib/libc/stdio/fmemopen.c | 371 +++++++++++++++++++++++++++++ newlib/libc/stdio/fopencookie.c | 8 +- newlib/libc/stdio/open_memstream.c | 330 +++++++++++++++++++++++++ newlib/libc/stdio/stdio.tex | 8 + 8 files changed, 780 insertions(+), 10 deletions(-) create mode 100644 newlib/libc/stdio/fmemopen.c create mode 100644 newlib/libc/stdio/open_memstream.c diff --git a/newlib/ChangeLog b/newlib/ChangeLog index 6f506cc8c..cb3f908a6 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,18 @@ +2007-08-02 Eric Blake + + 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. + 2007-07-31 Eric Blake More POSIX stream corner cases. diff --git a/newlib/libc/include/stdio.h b/newlib/libc/include/stdio.h index b387a4455..51c72a447 100644 --- a/newlib/libc/include/stdio.h +++ b/newlib/libc/include/stdio.h @@ -244,11 +244,9 @@ char * _EXFUN(asnprintf, (char *, size_t *, const char *, ...) _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 char *, __VALIST) _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 char *, __VALIST) #endif /* !__STRICT_ANSI__ */ /* - * Routines in POSIX 1003.1. + * Routines in POSIX 1003.1:2001. */ #ifndef __STRICT_ANSI__ @@ -329,6 +325,26 @@ int _EXFUN(putc_unlocked, (int, FILE *)); 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 *, FILE *, const char *, ...) _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 *, const char *, ...) _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)))); diff --git a/newlib/libc/stdio/Makefile.am b/newlib/libc/stdio/Makefile.am index 2b7cb62d9..4d3daaefb 100644 --- a/newlib/libc/stdio/Makefile.am +++ b/newlib/libc/stdio/Makefile.am @@ -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 @@ -257,6 +262,7 @@ $(lpfx)fwalk.$(oext): local.h $(lpfx)fwrite.$(oext): local.h fvwrite.h $(lpfx)iscanf.$(oext): local.h $(lpfx)makebuf.$(oext): local.h +$(lpfx)open_memstream.$(oext): local.h $(lpfx)puts.$(oext): fvwrite.h $(lpfx)refill.$(oext): local.h $(lpfx)scanf.$(oext): local.h diff --git a/newlib/libc/stdio/Makefile.in b/newlib/libc/stdio/Makefile.in index 32dfae2a2..42e3e6120 100644 --- a/newlib/libc/stdio/Makefile.in +++ b/newlib/libc/stdio/Makefile.in @@ -110,8 +110,10 @@ am__objects_1 = lib_a-clearerr.$(OBJEXT) lib_a-fclose.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-asnprintf.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-diprintf.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-dprintf.$(OBJEXT) \ +@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-fmemopen.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-fopencookie.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-funopen.$(OBJEXT) \ +@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-open_memstream.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-vasniprintf.$(OBJEXT) \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ lib_a-vasnprintf.$(OBJEXT) @USE_LIBTOOL_FALSE@am_lib_a_OBJECTS = $(am__objects_1) \ @@ -141,8 +143,10 @@ am__objects_4 = clearerr.lo fclose.lo fdopen.lo feof.lo ferror.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ asnprintf.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ diprintf.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ dprintf.lo \ +@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ fmemopen.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ fopencookie.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ funopen.lo \ +@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ open_memstream.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ vasniprintf.lo \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ vasnprintf.lo @USE_LIBTOOL_TRUE@am_libstdio_la_OBJECTS = $(am__objects_4) \ @@ -423,8 +427,10 @@ GENERAL_SOURCES = \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ asnprintf.c \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ diprintf.c \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ dprintf.c \ +@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ fmemopen.c \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ fopencookie.c \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ funopen.c \ +@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ open_memstream.c \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ vasniprintf.c \ @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@ vasnprintf.c @@ -463,6 +469,7 @@ CHEWOUT_FILES = \ fgetpos.def \ fgets.def \ fileno.def \ + fmemopen.def \ fopen.def \ fopencookie.def \ fputc.def \ @@ -483,6 +490,7 @@ CHEWOUT_FILES = \ gets.def \ getw.def \ mktemp.def \ + open_memstream.def \ perror.def \ putc.def \ putc_u.def \ @@ -1138,6 +1146,12 @@ lib_a-dprintf.o: dprintf.c lib_a-dprintf.obj: dprintf.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-dprintf.obj `if test -f 'dprintf.c'; then $(CYGPATH_W) 'dprintf.c'; else $(CYGPATH_W) '$(srcdir)/dprintf.c'; fi` +lib_a-fmemopen.o: fmemopen.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-fmemopen.o `test -f 'fmemopen.c' || echo '$(srcdir)/'`fmemopen.c + +lib_a-fmemopen.obj: fmemopen.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-fmemopen.obj `if test -f 'fmemopen.c'; then $(CYGPATH_W) 'fmemopen.c'; else $(CYGPATH_W) '$(srcdir)/fmemopen.c'; fi` + lib_a-fopencookie.o: fopencookie.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-fopencookie.o `test -f 'fopencookie.c' || echo '$(srcdir)/'`fopencookie.c @@ -1150,6 +1164,12 @@ lib_a-funopen.o: funopen.c lib_a-funopen.obj: funopen.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-funopen.obj `if test -f 'funopen.c'; then $(CYGPATH_W) 'funopen.c'; else $(CYGPATH_W) '$(srcdir)/funopen.c'; fi` +lib_a-open_memstream.o: open_memstream.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-open_memstream.o `test -f 'open_memstream.c' || echo '$(srcdir)/'`open_memstream.c + +lib_a-open_memstream.obj: open_memstream.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-open_memstream.obj `if test -f 'open_memstream.c'; then $(CYGPATH_W) 'open_memstream.c'; else $(CYGPATH_W) '$(srcdir)/open_memstream.c'; fi` + lib_a-vasniprintf.o: vasniprintf.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-vasniprintf.o `test -f 'vasniprintf.c' || echo '$(srcdir)/'`vasniprintf.c @@ -1342,6 +1362,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 @@ -1355,6 +1376,7 @@ $(lpfx)fwalk.$(oext): local.h $(lpfx)fwrite.$(oext): local.h fvwrite.h $(lpfx)iscanf.$(oext): local.h $(lpfx)makebuf.$(oext): local.h +$(lpfx)open_memstream.$(oext): local.h $(lpfx)puts.$(oext): fvwrite.h $(lpfx)refill.$(oext): local.h $(lpfx)scanf.$(oext): local.h diff --git a/newlib/libc/stdio/fmemopen.c b/newlib/libc/stdio/fmemopen.c new file mode 100644 index 000000000..4458d2176 --- /dev/null +++ b/newlib/libc/stdio/fmemopen.c @@ -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 +<>---open a stream around a fixed-length string + +INDEX + fmemopen + +ANSI_SYNOPSIS + #include + FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>, + const char *restrict <[mode]>); + +DESCRIPTION +<> creates a seekable <> stream that wraps a +fixed-length buffer of <[size]> bytes starting at <[buf]>. The stream +is opened with <[mode]> treated as in <>, where append mode +starts writing at the first NUL byte. If <[buf]> is NULL, then +<[size]> bytes are automatically provided as if by <>, 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 <>, the position starts at +<<0>>, and file size starts at <[size]> if <[buf]> was provided. If +<[mode]> starts with <>, the position and file size start at <<0>>, +and if <[buf]> was provided, the first byte is set to NUL. If +<[mode]> starts with <>, 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 +<> 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, +<> is returned, and <> 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: <>. +*/ + +#include +#include +#include +#include +#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 */ diff --git a/newlib/libc/stdio/fopencookie.c b/newlib/libc/stdio/fopencookie.c index 35afa3257..854bb49e9 100644 --- a/newlib/libc/stdio/fopencookie.c +++ b/newlib/libc/stdio/fopencookie.c @@ -122,9 +122,9 @@ _DEFUN(fcwriter, (ptr, cookie, buf, n), if (c->fp->_flags & __SAPP && c->fp->_seek) { #ifdef __LARGE64_FILES - c->fp->_seek64 (ptr, c->fp->_cookie, 0, SEEK_END); + c->fp->_seek64 (ptr, cookie, 0, SEEK_END); #else - c->fp->_seek (ptr, c->fp->_cookie, 0, SEEK_END); + c->fp->_seek (ptr, cookie, 0, SEEK_END); #endif } errno = 0; @@ -134,7 +134,7 @@ _DEFUN(fcwriter, (ptr, cookie, buf, n), } static _fpos_t -_DEFUN(fcseeker, (ptr, cookie, off, whence), +_DEFUN(fcseeker, (ptr, cookie, pos, whence), struct _reent *ptr _AND void *cookie _AND _fpos_t pos _AND @@ -162,7 +162,7 @@ _DEFUN(fcseeker, (ptr, cookie, off, whence), #ifdef __LARGE64_FILES static _fpos64_t -_DEFUN(fcseeker64, (ptr, cookie, off, whence), +_DEFUN(fcseeker64, (ptr, cookie, pos, whence), struct _reent *ptr _AND void *cookie _AND _fpos64_t pos _AND 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 a write stream around an arbitrary-length string + +INDEX + open_memstream + +ANSI_SYNOPSIS + #include + FILE *open_memstream(char **restrict <[buf]>, + size_t *restrict <[size]>); + +DESCRIPTION +<> creates a seekable <> stream that wraps an +arbitrary-length buffer, created as if by <>. 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 <> or <>, 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 <>. + +The stream is write-only, since the user can directly read *<[buf]> +after a flush; see <> for a way to wrap a string with a +readable stream. The user is responsible for calling <> on +the final *<[buf]> after <>. + +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, +<> is returned, and <> 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: <>. +*/ + +#include +#include +#include +#include +#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 */ diff --git a/newlib/libc/stdio/stdio.tex b/newlib/libc/stdio/stdio.tex index f31271523..582f43e37 100644 --- a/newlib/libc/stdio/stdio.tex +++ b/newlib/libc/stdio/stdio.tex @@ -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) @@ -123,6 +125,9 @@ structure. @page @include stdio/fileno.def +@page +@include stdio/fmemopen.def + @page @include stdio/fopen.def @@ -183,6 +188,9 @@ structure. @page @include stdio/mktemp.def +@page +@include stdio/open_memstream.def + @page @include stdio/perror.def