From: William Pitcock <nenolod@dereferenced.org>
To: musl@lists.openwall.com
Cc: William Pitcock <nenolod@dereferenced.org>
Subject: [PATCH] stdio: implement fopencookie(3)
Date: Tue, 10 Oct 2017 18:03:56 +0000 [thread overview]
Message-ID: <20171010180356.11352-1-nenolod@dereferenced.org> (raw)
The fopencookie(3) function allows the programmer to create a custom
stdio implementation, using four hook functions which operate on a
"cookie" data type.
Changelog:
v3:
- remove spurious `struct winsize`
- make f->lock unconditionally 0
v2:
- properly implement stdio buffering
v1:
- initial proof of concept
---
include/stdio.h | 9 +++++
src/stdio/fopencookie.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 src/stdio/fopencookie.c
diff --git a/include/stdio.h b/include/stdio.h
index 884d2e6a..998883e5 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -182,6 +182,15 @@ int vasprintf(char **, const char *, __isoc_va_list);
#ifdef _GNU_SOURCE
char *fgets_unlocked(char *, int, FILE *);
int fputs_unlocked(const char *, FILE *);
+
+typedef struct {
+ ssize_t (*read)(void *cookie, char *buf, size_t size);
+ ssize_t (*write)(void *cookie, const char *buf, size_t size);
+ int (*seek)(void *cookie, off_t *offset, int whence);
+ int (*close)(void *cookie);
+} cookie_io_functions_t;
+
+FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t io_funcs);
#endif
#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
diff --git a/src/stdio/fopencookie.c b/src/stdio/fopencookie.c
new file mode 100644
index 00000000..baad2585
--- /dev/null
+++ b/src/stdio/fopencookie.c
@@ -0,0 +1,96 @@
+#define _GNU_SOURCE
+#include "stdio_impl.h"
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+struct fcookie {
+ void *cookie;
+ cookie_io_functions_t iofuncs;
+};
+
+static size_t cookieread(FILE *f, unsigned char *buf, size_t len)
+{
+ struct fcookie *fc = f->cookie;
+ size_t ret;
+ if (fc->iofuncs.read == NULL) return -1;
+ ret = fc->iofuncs.read(fc->cookie, (char *) buf, len);
+ if (ret == 0) f->flags |= F_EOF;
+ f->rpos = f->buf;
+ f->rend = f->buf + ret;
+ return ret;
+}
+
+static size_t cookiewrite(FILE *f, const unsigned char *buf, size_t len)
+{
+ struct fcookie *fc = f->cookie;
+ size_t ret;
+ size_t len2 = f->wpos - f->wbase;
+ if (fc->iofuncs.write == NULL) return -1;
+ if (len2) {
+ f->wpos = f->wbase;
+ if (cookiewrite(f, f->wpos, len2) < len2) return 0;
+ }
+ return fc->iofuncs.write(fc->cookie, (const char *) buf, len);
+}
+
+static off_t cookieseek(FILE *f, off_t off, int whence)
+{
+ struct fcookie *fc = f->cookie;
+ if (fc->iofuncs.seek) return fc->iofuncs.seek(fc->cookie, &off, whence);
+ return -1;
+}
+
+static int cookieclose(FILE *f)
+{
+ struct fcookie *fc = f->cookie;
+ if (fc->iofuncs.close) return fc->iofuncs.close(fc->cookie);
+ return 0;
+}
+
+FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t iofuncs)
+{
+ FILE *f;
+ struct fcookie *fc;
+
+ /* Check for valid initial mode character */
+ if (!strchr("rwa", *mode)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ /* Allocate FILE+fcookie+buffer or fail */
+ if (!(f=malloc(sizeof *f + sizeof *fc + UNGET + BUFSIZ))) return 0;
+
+ /* Zero-fill only the struct, not the buffer */
+ memset(f, 0, sizeof *f);
+
+ /* Impose mode restrictions */
+ if (!strchr(mode, '+')) f->flags = (*mode == 'r') ? F_NOWR : F_NORD;
+
+ /* Set up our fcookie */
+ fc = (void *)(f + 1);
+ fc->cookie = cookie;
+ fc->iofuncs.read = iofuncs.read;
+ fc->iofuncs.write = iofuncs.write;
+ fc->iofuncs.seek = iofuncs.seek;
+ fc->iofuncs.close = iofuncs.close;
+
+ f->fd = -1;
+ f->cookie = fc;
+ f->buf = (unsigned char *)f + sizeof *f + sizeof *fc + UNGET;
+ f->buf_size = BUFSIZ;
+ f->lbf = EOF;
+ f->lock = 0;
+
+ /* Initialize op ptrs. No problem if some are unneeded. */
+ f->read = cookieread;
+ f->write = cookiewrite;
+ f->seek = cookieseek;
+ f->close = cookieclose;
+
+ /* Add new FILE to open file list */
+ return __ofl_add(f);
+}
--
2.13.3
next reply other threads:[~2017-10-10 18:03 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-10 18:03 William Pitcock [this message]
2017-10-10 18:51 ` Jens Gustedt
2017-10-10 20:56 ` Rich Felker
2017-10-10 21:40 ` Jens Gustedt
2017-10-11 2:08 ` Rich Felker
2017-10-11 5:51 ` Jens Gustedt
2017-10-10 22:58 ` Morten Welinder
2017-10-11 2:09 ` Rich Felker
-- strict thread matches above, loose matches on Subject: below --
2017-10-10 23:27 William Pitcock
2017-10-05 6:48 William Pitcock
2017-10-05 10:10 ` Szabolcs Nagy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171010180356.11352-1-nenolod@dereferenced.org \
--to=nenolod@dereferenced.org \
--cc=musl@lists.openwall.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).