1FOPENCOOKIE(3) Linux Programmer's Manual FOPENCOOKIE(3)
2
3
4
6 fopencookie - opening a custom stream
7
9 #define _GNU_SOURCE /* See feature_test_macros(7) */
10 #include <stdio.h>
11
12 FILE *fopencookie(void *cookie, const char *mode,
13 cookie_io_functions_t io_funcs);
14
16 The fopencookie() function allows the programmer to create a custom
17 implementation for a standard I/O stream. This implementation can
18 store the stream's data at a location of its own choosing; for example,
19 fopencookie() is used to implement fmemopen(3), which provides a stream
20 interface to data that is stored in a buffer in memory.
21
22 In order to create a custom stream the programmer must:
23
24 * Implement four "hook" functions that are used internally by the
25 standard I/O library when performing I/O on the stream.
26
27 * Define a "cookie" data type, a structure that provides bookkeeping
28 information (e.g., where to store data) used by the aforementioned
29 hook functions. The standard I/O package knows nothing about the
30 contents of this cookie (thus it is typed as void * when passed to
31 fopencookie()), but automatically supplies the cookie as the first
32 argument when calling the hook functions.
33
34 * Call fopencookie() to open a new stream and associate the cookie and
35 hook functions with that stream.
36
37 The fopencookie() function serves a purpose similar to fopen(3): it
38 opens a new stream and returns a pointer to a FILE object that is used
39 to operate on that stream.
40
41 The cookie argument is a pointer to the caller's cookie structure that
42 is to be associated with the new stream. This pointer is supplied as
43 the first argument when the standard I/O library invokes any of the
44 hook functions described below.
45
46 The mode argument serves the same purpose as for fopen(3). The follow‐
47 ing modes are supported: r, w, a, r+, w+, and a+. See fopen(3) for
48 details.
49
50 The io_funcs argument is a structure that contains four fields pointing
51 to the programmer-defined hook functions that are used to implement
52 this stream. The structure is defined as follows
53
54 typedef struct {
55 cookie_read_function_t *read;
56 cookie_write_function_t *write;
57 cookie_seek_function_t *seek;
58 cookie_close_function_t *close;
59 } cookie_io_functions_t;
60
61 The four fields are as follows:
62
63 cookie_read_function_t *read
64 This function implements read operations for the stream. When
65 called, it receives three arguments:
66
67 ssize_t read(void *cookie, char *buf, size_t size);
68
69 The buf and size arguments are, respectively, a buffer into
70 which input data can be placed and the size of that buffer. As
71 its function result, the read function should return the number
72 of bytes copied into buf, 0 on end of file, or -1 on error. The
73 read function should update the stream offset appropriately.
74
75 If *read is a null pointer, then reads from the custom stream
76 always return end of file.
77
78 cookie_write_function_t *write
79 This function implements write operations for the stream. When
80 called, it receives three arguments:
81
82 ssize_t write(void *cookie, const char *buf, size_t size);
83
84 The buf and size arguments are, respectively, a buffer of data
85 to be output to the stream and the size of that buffer. As its
86 function result, the write function should return the number of
87 bytes copied from buf, or 0 on error. (The function must not
88 return a negative value.) The write function should update the
89 stream offset appropriately.
90
91 If *write is a null pointer, then output to the stream is dis‐
92 carded.
93
94 cookie_seek_function_t *seek
95 This function implements seek operations on the stream. When
96 called, it receives three arguments:
97
98 int seek(void *cookie, off64_t *offset, int whence);
99
100 The *offset argument specifies the new file offset depending on
101 which of the following three values is supplied in whence:
102
103 SEEK_SET The stream offset should be set *offset bytes from the
104 start of the stream.
105
106 SEEK_CUR *offset should be added to the current stream offset.
107
108 SEEK_END The stream offset should be set to the size of the
109 stream plus *offset.
110
111 Before returning, the seek function should update *offset to
112 indicate the new stream offset.
113
114 As its function result, the seek function should return 0 on
115 success, and -1 on error.
116
117 If *seek is a null pointer, then it is not possible to perform
118 seek operations on the stream.
119
120 cookie_close_function_t *close
121 This function closes the stream. The hook function can do
122 things such as freeing buffers allocated for the stream. When
123 called, it receives one argument:
124
125 int close(void *cookie);
126
127 The cookie argument is the cookie that the programmer supplied
128 when calling fopencookie().
129
130 As its function result, the close function should return 0 on
131 success, and EOF on error.
132
133 If *close is NULL, then no special action is performed when the
134 stream is closed.
135
137 On success fopencookie() returns a pointer to the new stream. On
138 error, NULL is returned.
139
141 For an explanation of the terms used in this section, see
142 attributes(7).
143
144 ┌──────────────┬───────────────┬─────────┐
145 │Interface │ Attribute │ Value │
146 ├──────────────┼───────────────┼─────────┤
147 │fopencookie() │ Thread safety │ MT-Safe │
148 └──────────────┴───────────────┴─────────┘
150 This function is a nonstandard GNU extension.
151
153 The program below implements a custom stream whose functionality is
154 similar (but not identical) to that available via fmemopen(3). It
155 implements a stream whose data is stored in a memory buffer. The pro‐
156 gram writes its command-line arguments to the stream, and then seeks
157 through the stream reading two out of every five characters and writing
158 them to standard output. The following shell session demonstrates the
159 use of the program:
160
161 $ ./a.out 'hello world'
162 /he/
163 / w/
164 /d/
165 Reached end of file
166
167 Note that a more general version of the program below could be improved
168 to more robustly handle various error situations (e.g., opening a
169 stream with a cookie that already has an open stream; closing a stream
170 that has already been closed).
171
172 Program source
173
174 #define _GNU_SOURCE
175 #include <sys/types.h>
176 #include <stdio.h>
177 #include <stdlib.h>
178 #include <unistd.h>
179 #include <string.h>
180
181 #define INIT_BUF_SIZE 4
182
183 struct memfile_cookie {
184 char *buf; /* Dynamically sized buffer for data */
185 size_t allocated; /* Size of buf */
186 size_t endpos; /* Number of characters in buf */
187 off_t offset; /* Current file offset in buf */
188 };
189
190 ssize_t
191 memfile_write(void *c, const char *buf, size_t size)
192 {
193 char *new_buff;
194 struct memfile_cookie *cookie = c;
195
196 /* Buffer too small? Keep doubling size until big enough */
197
198 while (size + cookie->offset > cookie->allocated) {
199 new_buff = realloc(cookie->buf, cookie->allocated * 2);
200 if (new_buff == NULL) {
201 return -1;
202 } else {
203 cookie->allocated *= 2;
204 cookie->buf = new_buff;
205 }
206 }
207
208 memcpy(cookie->buf + cookie->offset, buf, size);
209
210 cookie->offset += size;
211 if (cookie->offset > cookie->endpos)
212 cookie->endpos = cookie->offset;
213
214 return size;
215 }
216
217 ssize_t
218 memfile_read(void *c, char *buf, size_t size)
219 {
220 ssize_t xbytes;
221 struct memfile_cookie *cookie = c;
222
223 /* Fetch minimum of bytes requested and bytes available */
224
225 xbytes = size;
226 if (cookie->offset + size > cookie->endpos)
227 xbytes = cookie->endpos - cookie->offset;
228 if (xbytes < 0) /* offset may be past endpos */
229 xbytes = 0;
230
231 memcpy(buf, cookie->buf + cookie->offset, xbytes);
232
233 cookie->offset += xbytes;
234 return xbytes;
235 }
236
237 int
238 memfile_seek(void *c, off64_t *offset, int whence)
239 {
240 off64_t new_offset;
241 struct memfile_cookie *cookie = c;
242
243 if (whence == SEEK_SET)
244 new_offset = *offset;
245 else if (whence == SEEK_END)
246 new_offset = cookie->endpos + *offset;
247 else if (whence == SEEK_CUR)
248 new_offset = cookie->offset + *offset;
249 else
250 return -1;
251
252 if (new_offset < 0)
253 return -1;
254
255 cookie->offset = new_offset;
256 *offset = new_offset;
257 return 0;
258 }
259
260 int
261 memfile_close(void *c)
262 {
263 struct memfile_cookie *cookie = c;
264
265 free(cookie->buf);
266 cookie->allocated = 0;
267 cookie->buf = NULL;
268
269 return 0;
270 }
271
272 int
273 main(int argc, char *argv[])
274 {
275 cookie_io_functions_t memfile_func = {
276 .read = memfile_read,
277 .write = memfile_write,
278 .seek = memfile_seek,
279 .close = memfile_close
280 };
281 FILE *stream;
282 struct memfile_cookie mycookie;
283 ssize_t nread;
284 long p;
285 int j;
286 char buf[1000];
287
288 /* Set up the cookie before calling fopencookie() */
289
290 mycookie.buf = malloc(INIT_BUF_SIZE);
291 if (mycookie.buf == NULL) {
292 perror("malloc");
293 exit(EXIT_FAILURE);
294 }
295
296 mycookie.allocated = INIT_BUF_SIZE;
297 mycookie.offset = 0;
298 mycookie.endpos = 0;
299
300 stream = fopencookie(&mycookie,"w+", memfile_func);
301 if (stream == NULL) {
302 perror("fopencookie");
303 exit(EXIT_FAILURE);
304 }
305
306 /* Write command-line arguments to our file */
307
308 for (j = 1; j < argc; j++)
309 if (fputs(argv[j], stream) == EOF) {
310 perror("fputs");
311 exit(EXIT_FAILURE);
312 }
313
314 /* Read two bytes out of every five, until EOF */
315
316 for (p = 0; ; p += 5) {
317 if (fseek(stream, p, SEEK_SET) == -1) {
318 perror("fseek");
319 exit(EXIT_FAILURE);
320 }
321 nread = fread(buf, 1, 2, stream);
322 if (nread == -1) {
323 perror("fread");
324 exit(EXIT_FAILURE);
325 }
326 if (nread == 0) {
327 printf("Reached end of file\n");
328 break;
329 }
330
331 printf("/%.*s/\n", nread, buf);
332 }
333
334 exit(EXIT_SUCCESS);
335 }
336
338 fclose(3), fmemopen(3), fopen(3), fseek(3)
339
341 This page is part of release 5.02 of the Linux man-pages project. A
342 description of the project, information about reporting bugs, and the
343 latest version of this page, can be found at
344 https://www.kernel.org/doc/man-pages/.
345
346
347
348Linux 2019-03-06 FOPENCOOKIE(3)