2 .\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk
3 .\" <mtk.manpages@gmail.com>
5 .\" SPDX-License-Identifier: Linux-man-pages-copyleft
7 .TH fopencookie 3 (date) "Linux man-pages (unreleased)"
9 fopencookie \- opening a custom stream
12 .RI ( libc ", " \-lc )
15 .BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */"
16 .B #define _FILE_OFFSET_BITS 64
19 .BI "FILE *fopencookie(void *restrict " cookie ", const char *restrict " mode ,
20 .BI " cookie_io_functions_t " io_funcs );
25 function allows the programmer to create a custom implementation
26 for a standard I/O stream.
27 This implementation can store the stream's data at a location of
28 its own choosing; for example,
32 which provides a stream interface to data that is stored in a
35 In order to create a custom stream the programmer must:
37 Implement four "hook" functions that are used internally by the
38 standard I/O library when performing I/O on the stream.
40 Define a "cookie" data type,
41 a structure that provides bookkeeping information
42 (e.g., where to store data) used by the aforementioned hook functions.
43 The standard I/O package knows nothing about the contents of this cookie
48 but automatically supplies the cookie
49 as the first argument when calling the hook functions.
53 to open a new stream and associate the cookie and hook functions
58 function serves a purpose similar to
60 it opens a new stream and returns a pointer to a
62 object that is used to operate on that stream.
66 argument is a pointer to the caller's cookie structure
67 that is to be associated with the new stream.
68 This pointer is supplied as the first argument when the standard I/O
69 library invokes any of the hook functions described below.
73 argument serves the same purpose as for
75 The following modes are supported:
89 argument is a structure that contains four fields pointing to the
90 programmer-defined hook functions that are used to implement this stream.
91 The structure is defined as follows
96 cookie_read_function_t *read;
97 cookie_write_function_t *write;
98 cookie_seek_function_t *seek;
99 cookie_close_function_t *close;
100 } cookie_io_functions_t;
104 The four fields are as follows:
106 .I cookie_read_function_t *read
107 This function implements read operations for the stream.
108 When called, it receives three arguments:
112 ssize_t read(void *cookie, char *buf, size_t size);
120 arguments are, respectively,
121 a buffer into which input data can be placed and the size of that buffer.
122 As its function result, the
124 function should return the number of bytes copied into
126 0 on end of file, or \-1 on error.
129 function should update the stream offset appropriately.
134 then reads from the custom stream always return end of file.
136 .I cookie_write_function_t *write
137 This function implements write operations for the stream.
138 When called, it receives three arguments:
142 ssize_t write(void *cookie, const char *buf, size_t size);
150 arguments are, respectively,
151 a buffer of data to be output to the stream and the size of that buffer.
152 As its function result, the
154 function should return the number of bytes copied from
157 (The function must not return a negative value.)
160 function should update the stream offset appropriately.
165 then output to the stream is discarded.
167 .I cookie_seek_function_t *seek
168 This function implements seek operations on the stream.
169 When called, it receives three arguments:
173 int seek(void *cookie, off_t *offset, int whence);
179 argument specifies the new file offset depending on which
180 of the following three values is supplied in
185 The stream offset should be set
187 bytes from the start of the stream.
191 should be added to the current stream offset.
194 The stream offset should be set to the size of the stream plus
198 Before returning, the
200 function should update
202 to indicate the new stream offset.
204 As its function result, the
206 function should return 0 on success, and \-1 on error.
211 then it is not possible to perform seek operations on the stream.
213 .I cookie_close_function_t *close
214 This function closes the stream.
215 The hook function can do things such as freeing buffers allocated
217 When called, it receives one argument:
221 int close(void *cookie);
227 argument is the cookie that the programmer supplied when calling
230 As its function result, the
232 function should return 0 on success, and
238 is NULL, then no special action is performed when the stream is closed.
242 returns a pointer to the new stream.
243 On error, NULL is returned.
245 .\" It's not clear if errno ever gets set...
247 For an explanation of the terms used in this section, see
253 Interface Attribute Value
258 T} Thread safety MT-Safe
263 The program below implements a custom stream whose functionality
264 is similar (but not identical) to that available via
266 It implements a stream whose data is stored in a memory buffer.
267 The program writes its command-line arguments to the stream,
268 and then seeks through the stream reading two out of every
269 five characters and writing them to standard output.
270 The following shell session demonstrates the use of the program:
274 .RB "$" " ./a.out \[aq]hello world\[aq]"
282 Note that a more general version of the program below
283 could be improved to more robustly handle various error situations
284 (e.g., opening a stream with a cookie that already has an open stream;
285 closing a stream that has already been closed).
288 .\" SRC BEGIN (fopencookie.c)
294 #include <sys/types.h>
297 #define INIT_BUF_SIZE 4
299 struct memfile_cookie {
300 char *buf; /* Dynamically sized buffer for data */
301 size_t allocated; /* Size of buf */
302 size_t endpos; /* Number of characters in buf */
303 off_t offset; /* Current file offset in buf */
307 memfile_write(void *c, const char *buf, size_t size)
310 struct memfile_cookie *cookie = c;
312 /* Buffer too small? Keep doubling size until big enough. */
314 while (size + cookie\->offset > cookie\->allocated) {
315 new_buff = realloc(cookie\->buf, cookie\->allocated * 2);
316 if (new_buff == NULL)
318 cookie\->allocated *= 2;
319 cookie\->buf = new_buff;
322 memcpy(cookie\->buf + cookie\->offset, buf, size);
324 cookie\->offset += size;
325 if (cookie\->offset > cookie\->endpos)
326 cookie\->endpos = cookie\->offset;
332 memfile_read(void *c, char *buf, size_t size)
335 struct memfile_cookie *cookie = c;
337 /* Fetch minimum of bytes requested and bytes available. */
340 if (cookie\->offset + size > cookie\->endpos)
341 xbytes = cookie\->endpos \- cookie\->offset;
342 if (xbytes < 0) /* offset may be past endpos */
345 memcpy(buf, cookie\->buf + cookie\->offset, xbytes);
347 cookie\->offset += xbytes;
352 memfile_seek(void *c, off_t *offset, int whence)
355 struct memfile_cookie *cookie = c;
357 if (whence == SEEK_SET)
358 new_offset = *offset;
359 else if (whence == SEEK_END)
360 new_offset = cookie\->endpos + *offset;
361 else if (whence == SEEK_CUR)
362 new_offset = cookie\->offset + *offset;
369 cookie\->offset = new_offset;
370 *offset = new_offset;
375 memfile_close(void *c)
377 struct memfile_cookie *cookie = c;
380 cookie\->allocated = 0;
387 main(int argc, char *argv[])
389 cookie_io_functions_t memfile_func = {
390 .read = memfile_read,
391 .write = memfile_write,
392 .seek = memfile_seek,
393 .close = memfile_close
396 struct memfile_cookie mycookie;
400 /* Set up the cookie before calling fopencookie(). */
402 mycookie.buf = malloc(INIT_BUF_SIZE);
403 if (mycookie.buf == NULL) {
408 mycookie.allocated = INIT_BUF_SIZE;
412 stream = fopencookie(&mycookie, "w+", memfile_func);
413 if (stream == NULL) {
414 perror("fopencookie");
418 /* Write command\-line arguments to our file. */
420 for (size_t j = 1; j < argc; j++)
421 if (fputs(argv[j], stream) == EOF) {
426 /* Read two bytes out of every five, until EOF. */
428 for (long p = 0; ; p += 5) {
429 if (fseek(stream, p, SEEK_SET) == \-1) {
433 nread = fread(buf, 1, 2, stream);
435 if (ferror(stream) != 0) {
436 fprintf(stderr, "fread failed\en");
439 printf("Reached end of file\en");
443 printf("/%.*s/\en", (int) nread, buf);
454 should be defined to be 64 in code that uses non-null
456 or that takes the address of
458 if the code is intended to be portable
459 to traditional 32-bit x86 and ARM platforms where
461 width defaults to 32 bits.