This is a wrapper over the glibc fopencookie() function.
We diverge from the FreeBSD, OpenBSD and DragonFlyBSD declarations,
because seekfn() there wrongly uses fpos_t, assuming it's an integral
type, and any code using that on a system where fpos_t is a struct
(such as GNU-based systems or NetBSD) will fail to build. In which case,
as the code has to be modified anyway, we might just as well use the
correct declaration.
AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])])
-AC_CHECK_FUNCS([clearenv dirfd __fpurge getexecname getline sysconf])
+AC_CHECK_FUNCS([clearenv dirfd fopencookie __fpurge getexecname getline sysconf])
AC_CONFIG_FILES([
Makefile
char *fgetln(FILE *fp, size_t *lenp);
+/*
+ * Note: We diverge from the FreeBSD, OpenBSD and DragonFlyBSD declarations,
+ * because seekfn() there wrongly uses fpos_t, assuming it's an integral
+ * type, and any code using that on a system where fpos_t is a struct
+ * (such as GNU-based systems or NetBSD) will fail to build. In which case,
+ * as the code has to be modified anyway, we might just as well use the
+ * correct declaration here.
+ */
+FILE *funopen(const void *cookie,
+ int (*readfn)(void *cookie, char *buf, int size),
+ int (*writefn)(void *cookie, const char *buf, int size),
+ off_t (*seekfn)(void *cookie, off_t offset, int whence),
+ int (*closefn)(void *cookie));
+
+#define fropen(cookie, fn) funopen(cookie, fn, NULL, NULL, NULL)
+#define fwopen(cookie, fn) funopen(cookie, NULL, fn, NULL, NULL)
+
int fpurge(FILE *fp);
__END_DECLS
flopen.3 \
fmtcheck.3 \
fparseln.3 \
+ funopen.3 \
getmode.3 \
getpeereid.3 \
getprogname.3 \
--- /dev/null
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Chris Torek.
+.\" 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.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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.
+.\"
+.\" @(#)funopen.3 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd March 19, 2004
+.Dt FUNOPEN 3
+.Os
+.Sh NAME
+.Nm funopen ,
+.Nm fropen ,
+.Nm fwopen
+.Nd open a stream
+.Sh LIBRARY
+.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
+.Lb libbsd
+.Sh SYNOPSIS
+.In bsd/stdio.h
+.Ft FILE *
+.Fn funopen "const void *cookie" "int (*readfn)(void *, char *, int)" "int (*writefn)(void *, const char *, int)" "off_t (*seekfn)(void *, off_t, int)" "int (*closefn)(void *)"
+.Ft FILE *
+.Fn fropen "void *cookie" "int (*readfn)(void *, char *, int)"
+.Ft FILE *
+.Fn fwopen "void *cookie" "int (*writefn)(void *, const char *, int)"
+.Sh DESCRIPTION
+The
+.Fn funopen
+function
+associates a stream with up to four
+.Dq Tn I/O No functions .
+Either
+.Fa readfn
+or
+.Fa writefn
+must be specified;
+the others can be given as an appropriately-typed
+.Dv NULL
+pointer.
+These
+.Tn I/O
+functions will be used to read, write, seek and
+close the new stream.
+.Pp
+In general, omitting a function means that any attempt to perform the
+associated operation on the resulting stream will fail.
+If the close function is omitted, closing the stream will flush
+any buffered output and then succeed.
+.Pp
+The calling conventions of
+.Fa readfn ,
+.Fa writefn ,
+.Fa seekfn
+and
+.Fa closefn
+must match those, respectively, of
+.Xr read 2 ,
+.Xr write 2 ,
+.Xr lseek 2 ,
+and
+.Xr close 2
+with the single exception that they are passed the
+.Fa cookie
+argument specified to
+.Fn funopen
+in place of the traditional file descriptor argument.
+.Pp
+Read and write
+.Tn I/O
+functions are allowed to change the underlying buffer
+on fully buffered or line buffered streams by calling
+.Xr setvbuf 3 .
+They are also not required to completely fill or empty the buffer.
+They are not, however, allowed to change streams from unbuffered to buffered
+or to change the state of the line buffering flag.
+They must also be prepared to have read or write calls occur on buffers other
+than the one most recently specified.
+.Pp
+All user
+.Tn I/O
+functions can report an error by returning \-1.
+Additionally, all of the functions should set the external variable
+.Va errno
+appropriately if an error occurs.
+.Pp
+An error on
+.Fn closefn
+does not keep the stream open.
+.Pp
+As a convenience, the include file
+.In stdio.h
+defines the macros
+.Fn fropen
+and
+.Fn fwopen
+as calls to
+.Fn funopen
+with only a read or write function specified.
+.Sh RETURN VALUES
+Upon successful completion,
+.Fn funopen
+returns a
+.Dv FILE
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fn funopen
+function
+was called without either a read or write function.
+The
+.Fn funopen
+function
+may also fail and set
+.Va errno
+for any of the errors
+specified for the routine
+.Xr malloc 3 .
+.El
+.Sh SEE ALSO
+.Xr fcntl 2 ,
+.Xr open 2 ,
+.Xr fclose 3 ,
+.Xr fopen 3 ,
+.Xr fseek 3 ,
+.Xr setbuf 3
+.Sh HISTORY
+The
+.Fn funopen
+functions first appeared in
+.Bx 4.4 .
+.Sh BUGS
+The
+.Fn funopen
+function
+may not be portable to systems other than
+.Bx .
+.Pp
+On
+.Fx ,
+.Ox
+and
+.Dx
+the
+.Fn funopen
+interface erroneously assumes that
+.Vt fpos_t
+is an integral type, and uses it in the
+.Fa seekfn
+hook; but because code using a
+.Fa seekfn
+hook will fail to build on systems where
+.Vt fpos_t
+is a struct, and it will need to be slightly fixed anyway, the
+implementation provided by libbsd (in the same way as
+.Nx )
+uses the correct
+.Vt off_t
+types.
fmtcheck.c \
fparseln.c \
fpurge.c \
+ funopen.c \
getpeereid.c \
hash/md5.c \
hash/md5hl.c \
--- /dev/null
+/*
+ * Copyright © 2011, 2013 Guillem Jover
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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
+ * THE AUTHOR 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>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HAVE_FOPENCOOKIE
+struct funopen_cookie {
+ void *orig_cookie;
+
+ int (*readfn)(void *cookie, char *buf, int size);
+ int (*writefn)(void *cookie, const char *buf, int size);
+ off_t (*seekfn)(void *cookie, off_t offset, int whence);
+ int (*closefn)(void *cookie);
+};
+
+static ssize_t
+funopen_read(void *cookie, char *buf, size_t size)
+{
+ struct funopen_cookie *cookiewrap = cookie;
+
+ if (cookiewrap->readfn == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+
+ return cookiewrap->readfn(cookiewrap->orig_cookie, buf, size);
+}
+
+static ssize_t
+funopen_write(void *cookie, const char *buf, size_t size)
+{
+ struct funopen_cookie *cookiewrap = cookie;
+
+ if (cookiewrap->writefn == NULL)
+ return EOF;
+
+ return cookiewrap->writefn(cookiewrap->orig_cookie, buf, size);
+}
+
+static int
+funopen_seek(void *cookie, off64_t *offset, int whence)
+{
+ struct funopen_cookie *cookiewrap = cookie;
+ off_t soff = *offset;
+
+ if (cookiewrap->seekfn == NULL) {
+ errno = ESPIPE;
+ return -1;
+ }
+
+ soff = cookiewrap->seekfn(cookiewrap->orig_cookie, soff, whence);
+ *offset = soff;
+
+ return *offset;
+}
+
+static int
+funopen_close(void *cookie)
+{
+ struct funopen_cookie *cookiewrap = cookie;
+ int rc;
+
+ if (cookiewrap->closefn == NULL)
+ return 0;
+
+ rc = cookiewrap->closefn(cookiewrap->orig_cookie);
+
+ free(cookiewrap);
+
+ return rc;
+}
+
+FILE *
+funopen(const void *cookie,
+ int (*readfn)(void *cookie, char *buf, int size),
+ int (*writefn)(void *cookie, const char *buf, int size),
+ off_t (*seekfn)(void *cookie, off_t offset, int whence),
+ int (*closefn)(void *cookie))
+{
+ struct funopen_cookie *cookiewrap;
+ cookie_io_functions_t funcswrap = {
+ .read = funopen_read,
+ .write = funopen_write,
+ .seek = funopen_seek,
+ .close = funopen_close,
+ };
+ const char *mode;
+
+ if (readfn) {
+ if (writefn == NULL)
+ mode = "r";
+ else
+ mode = "r+";
+ } else if (writefn) {
+ mode = "w";
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ cookiewrap = malloc(sizeof(*cookiewrap));
+ if (cookiewrap == NULL)
+ return NULL;
+
+ cookiewrap->orig_cookie = (void *)cookie;
+ cookiewrap->readfn = readfn;
+ cookiewrap->writefn = writefn;
+ cookiewrap->seekfn = seekfn;
+ cookiewrap->closefn = closefn;
+
+ return fopencookie(cookiewrap, mode, funcswrap);
+}
+#else
+#error "Function funopen() needs to be ported."
+#endif
/* Exported to cope with the constructor+dlopen+threads mess. */
setproctitle_init;
} LIBBSD_0.5;
+
+LIBBSD_0.7 {
+ funopen;
+} LIBBSD_0.6;
endian
fgetln
+funopen
headers
humanize
overlay
endian \
humanize \
fgetln \
+ funopen \
proctitle \
$(nil)
humanize_LDFLAGS = $(top_builddir)/src/libbsd.la
fgetln_LDFLAGS = $(top_builddir)/src/libbsd.la
+funopen_LDFLAGS = $(top_builddir)/src/libbsd.la
proctitle_LDFLAGS = \
-Wl,-u,libbsd_init_func \
$(top_builddir)/src/libbsd-ctor.a \
--- /dev/null
+/*
+ * Copyright © 2013 Guillem Jover <guillem@hadrons.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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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
+ * THE AUTHOR 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 <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#define ARRAY_SIZE 100
+#define TEST_SIZE 50
+
+struct test_cookie {
+ char array[ARRAY_SIZE];
+ int index;
+};
+
+int
+test_readfn(void *cookie, char *buf, int size)
+{
+ struct test_cookie *tc = cookie;
+ int left_size = sizeof(tc->array) - tc->index;
+
+ if (left_size < 0)
+ size = 0;
+ else if (left_size < size)
+ size = left_size;
+
+ if (size > 0) {
+ memcpy(buf, tc->array + tc->index, size);
+ tc->index += size;
+ }
+
+ return size;
+}
+
+int
+test_writefn(void *cookie, const char *buf, int size)
+{
+ struct test_cookie *tc = cookie;
+ int left_size = sizeof(tc->array) - tc->index;
+
+ if (left_size < 0)
+ size = 0;
+ else if (left_size < size)
+ size = left_size;
+
+ if (size > 0) {
+ memcpy(tc->array + tc->index, buf, size);
+ tc->index += size;
+ }
+
+ return size;
+}
+
+off_t
+test_seekfn(void *cookie, off_t offset, int whence)
+{
+ struct test_cookie *tc = cookie;
+
+ switch (whence) {
+ case SEEK_SET:
+ tc->index = offset;
+ break;
+ case SEEK_CUR:
+ tc->index += offset;
+ break;
+ case SEEK_END:
+ tc->index = sizeof(tc->array) + offset;
+ break;
+ }
+
+ return tc->index;
+}
+
+int
+test_closefn(void *cookie)
+{
+ struct test_cookie *tc = cookie;
+
+ memset(tc->array, 0x7f, sizeof(tc->array));
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct test_cookie tc;
+ char data[ARRAY_SIZE];
+ FILE *fp;
+ size_t i;
+
+ /* Test invalid hooks. */
+ fp = funopen(&tc, NULL, NULL, NULL, NULL);
+ assert(fp == NULL);
+ assert(errno == EINVAL);
+
+ /* Test read-only file. */
+ tc.index = 0;
+ for (i = 0; i < sizeof(tc.array); i++)
+ tc.array[i] = i;
+
+ fp = fropen(&tc, test_readfn);
+ assert(fp);
+
+ assert(fread(data, 1, TEST_SIZE, fp) == TEST_SIZE);
+ assert(memcmp(tc.array, data, TEST_SIZE) == 0);
+
+ assert(fwrite(data, 1, TEST_SIZE, fp) == 0);
+
+ assert(fclose(fp) == 0);
+
+ /* Test write-only file. */
+ memset(&tc, 0, sizeof(tc));
+
+ fp = fwopen(&tc, test_writefn);
+ assert(fp);
+
+ setvbuf(fp, NULL, _IONBF, 0);
+
+ assert(fwrite(data, 1, TEST_SIZE, fp) == TEST_SIZE);
+ assert(memcmp(tc.array, data, TEST_SIZE) == 0);
+
+ assert(fread(data, 1, TEST_SIZE, fp) == 0);
+
+ assert(fclose(fp) == 0);
+
+ /* Test seekable file. */
+ memset(&tc, 0, sizeof(tc));
+
+ fp = funopen(&tc, test_readfn, test_writefn, test_seekfn, NULL);
+ assert(fp);
+
+ setvbuf(fp, NULL, _IONBF, 0);
+
+ assert(fwrite(data, 1, TEST_SIZE, fp) == TEST_SIZE);
+ assert(fseek(fp, 0L, SEEK_SET) == 0);
+ assert(fwrite(data, 1, ARRAY_SIZE, fp) == ARRAY_SIZE);
+ assert(memcmp(tc.array, data, ARRAY_SIZE) == 0);
+
+ assert(fread(data, 1, TEST_SIZE, fp) == 0);
+
+ assert(fclose(fp) == 0);
+
+ /* Test close hook. */
+ memset(&tc, 0, sizeof(tc));
+
+ fp = funopen(&tc, test_readfn, test_writefn, NULL, test_closefn);
+ assert(fclose(fp) == 0);
+
+ for (i = 0; i < sizeof(tc.array); i++)
+ assert(tc.array[i] == 0x7f);
+
+ return 0;
+}