diff options
Diffstat (limited to 'lib/libc/stdio')
-rw-r--r-- | lib/libc/stdio/Makefile.inc | 5 | ||||
-rw-r--r-- | lib/libc/stdio/Symbol.map | 1 | ||||
-rw-r--r-- | lib/libc/stdio/fdopen.c | 6 | ||||
-rw-r--r-- | lib/libc/stdio/flags.c | 5 | ||||
-rw-r--r-- | lib/libc/stdio/fmemopen.c | 258 | ||||
-rw-r--r-- | lib/libc/stdio/fopen.3 | 71 | ||||
-rw-r--r-- | lib/libc/stdio/freopen.c | 5 | ||||
-rw-r--r-- | lib/libc/stdio/getline.3 | 4 | ||||
-rw-r--r-- | lib/libc/stdio/printf.3 | 2 |
9 files changed, 345 insertions, 12 deletions
diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index 6570051..bc3b9e5 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -8,7 +8,8 @@ SRCS+= _flock_stub.c asprintf.c clrerr.c dprintf.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 \ diff --git a/lib/libc/stdio/Symbol.map b/lib/libc/stdio/Symbol.map index cafb0c6..b78de5a 100644 --- a/lib/libc/stdio/Symbol.map +++ b/lib/libc/stdio/Symbol.map @@ -155,6 +155,7 @@ FBSD_1.3 { getwchar_l; putwc_l; putwchar_l; + fmemopen; }; FBSDprivate_1.0 { diff --git a/lib/libc/stdio/fdopen.c b/lib/libc/stdio/fdopen.c index 26e2cd7..8fc90a4 100644 --- a/lib/libc/stdio/fdopen.c +++ b/lib/libc/stdio/fdopen.c @@ -80,6 +80,12 @@ fdopen(fd, mode) if ((fp = __sfp()) == NULL) return (NULL); + + if ((oflags & O_CLOEXEC) && _fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + fp->_flags = 0; + return (NULL); + } + fp->_flags = flags; /* * If opened for appending, but underlying descriptor does not have diff --git a/lib/libc/stdio/flags.c b/lib/libc/stdio/flags.c index 0b6b075..e445fed 100644 --- a/lib/libc/stdio/flags.c +++ b/lib/libc/stdio/flags.c @@ -97,6 +97,7 @@ __sflags(mode, optr) /* 'x' means exclusive (fail if the file exists) */ if (*mode == 'x') { + mode++; if (m == O_RDONLY) { errno = EINVAL; return (0); @@ -104,6 +105,10 @@ __sflags(mode, optr) o |= O_EXCL; } + /* set close-on-exec */ + if (*mode == 'e') + o |= O_CLOEXEC; + *optr = m | o; return (ret); } diff --git a/lib/libc/stdio/fmemopen.c b/lib/libc/stdio/fmemopen.c new file mode 100644 index 0000000..581a91e --- /dev/null +++ b/lib/libc/stdio/fmemopen.c @@ -0,0 +1,258 @@ +/*- + * Copyright (C) 2013 Pietro Cerutti <gahr@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 <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "local.h" + +struct fmemopen_cookie +{ + char *buf; /* pointer to the memory region */ + bool 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); +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) +{ + 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); + } + + ck = malloc(sizeof(struct fmemopen_cookie)); + if (ck == NULL) { + return (NULL); + } + + ck->off = 0; + ck->size = size; + + /* 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); + 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'; + } + + /* 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; + } + + f = funopen(ck, + flags & O_WRONLY ? NULL : fmemopen_read, + flags & O_RDONLY ? NULL : fmemopen_write, + fmemopen_seek, fmemopen_close); + + 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, NULL, _IONBF, 0); + + return (f); +} + +static int +fmemopen_read(void *cookie, char *buf, int nbytes) +{ + 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); +} + +static int +fmemopen_write(void *cookie, const char *buf, int nbytes) +{ + struct fmemopen_cookie *ck = cookie; + + if (nbytes > ck->size - ck->off) + nbytes = ck->size - ck->off; + + if (nbytes == 0) + return (0); + + memcpy(ck->buf + ck->off, buf, nbytes); + + ck->off += nbytes; + + 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); +} + +static fpos_t +fmemopen_seek(void *cookie, fpos_t offset, int whence) +{ + struct fmemopen_cookie *ck = cookie; + + + switch (whence) { + case SEEK_SET: + if (offset > ck->size) { + errno = EINVAL; + return (-1); + } + ck->off = offset; + break; + + case SEEK_CUR: + if (ck->off + offset > ck->size) { + errno = EINVAL; + return (-1); + } + ck->off += offset; + break; + + case SEEK_END: + if (offset > 0 || -offset > ck->len) { + errno = EINVAL; + return (-1); + } + ck->off = ck->len + offset; + break; + + default: + errno = EINVAL; + return (-1); + } + + return (ck->off); +} + +static int +fmemopen_close(void *cookie) +{ + struct fmemopen_cookie *ck = cookie; + + if (ck->own) + free(ck->buf); + + free(ck); + + return (0); +} diff --git a/lib/libc/stdio/fopen.3 b/lib/libc/stdio/fopen.3 index 08438e7..a07be38 100644 --- a/lib/libc/stdio/fopen.3 +++ b/lib/libc/stdio/fopen.3 @@ -32,13 +32,14 @@ .\" @(#)fopen.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd October 17, 2011 +.Dd January 30, 2013 .Dt FOPEN 3 .Os .Sh NAME .Nm fopen , .Nm fdopen , -.Nm freopen +.Nm freopen , +.Nm fmemopen .Nd stream open functions .Sh LIBRARY .Lb libc @@ -50,6 +51,8 @@ .Fn fdopen "int fildes" "const char *mode" .Ft FILE * .Fn freopen "const char *path" "const char *mode" "FILE *stream" +.Ft FILE * +.Fn fmemopen "void *restrict *buf" "size_t size" "const char * restrict mode" .Sh DESCRIPTION The .Fn fopen @@ -97,6 +100,14 @@ or causes the .Fn fopen call to fail if the file already exists. +An optional +.Dq Li e +following the above +causes the +.Fn fopen +call to set the +.Dv FD_CLOEXEC +flag on the underlying file descriptor. .Pp The .Fa mode @@ -107,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 @@ -144,6 +157,11 @@ of the stream must be compatible with the mode of the file descriptor. The .Dq Li x mode option is ignored. +If the +.Dq Li e +mode option is present, the +.Dv FD_CLOEXEC +flag is set, otherwise it remains unchanged. When the stream is closed via .Xr fclose 3 , .Fa fildes @@ -189,6 +207,34 @@ 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 is 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 +allocates +.Fa size +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 , @@ -212,16 +258,18 @@ argument to .Fn fopen , .Fn fdopen , +.Fn freopen , or -.Fn freopen +.Fn fmemopen was invalid. .El .Pp The .Fn fopen , -.Fn fdopen -and +.Fn fdopen , .Fn freopen +and +.Fn fmemopen functions may also fail and set .Va errno @@ -277,3 +325,14 @@ The function conforms to .St -p1003.1-88 . +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 . +The ``b'' mode does not conform to any standard +but is also supported by glibc. diff --git a/lib/libc/stdio/freopen.c b/lib/libc/stdio/freopen.c index be7bc8a..8d1ec91 100644 --- a/lib/libc/stdio/freopen.c +++ b/lib/libc/stdio/freopen.c @@ -118,6 +118,8 @@ freopen(file, mode, fp) (void) ftruncate(fp->_file, (off_t)0); if (!(oflags & O_APPEND)) (void) _sseek(fp, (fpos_t)0, SEEK_SET); + if (oflags & O_CLOEXEC) + (void) _fcntl(fp->_file, F_SETFD, FD_CLOEXEC); f = fp->_file; isopen = 0; wantfd = -1; @@ -194,7 +196,8 @@ finish: * assume stderr is always fd STDERR_FILENO, even if being freopen'd. */ if (wantfd >= 0) { - if (_dup2(f, wantfd) >= 0) { + if ((oflags & O_CLOEXEC ? _fcntl(f, F_DUP2FD_CLOEXEC, wantfd) : + _dup2(f, wantfd)) >= 0) { (void)_close(f); f = wantfd; } else diff --git a/lib/libc/stdio/getline.3 b/lib/libc/stdio/getline.3 index 6061bfc..2161999 100644 --- a/lib/libc/stdio/getline.3 +++ b/lib/libc/stdio/getline.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 30, 2010 +.Dd November 30, 2012 .Dt GETLINE 3 .Os .Sh NAME @@ -76,7 +76,7 @@ The .Fn getdelim and .Fn getline -functions return the number of characters written, excluding the +functions return the number of characters stored in the buffer, excluding the terminating .Dv NUL character. diff --git a/lib/libc/stdio/printf.3 b/lib/libc/stdio/printf.3 index 0d9339e..05c30dc 100644 --- a/lib/libc/stdio/printf.3 +++ b/lib/libc/stdio/printf.3 @@ -267,7 +267,7 @@ number produced by a signed conversion. A .Cm + overrides a space if both are used. -.It Sq Cm ' +.It So "'" Sc (apostrophe) Decimal conversions .Cm ( d , u , or |