1 .\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk
2 .\" <mtk.manpages@gmail.com>
4 .\" %%%LICENSE_START(VERBATIM)
5 .\" Permission is granted to make and distribute verbatim copies of this
6 .\" manual provided the copyright notice and this permission notice are
7 .\" preserved on all copies.
9 .\" Permission is granted to copy and distribute modified versions of this
10 .\" manual under the conditions for verbatim copying, provided that the
11 .\" entire resulting derived work is distributed under the terms of a
12 .\" permission notice identical to this one.
14 .\" Since the Linux kernel and libraries are constantly changing, this
15 .\" manual page may be incorrect or out-of-date. The author(s) assume no
16 .\" responsibility for errors or omissions, or for damages resulting from
17 .\" the use of the information contained herein. The author(s) may not
18 .\" have taken the same level of care in the production of this manual,
19 .\" which is licensed free of charge, as they might when working
22 .\" Formatted or processed versions of this manual, if unaccompanied by
23 .\" the source, must acknowledge the copyright and authors of this work.
26 .TH FOPENCOOKIE 3 2021-03-22 "Linux" "Linux Programmer's Manual"
28 fopencookie \- opening a custom stream
31 .BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */"
34 .BI "FILE *fopencookie(void *restrict " cookie ", const char *restrict " mode ,
35 .BI " cookie_io_functions_t " io_funcs );
40 function allows the programmer to create a custom implementation
41 for a standard I/O stream.
42 This implementation can store the stream's data at a location of
43 its own choosing; for example,
47 which provides a stream interface to data that is stored in a
50 In order to create a custom stream the programmer must:
52 Implement four "hook" functions that are used internally by the
53 standard I/O library when performing I/O on the stream.
55 Define a "cookie" data type,
56 a structure that provides bookkeeping information
57 (e.g., where to store data) used by the aforementioned hook functions.
58 The standard I/O package knows nothing about the contents of this cookie
63 but automatically supplies the cookie
64 as the first argument when calling the hook functions.
68 to open a new stream and associate the cookie and hook functions
73 function serves a purpose similar to
75 it opens a new stream and returns a pointer to a
77 object that is used to operate on that stream.
81 argument is a pointer to the caller's cookie structure
82 that is to be associated with the new stream.
83 This pointer is supplied as the first argument when the standard I/O
84 library invokes any of the hook functions described below.
88 argument serves the same purpose as for
90 The following modes are supported:
104 argument is a structure that contains four fields pointing to the
105 programmer-defined hook functions that are used to implement this stream.
106 The structure is defined as follows
111 cookie_read_function_t *read;
112 cookie_write_function_t *write;
113 cookie_seek_function_t *seek;
114 cookie_close_function_t *close;
115 } cookie_io_functions_t;
119 The four fields are as follows:
121 .I cookie_read_function_t *read
122 This function implements read operations for the stream.
123 When called, it receives three arguments:
125 ssize_t read(void *cookie, char *buf, size_t size);
131 arguments are, respectively,
132 a buffer into which input data can be placed and the size of that buffer.
133 As its function result, the
135 function should return the number of bytes copied into
137 0 on end of file, or \-1 on error.
140 function should update the stream offset appropriately.
145 then reads from the custom stream always return end of file.
147 .I cookie_write_function_t *write
148 This function implements write operations for the stream.
149 When called, it receives three arguments:
151 ssize_t write(void *cookie, const char *buf, size_t size);
157 arguments are, respectively,
158 a buffer of data to be output to the stream and the size of that buffer.
159 As its function result, the
161 function should return the number of bytes copied from
164 (The function must not return a negative value.)
167 function should update the stream offset appropriately.
172 then output to the stream is discarded.
174 .I cookie_seek_function_t *seek
175 This function implements seek operations on the stream.
176 When called, it receives three arguments:
178 int seek(void *cookie, off64_t *offset, int whence);
182 argument specifies the new file offset depending on which
183 of the following three values is supplied in
188 The stream offset should be set
190 bytes from the start of the stream.
194 should be added to the current stream offset.
197 The stream offset should be set to the size of the stream plus
201 Before returning, the
203 function should update
205 to indicate the new stream offset.
207 As its function result, the
209 function should return 0 on success, and \-1 on error.
214 then it is not possible to perform seek operations on the stream.
216 .I cookie_close_function_t *close
217 This function closes the stream.
218 The hook function can do things such as freeing buffers allocated
220 When called, it receives one argument:
222 int close(void *cookie);
226 argument is the cookie that the programmer supplied when calling
229 As its function result, the
231 function should return 0 on success, and
237 is NULL, then no special action is performed when the stream is closed.
241 returns a pointer to the new stream.
242 On error, NULL is returned.
244 .\" It's not clear if errno ever gets set...
246 For an explanation of the terms used in this section, see
254 Interface Attribute Value
257 T} Thread safety MT-Safe
263 This function is a nonstandard GNU extension.
265 The program below implements a custom stream whose functionality
266 is similar (but not identical) to that available via
268 It implements a stream whose data is stored in a memory buffer.
269 The program writes its command-line arguments to the stream,
270 and then seeks through the stream reading two out of every
271 five characters and writing them to standard output.
272 The following shell session demonstrates the use of the program:
276 .RB "$" " ./a.out \(aqhello world\(aq"
284 Note that a more general version of the program below
285 could be improved to more robustly handle various error situations
286 (e.g., opening a stream with a cookie that already has an open stream;
287 closing a stream that has already been closed).
292 #include <sys/types.h>
298 #define INIT_BUF_SIZE 4
300 struct memfile_cookie {
301 char *buf; /* Dynamically sized buffer for data */
302 size_t allocated; /* Size of buf */
303 size_t endpos; /* Number of characters in buf */
304 off_t offset; /* Current file offset in buf */
308 memfile_write(void *c, const char *buf, size_t size)
311 struct memfile_cookie *cookie = c;
313 /* Buffer too small? Keep doubling size until big enough. */
315 while (size + cookie\->offset > cookie\->allocated) {
316 new_buff = realloc(cookie\->buf, cookie\->allocated * 2);
317 if (new_buff == NULL) {
320 cookie\->allocated *= 2;
321 cookie\->buf = new_buff;
325 memcpy(cookie\->buf + cookie\->offset, buf, size);
327 cookie\->offset += size;
328 if (cookie\->offset > cookie\->endpos)
329 cookie\->endpos = cookie\->offset;
335 memfile_read(void *c, char *buf, size_t size)
338 struct memfile_cookie *cookie = c;
340 /* Fetch minimum of bytes requested and bytes available. */
343 if (cookie\->offset + size > cookie\->endpos)
344 xbytes = cookie\->endpos \- cookie\->offset;
345 if (xbytes < 0) /* offset may be past endpos */
348 memcpy(buf, cookie\->buf + cookie\->offset, xbytes);
350 cookie\->offset += xbytes;
355 memfile_seek(void *c, off64_t *offset, int whence)
358 struct memfile_cookie *cookie = c;
360 if (whence == SEEK_SET)
361 new_offset = *offset;
362 else if (whence == SEEK_END)
363 new_offset = cookie\->endpos + *offset;
364 else if (whence == SEEK_CUR)
365 new_offset = cookie\->offset + *offset;
372 cookie\->offset = new_offset;
373 *offset = new_offset;
378 memfile_close(void *c)
380 struct memfile_cookie *cookie = c;
383 cookie\->allocated = 0;
390 main(int argc, char *argv[])
392 cookie_io_functions_t memfile_func = {
393 .read = memfile_read,
394 .write = memfile_write,
395 .seek = memfile_seek,
396 .close = memfile_close
399 struct memfile_cookie mycookie;
403 /* Set up the cookie before calling fopencookie(). */
405 mycookie.buf = malloc(INIT_BUF_SIZE);
406 if (mycookie.buf == NULL) {
411 mycookie.allocated = INIT_BUF_SIZE;
415 stream = fopencookie(&mycookie, "w+", memfile_func);
416 if (stream == NULL) {
417 perror("fopencookie");
421 /* Write command\-line arguments to our file. */
423 for (int j = 1; j < argc; j++)
424 if (fputs(argv[j], stream) == EOF) {
429 /* Read two bytes out of every five, until EOF. */
431 for (long p = 0; ; p += 5) {
432 if (fseek(stream, p, SEEK_SET) == \-1) {
436 nread = fread(buf, 1, 2, stream);
438 if (ferror(stream) != 0) {
439 fprintf(stderr, "fread failed\en");
442 printf("Reached end of file\en");
446 printf("/%.*s/\en", (int) nread, buf);