summaryrefslogtreecommitdiffstats
path: root/lib/libc/stdio
diff options
context:
space:
mode:
authorgahr <gahr@FreeBSD.org>2013-01-31 16:39:50 +0000
committergahr <gahr@FreeBSD.org>2013-01-31 16:39:50 +0000
commit3706909a8374ad86e702bde80ab176b0c1fcb9b7 (patch)
tree639be13fc132482e3750c6edb3e6b1fcfb09368b /lib/libc/stdio
parent96dbcebea0dd37d6891a4dff6c621d8550f93769 (diff)
downloadFreeBSD-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')
-rw-r--r--lib/libc/stdio/fmemopen.c131
-rw-r--r--lib/libc/stdio/fopen.319
2 files changed, 118 insertions, 32 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);
diff --git a/lib/libc/stdio/fopen.3 b/lib/libc/stdio/fopen.3
index 41d66b7..a07be38 100644
--- a/lib/libc/stdio/fopen.3
+++ b/lib/libc/stdio/fopen.3
@@ -118,7 +118,9 @@ after either the
or the first letter.
This is strictly for compatibility with
.St -isoC
-and has no effect; the ``b'' is ignored.
+and has effect only for
+.Fn fmemopen
+; otherwise the ``b'' is ignored.
.Pp
Any created files will have mode
.Do Dv S_IRUSR
@@ -216,7 +218,7 @@ and
arguments with a stream.
The
.Fa buf
-argument shall be either a null pointer or point to a buffer that
+argument is either a null pointer or point to a buffer that
is at least
.Fa size
bytes long.
@@ -224,10 +226,15 @@ If a null pointer is specified as the
.Fa buf
argument,
.Fn fmemopen
-shall allocate
+allocates
.Fa size
-bytes of memory. This buffer shall be automatically freed when the
-stream is closed.
+bytes of memory. This buffer is automatically freed when the
+stream is closed. Buffers can be opened in text-mode (default) or binary-mode
+(if ``b'' is present in the second or third position of the
+.Fa mode
+argument). Buffers opened in text-mode make sure that writes are terminated with
+a NULL byte, if the last write hasn't filled up the whole buffer. Buffers
+opened in binary-mode never append a NULL byte.
.Sh RETURN VALUES
Upon successful completion
.Fn fopen ,
@@ -327,3 +334,5 @@ The
function
conforms to
.St -p1003.1-2008 .
+The ``b'' mode does not conform to any standard
+but is also supported by glibc.
OpenPOWER on IntegriCloud