1 /* fopencookie.c -- implement GNU-style fopencookie() with BSD-style funopen()
2 *
3 * Todo: handle "a+" mode (probably by keeping read and write file pointers and
4 * calling seek() before calling read or write)
5 *
6 * Todo: Is this solution, calling readfn() which in turn calls
7 * sc->read() with some typecasts, better than ignoring the compiler
8 * warnings and calling sc->read directly?
9 *
10 * Created: 22 August 2011
11 * Author: Bert Bos <bert@w3.org>
12 *
13 * Copyright © 2011 World Wide Web Consortium
14 * See http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
15 */
16
17 #include "config.h"
18 #if HAVE_LIBCURL /* We only need this if we're using libcurl */
19 #if !HAVE_FOPENCOOKIE /* We don't need this on GNU Linux */
20
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include "errexit.e"
26 #include "fopencookie.h"
27
28 typedef struct {
29 cookie_read_function_t *read;
30 cookie_write_function_t *write;
31 cookie_seek_function_t *seek;
32 cookie_close_function_t *close;
33 void *cookie;
34 } cookiewrapper;
35
36
37 /* readfn -- callback that in turn calls sc->read with proper typecasts */
readfn(void * sc,char * buf,int n)38 static int readfn(void *sc, char *buf, int n)
39 {
40 cookiewrapper *c = (cookiewrapper*)sc;
41 return (int)(c->read(c->cookie, buf, (size_t)n));
42 }
43
44 /* writefn -- callback that in turn calls sc->write with proper typecasts */
writefn(void * sc,const char * buf,int n)45 static int writefn(void *sc, const char *buf, int n)
46 {
47 cookiewrapper *c = (cookiewrapper*)sc;
48 return (int)(c->write(c->cookie, buf, (size_t)n));
49 }
50
51 /* seekfn -- callback that in turn calls sc->seek with proper typecasts */
seekfn(void * sc,fpos_t offset,int whence)52 static fpos_t seekfn(void *sc, fpos_t offset, int whence)
53 {
54 cookiewrapper *c = (cookiewrapper*)sc;
55 return (fpos_t)(c->seek(c->cookie, (off64_t)offset, whence));
56 }
57
58 /* closefn -- callback that in turn calls sc->close and then frees memory */
closefn(void * sc)59 static int closefn(void *sc)
60 {
61 cookiewrapper *c = (cookiewrapper*)sc;
62 int r = c->close ? c->close(c->cookie) : 0;
63 free(sc);
64 return r;
65 }
66
67
68 /* fopencookie -- open a stream defined by four callback functions */
fopencookie(void * cookie,const char * mode,cookie_io_functions_t funcs)69 FILE *fopencookie(void *cookie, const char *mode,
70 cookie_io_functions_t funcs)
71 {
72 cookiewrapper *s;
73 int mask; /* 1 = read, 2 = write, 4 = append */
74 FILE *f;
75
76 /* Check that the parameters make sense */
77 if (!mode) {errno = EINVAL; return NULL;}
78
79 if (mode[0] == 'r') mask = 1;
80 else if (mode[0] == 'w') mask = 2;
81 else if (mode[0] == 'a') mask = 4;
82 else {errno = EINVAL; return NULL;}
83 if (mode[1] == '+' || (mode[1] =='b' && mode[2] == '+')) mask |= 3;
84
85 if ((mask & 1) && !funcs.read) {errno = EINVAL; return NULL;}
86 if ((mask & 2) && !funcs.write) {errno = EINVAL; return NULL;}
87 if ((mask & 4) && !funcs.seek) {errno = EINVAL; return NULL;}
88
89 if (mask == 7) errexit("Bug: fopencookie() can't yet handle mode \"a+\"\n");
90
91 /* Open the "file" */
92 if (!(s = malloc(sizeof(*s)))) {errno = ENOMEM; return NULL;}
93 s->read = funcs.read;
94 s->write = funcs.write;
95 s->seek = funcs.seek;
96 s->close = funcs.close;
97 s->cookie = cookie;
98
99 f = funopen(s,
100 s->read ? readfn : NULL,
101 s->write ? writefn : NULL,
102 s->seek ? seekfn : NULL,
103 closefn);
104 if (!f) {free(s); return NULL;}
105
106 /* If the mode is "a" (append), position the file pointer at the end */
107 if ((mask & 4) && fseek(f, 0L, SEEK_END) < 0) {fclose(f); return NULL;}
108
109 return f;
110 }
111
112 #endif /* !HAVE_FOPENCOOKIE */
113 #endif /* HAVE_LIBCURL */
114