svn commit: r246120 - in head: include lib/libc/stdio tools/regression/lib/libc/stdio
Jilles Tjoelker
jilles at stack.nl
Wed Jan 30 23:54:35 UTC 2013
On Wed, Jan 30, 2013 at 02:59:26PM +0000, Pietro Cerutti wrote:
> Author: gahr (ports committer)
> Date: Wed Jan 30 14:59:26 2013
> New Revision: 246120
> URL: http://svnweb.freebsd.org/changeset/base/246120
> Log:
> Add fmemopen(3), an interface to get a FILE * from a buffer in memory, along
> with the respective regression test.
> See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html
Various comments are inline.
> Added:
> head/lib/libc/stdio/fmemopen.c (contents, props changed)
> head/tools/regression/lib/libc/stdio/test-fmemopen.c (contents, props changed)
> head/tools/regression/lib/libc/stdio/test-fmemopen.t (contents, props changed)
> Modified:
> head/include/stdio.h (contents, props changed)
> head/lib/libc/stdio/Makefile.inc (contents, props changed)
> head/lib/libc/stdio/Symbol.map (contents, props changed)
> head/lib/libc/stdio/fopen.3 (contents, props changed)
> Modified: head/lib/libc/stdio/Makefile.inc
> ==============================================================================
> --- head/lib/libc/stdio/Makefile.inc Wed Jan 30 13:14:34 2013 (r246119)
> +++ head/lib/libc/stdio/Makefile.inc Wed Jan 30 14:59:26 2013 (r246120)
> @@ -8,7 +8,8 @@ SRCS+= _flock_stub.c asprintf.c clrerr.c
> fclose.c fcloseall.c fdopen.c \
> feof.c ferror.c fflush.c fgetc.c fgetln.c fgetpos.c fgets.c fgetwc.c \
> fgetwln.c fgetws.c \
> - fileno.c findfp.c flags.c fopen.c fprintf.c fpurge.c fputc.c fputs.c \
> + fileno.c findfp.c flags.c fmemopen.c fopen.c fprintf.c fpurge.c \
> + fputc.c fputs.c \
> fputwc.c fputws.c fread.c freopen.c fscanf.c fseek.c fsetpos.c \
> ftell.c funopen.c fvwrite.c fwalk.c fwide.c fwprintf.c fwscanf.c \
> fwrite.c getc.c getchar.c getdelim.c getline.c \
> @@ -48,7 +49,7 @@ MLINKS+=ferror.3 ferror_unlocked.3 \
> MLINKS+=fflush.3 fpurge.3
> MLINKS+=fgets.3 gets.3
> MLINKS+=flockfile.3 ftrylockfile.3 flockfile.3 funlockfile.3
> -MLINKS+=fopen.3 fdopen.3 fopen.3 freopen.3
> +MLINKS+=fopen.3 fdopen.3 fopen.3 freopen.3 fopen.3 fmemopen.3
> MLINKS+=fputs.3 puts.3
> MLINKS+=fread.3 fwrite.3
> MLINKS+=fseek.3 fgetpos.3 fseek.3 fseeko.3 fseek.3 fsetpos.3 fseek.3 ftell.3 \
I think the man page could be a separate page without problem. There is
a fair amount of behaviour unique to fmemopen().
> Added: head/lib/libc/stdio/fmemopen.c
> ==============================================================================
> --- /dev/null 00:00:00 1970 (empty, because file is newly added)
> +++ head/lib/libc/stdio/fmemopen.c Wed Jan 30 14:59:26 2013 (r246120)
> @@ -0,0 +1,182 @@
> +/*-
> +Copyright (C) 2013 Pietro Cerutti <gahr at FreeBSD.org>
> +
> +Redistribution and use in source and binary forms, with or without
> +modification, are permitted provided that the following conditions
> +are met:
> +1. Redistributions of source code must retain the above copyright
> + notice, this list of conditions and the following disclaimer.
> +2. Redistributions in binary form must reproduce the above copyright
> + notice, this list of conditions and the following disclaimer in the
> + documentation and/or other materials provided with the distribution.
> +
> +THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
> +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> +ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
> +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> +SUCH DAMAGE.
> +*/
> +
> +#include <sys/cdefs.h>
> +__FBSDID("$FreeBSD$");
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +struct __fmemopen_cookie
I think the underscores are not necessary if the name cannot collide
with a user's name.
> +{
> + char *buf; /* pointer to the memory region */
> + char own; /* did we allocate the buffer ourselves? */
> + long len; /* buffer length in bytes */
This is in fact a size_t, as passed to fmemopen().
> + long off; /* current offset into the buffer */
This should probably be a size_t as well.
> +};
> +
> +static int fmemopen_read (void *cookie, char *buf, int nbytes);
> +static int fmemopen_write (void *cookie, const char *buf, int nbytes);
> +static fpos_t fmemopen_seek (void *cookie, fpos_t offset, int whence);
> +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));
> + if (ck == NULL) {
> + errno = ENOMEM;
malloc() already sets errno when it fails; you need not do it yourself.
> + return (NULL);
> + }
> +
> + ck->off = 0;
> + ck->len = size;
> +
> + /* do 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);
> + }
> + ck->buf[0] = '\0';
> + }
> +
> + if (mode[0] == 'a')
> + ck->off = strnlen(ck->buf, ck->len);
This is the only use of 'mode'. POSIX mentions a difference between 'r'
and 'w' with regard to another piece of internal state: the notion of
the size of the current buffer contents, which seems completely missing
here. There was an interpretation issued about that as well,
http://austingroupbugs.net/view.php?id=587 .
glibc does something different when the mode specifies binary ('b') and
POSIX will likely require this in future versions, see
http://austingroupbugs.net/view.php?id=456 . It is probably best to fail
with [EINVAL] if this special behaviour is not implemented so that
people are not surprised.
> +
> + /* actuall wrapper */
> + FILE *f = funopen ((void *)ck, fmemopen_read, fmemopen_write,
> + fmemopen_seek, fmemopen_close);
The read and write functions should be passed as NULL instead when the
mode does not permit reading or writing respectively.
> +
> + if (f == NULL) {
> + if (ck->own)
> + free (ck->buf);
> + free (ck);
> + return (NULL);
> + }
> +
> + /* turn off buffering, so a write past the end of the buffer
> + * correctly returns a short object count */
> + setvbuf (f, (char *) NULL, _IONBF, 0);
This is indeed required but I think it makes the stream rather slow
(because it will call fmemopen_read/fmemopen_write for single bytes). If
this is a problem, I think it should be fixed by making unbuffered mode
smarter in the rest of stdio and not here.
> +
> + return (f);
> +}
> +
> +static int
> +fmemopen_read (void *cookie, char *buf, int nbytes)
stdio is strange, requiring 'int' here :(
> +{
> + struct __fmemopen_cookie *ck = cookie;
> +
> + if (nbytes > ck->len - ck->off)
> + nbytes = ck->len - ck->off;
> +
> + if (nbytes == 0)
> + return (0);
> +
> + memcpy (buf, ck->buf + ck->off, nbytes);
> +
> + ck->off += nbytes;
> +
> + return (nbytes);
> +}
> [snip]
> Modified: head/lib/libc/stdio/fopen.3
> ==============================================================================
> --- head/lib/libc/stdio/fopen.3 Wed Jan 30 13:14:34 2013 (r246119)
> +++ head/lib/libc/stdio/fopen.3 Wed Jan 30 14:59:26 2013 (r246120)
> @@ -32,13 +32,14 @@
> [snip]
> @@ -202,6 +205,29 @@ standard text stream
> .Dv ( stderr , stdin ,
> or
> .Dv stdout ) .
> +.Pp
> +The
> +.Fn fmemopen
> +function
> +associates the buffer given by the
> +.Fa buf
> +and
> +.Fa size
> +arguments with a stream.
> +The
> +.Fa buf
> +argument shall be either a null pointer or point to a buffer that
> +is at least
> +.Fa size
> +bytes long.
> +If a null pointer is specified as the
> +.Fa buf
> +argument,
> +.Fn fmemopen
> +shall allocate
We don't usually use 'shall' for requirements on the implementation in
man pages. We made it such that it is compliant; thus, it is, not shall
be.
> [snip]
> @@ -294,3 +322,8 @@ The
> .Dq Li e
> mode option does not conform to any standard
> but is also supported by glibc.
> +The
> +.Fn fmemopen
> +function
> +conforms to
> +.St -p1003.1-2008 .
Quite a bit of functionality is missing.
> Added: head/tools/regression/lib/libc/stdio/test-fmemopen.c
> ==============================================================================
> --- /dev/null 00:00:00 1970 (empty, because file is newly added)
> +++ head/tools/regression/lib/libc/stdio/test-fmemopen.c Wed Jan 30 14:59:26 2013 (r246120)
> @@ -0,0 +1,143 @@
> +/*-
> +Copyright (C) 2013 Pietro Cerutti <gahr at FreeBSD.org>
> +
> +Redistribution and use in source and binary forms, with or without
> +modification, are permitted provided that the following conditions
> +are met:
> +1. Redistributions of source code must retain the above copyright
> + notice, this list of conditions and the following disclaimer.
> +2. Redistributions in binary form must reproduce the above copyright
> + notice, this list of conditions and the following disclaimer in the
> + documentation and/or other materials provided with the distribution.
> +
> +THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
> +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> +ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
> +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> +SUCH DAMAGE.
> +*/
> +
> +/*
> + * Test basic FILE * functions (fread, fwrite, fseek, fclose) against
> + * a FILE * retrieved using fmemopen()
> + */
> +
> +#include <sys/cdefs.h>
> +__FBSDID("$FreeBSD$");
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <strings.h>
> +
> [snip]
> +void
> +test_autoalloc ()
> +{
> + /*
> + * let fmemopen allocate the buffer
> + */
> +
> + char str[] = "A quick test";
> + FILE *fp;
> + long pos;
> + size_t nofw, nofr, i;
> + int rc;
> +
> + /* open a FILE * using fmemopen */
> + fp = fmemopen (NULL, 512, "w");
This does not make sense in a real program: there is no way to read the
data back. POSIX permits fmemopen(NULL, ...) to fail when the mode is
read-only or write-only.
> + assert (fp != NULL);
> +
> + /* fill the buffer */
> + for (i = 0; i < 512; i++) {
> + nofw = fwrite ("a", 1, 1, fp);
> + assert (nofw == 1);
> + }
> +
> + /* get the current position into the stream */
> + pos = ftell (fp);
> + assert (pos == 512);
> +
> + /* try to write past the end, we should get a short object count (0) */
> + nofw = fwrite ("a", 1, 1, fp);
> + assert (nofw == 0);
> +
> + /* close the FILE * */
> + rc = fclose (fp);
> + assert (rc == 0);
> +}
> [snip]
--
Jilles Tjoelker
More information about the svn-src-all
mailing list