diff options
author | gahr <gahr@FreeBSD.org> | 2013-01-31 16:39:50 +0000 |
---|---|---|
committer | gahr <gahr@FreeBSD.org> | 2013-01-31 16:39:50 +0000 |
commit | 3706909a8374ad86e702bde80ab176b0c1fcb9b7 (patch) | |
tree | 639be13fc132482e3750c6edb3e6b1fcfb09368b /lib/libc/stdio/fmemopen.c | |
parent | 96dbcebea0dd37d6891a4dff6c621d8550f93769 (diff) | |
download | FreeBSD-src-3706909a8374ad86e702bde80ab176b0c1fcb9b7.zip FreeBSD-src-3706909a8374ad86e702bde80ab176b0c1fcb9b7.tar.gz |
- Remove underscores from the internal structure name, as it doesn't collide
with the user's namespace.
- Correct size and position variables type from long to size_t.
- Do not set errno to ENOMEM on malloc failure, as malloc already does so.
- Implement the concept of "buffer data length", which mandates what SEEK_END
refers to and the allowed extent for a read.
- Use NULL as read-callback if the buffer is opened in write-only mode.
Conversely, use NULL as write-callback when opened in read-only mode.
- Implement the handling of the ``b'' character in the mode argument. A binary
buffer differs from a text buffer (default mode if ``b'' is omitted) in that
NULL bytes are never appended to writes and that the "buffer data length"
equals to the size of the buffer.
- Remove shall from the man page. Use indicative instead. Also, specify that
the ``b'' flag does not conform with POSIX but is supported by glibc.
- Update the regression test so that the ``b'' functionality and the "buffer
data length" concepts are tested.
- Minor style(9) corrections.
Suggested by: jilles
Reviewed by: cognet
Approved by: cognet
Diffstat (limited to 'lib/libc/stdio/fmemopen.c')
-rw-r--r-- | lib/libc/stdio/fmemopen.c | 131 |
1 files changed, 104 insertions, 27 deletions
diff --git a/lib/libc/stdio/fmemopen.c b/lib/libc/stdio/fmemopen.c index 2cdf324..dec2c86 100644 --- a/lib/libc/stdio/fmemopen.c +++ b/lib/libc/stdio/fmemopen.c @@ -26,17 +26,21 @@ SUCH DAMAGE. #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> +#include "local.h" -struct __fmemopen_cookie +struct fmemopen_cookie { - char *buf; /* pointer to the memory region */ - char own; /* did we allocate the buffer ourselves? */ - long len; /* buffer length in bytes */ - long off; /* current offset into the buffer */ + char *buf; /* pointer to the memory region */ + char own; /* did we allocate the buffer ourselves? */ + char bin; /* is this a binary buffer? */ + size_t size; /* buffer length in bytes */ + size_t len; /* data length in bytes */ + size_t off; /* current offset into the buffer */ }; static int fmemopen_read (void *cookie, char *buf, int nbytes); @@ -47,33 +51,95 @@ static int fmemopen_close (void *cookie); FILE * fmemopen (void * __restrict buf, size_t size, const char * __restrict mode) { - /* allocate cookie */ - struct __fmemopen_cookie *ck = malloc (sizeof (struct __fmemopen_cookie)); + struct fmemopen_cookie *ck; + FILE *f; + int flags, rc; + + /* + * Retrieve the flags as used by open(2) from the mode argument, and + * validate them. + * */ + rc = __sflags (mode, &flags); + if (rc == 0) { + errno = EINVAL; + return (NULL); + } + + /* + * There's no point in requiring an automatically allocated buffer + * in write-only mode. + */ + if (!(flags & O_RDWR) && buf == NULL) { + errno = EINVAL; + return (NULL); + } + + /* Allocate a cookie. */ + ck = malloc (sizeof (struct fmemopen_cookie)); if (ck == NULL) { - errno = ENOMEM; return (NULL); } - ck->off = 0; - ck->len = size; + ck->off = 0; + ck->size = size; - /* do we have to allocate the buffer ourselves? */ + /* Check whether we have to allocate the buffer ourselves. */ ck->own = ((ck->buf = buf) == NULL); if (ck->own) { ck->buf = malloc (size); if (ck->buf == NULL) { free (ck); - errno = ENOMEM; return (NULL); } + } + + /* + * POSIX distinguishes between w+ and r+, in that w+ is supposed to + * truncate the buffer. + */ + if (ck->own || mode[0] == 'w') { ck->buf[0] = '\0'; } - if (mode[0] == 'a') - ck->off = strnlen(ck->buf, ck->len); + /* Check for binary mode. */ + ck->bin = strchr(mode, 'b') != NULL; + + /* + * The size of the current buffer contents is set depending on the + * mode: + * + * for append (text-mode), the position of the first NULL byte, or the + * size of the buffer if none is found + * + * for append (binary-mode), the size of the buffer + * + * for read, the size of the buffer + * + * for write, 0 + */ + switch (mode[0]) { + case 'a': + if (ck->bin) { + /* + * This isn't useful, since the buffer isn't + * allowed to grow. + */ + ck->off = ck->len = size; + } else + ck->off = ck->len = strnlen(ck->buf, ck->size); + break; + case 'r': + ck->len = size; + break; + case 'w': + ck->len = 0; + break; + } - /* actuall wrapper */ - FILE *f = funopen ((void *)ck, fmemopen_read, fmemopen_write, + /* Actuall wrapper. */ + f = funopen ((void *)ck, + flags & O_WRONLY ? NULL : fmemopen_read, + flags & O_RDONLY ? NULL : fmemopen_write, fmemopen_seek, fmemopen_close); if (f == NULL) { @@ -83,8 +149,10 @@ fmemopen (void * __restrict buf, size_t size, const char * __restrict mode) return (NULL); } - /* turn off buffering, so a write past the end of the buffer - * correctly returns a short object count */ + /* + * Turn off buffering, so a write past the end of the buffer + * correctly returns a short object count. + */ setvbuf (f, (char *) NULL, _IONBF, 0); return (f); @@ -93,7 +161,7 @@ fmemopen (void * __restrict buf, size_t size, const char * __restrict mode) static int fmemopen_read (void *cookie, char *buf, int nbytes) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; if (nbytes > ck->len - ck->off) nbytes = ck->len - ck->off; @@ -111,10 +179,10 @@ fmemopen_read (void *cookie, char *buf, int nbytes) static int fmemopen_write (void *cookie, const char *buf, int nbytes) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; - if (nbytes > ck->len - ck->off) - nbytes = ck->len - ck->off; + if (nbytes > ck->size - ck->off) + nbytes = ck->size - ck->off; if (nbytes == 0) return (0); @@ -123,7 +191,16 @@ fmemopen_write (void *cookie, const char *buf, int nbytes) ck->off += nbytes; - if (ck->off < ck->len && ck->buf[ck->off - 1] != '\0') + if (ck->off > ck->len) + ck->len = ck->off; + + /* + * We append a NULL byte if all these conditions are met: + * - the buffer is not binary + * - the buffer is not full + * - the data just written doesn't already end with a NULL byte + */ + if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') ck->buf[ck->off] = '\0'; return (nbytes); @@ -132,12 +209,12 @@ fmemopen_write (void *cookie, const char *buf, int nbytes) static fpos_t fmemopen_seek (void *cookie, fpos_t offset, int whence) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; switch (whence) { case SEEK_SET: - if (offset > ck->len) { + if (offset > ck->size) { errno = EINVAL; return (-1); } @@ -145,7 +222,7 @@ fmemopen_seek (void *cookie, fpos_t offset, int whence) break; case SEEK_CUR: - if (ck->off + offset > ck->len) { + if (ck->off + offset > ck->size) { errno = EINVAL; return (-1); } @@ -171,7 +248,7 @@ fmemopen_seek (void *cookie, fpos_t offset, int whence) static int fmemopen_close (void *cookie) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; if (ck->own) free (ck->buf); |