1FOPENCOOKIE(3)             Linux Programmer's Manual            FOPENCOOKIE(3)
2
3
4

NAME

6       fopencookie - opening a custom stream
7

SYNOPSIS

9       #define _GNU_SOURCE         /* See feature_test_macros(7) */
10       #include <stdio.h>
11
12       FILE *fopencookie(void *restrict cookie, const char *restrict mode,
13                         cookie_io_functions_t io_funcs);
14

DESCRIPTION

16       The fopencookie() function allows the programmer to create a custom im‐
17       plementation for a standard I/O stream.  This implementation can  store
18       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 de‐
48       tails.
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
104                     The stream offset should be set *offset  bytes  from  the
105                     start of the stream.
106
107              SEEK_CUR
108                     *offset should be added to the current stream offset.
109
110              SEEK_END
111                     The stream offset should be set to the size of the stream
112                     plus *offset.
113
114              Before returning, the seek function should update *offset to in‐
115              dicate the new stream offset.
116
117              As  its  function  result,  the seek function should return 0 on
118              success, and -1 on error.
119
120              If *seek is a null pointer, then it is not possible  to  perform
121              seek operations on the stream.
122
123       cookie_close_function_t *close
124              This  function  closes  the  stream.   The  hook function can do
125              things such as freeing buffers allocated for the  stream.   When
126              called, it receives one argument:
127
128                  int close(void *cookie);
129
130              The  cookie  argument is the cookie that the programmer supplied
131              when calling fopencookie().
132
133              As its function result, the close function should  return  0  on
134              success, and EOF on error.
135
136              If  *close is NULL, then no special action is performed when the
137              stream is closed.
138

RETURN VALUE

140       On success fopencookie() returns a pointer to the new stream.   On  er‐
141       ror, NULL is returned.
142

ATTRIBUTES

144       For  an  explanation  of  the  terms  used  in  this  section,  see at‐
145       tributes(7).
146
147       ┌────────────────────────────────────────────┬───────────────┬─────────┐
148Interface                                   Attribute     Value   
149       ├────────────────────────────────────────────┼───────────────┼─────────┤
150fopencookie()                               │ Thread safety │ MT-Safe │
151       └────────────────────────────────────────────┴───────────────┴─────────┘
152

CONFORMING TO

154       This function is a nonstandard GNU extension.
155

EXAMPLES

157       The program below implements a custom  stream  whose  functionality  is
158       similar  (but not identical) to that available via fmemopen(3).  It im‐
159       plements a stream whose data is stored in a memory buffer.  The program
160       writes its command-line arguments to the stream, and then seeks through
161       the stream reading two out of every five characters and writing them to
162       standard  output.   The following shell session demonstrates the use of
163       the program:
164
165           $ ./a.out 'hello world'
166           /he/
167           / w/
168           /d/
169           Reached end of file
170
171       Note that a more general version of the program below could be improved
172       to  more  robustly  handle  various  error  situations (e.g., opening a
173       stream with a cookie that already has an open stream; closing a  stream
174       that has already been closed).
175
176   Program source
177
178       #define _GNU_SOURCE
179       #include <sys/types.h>
180       #include <stdio.h>
181       #include <stdlib.h>
182       #include <unistd.h>
183       #include <string.h>
184
185       #define INIT_BUF_SIZE 4
186
187       struct memfile_cookie {
188           char   *buf;        /* Dynamically sized buffer for data */
189           size_t  allocated;  /* Size of buf */
190           size_t  endpos;     /* Number of characters in buf */
191           off_t   offset;     /* Current file offset in buf */
192       };
193
194       ssize_t
195       memfile_write(void *c, const char *buf, size_t size)
196       {
197           char *new_buff;
198           struct memfile_cookie *cookie = c;
199
200           /* Buffer too small? Keep doubling size until big enough. */
201
202           while (size + cookie->offset > cookie->allocated) {
203               new_buff = realloc(cookie->buf, cookie->allocated * 2);
204               if (new_buff == NULL) {
205                   return -1;
206               } else {
207                   cookie->allocated *= 2;
208                   cookie->buf = new_buff;
209               }
210           }
211
212           memcpy(cookie->buf + cookie->offset, buf, size);
213
214           cookie->offset += size;
215           if (cookie->offset > cookie->endpos)
216               cookie->endpos = cookie->offset;
217
218           return size;
219       }
220
221       ssize_t
222       memfile_read(void *c, char *buf, size_t size)
223       {
224           ssize_t xbytes;
225           struct memfile_cookie *cookie = c;
226
227           /* Fetch minimum of bytes requested and bytes available. */
228
229           xbytes = size;
230           if (cookie->offset + size > cookie->endpos)
231               xbytes = cookie->endpos - cookie->offset;
232           if (xbytes < 0)     /* offset may be past endpos */
233              xbytes = 0;
234
235           memcpy(buf, cookie->buf + cookie->offset, xbytes);
236
237           cookie->offset += xbytes;
238           return xbytes;
239       }
240
241       int
242       memfile_seek(void *c, off64_t *offset, int whence)
243       {
244           off64_t new_offset;
245           struct memfile_cookie *cookie = c;
246
247           if (whence == SEEK_SET)
248               new_offset = *offset;
249           else if (whence == SEEK_END)
250               new_offset = cookie->endpos + *offset;
251           else if (whence == SEEK_CUR)
252               new_offset = cookie->offset + *offset;
253           else
254               return -1;
255
256           if (new_offset < 0)
257               return -1;
258
259           cookie->offset = new_offset;
260           *offset = new_offset;
261           return 0;
262       }
263
264       int
265       memfile_close(void *c)
266       {
267           struct memfile_cookie *cookie = c;
268
269           free(cookie->buf);
270           cookie->allocated = 0;
271           cookie->buf = NULL;
272
273           return 0;
274       }
275
276       int
277       main(int argc, char *argv[])
278       {
279           cookie_io_functions_t  memfile_func = {
280               .read  = memfile_read,
281               .write = memfile_write,
282               .seek  = memfile_seek,
283               .close = memfile_close
284           };
285           FILE *stream;
286           struct memfile_cookie mycookie;
287           size_t nread;
288           char buf[1000];
289
290           /* Set up the cookie before calling fopencookie(). */
291
292           mycookie.buf = malloc(INIT_BUF_SIZE);
293           if (mycookie.buf == NULL) {
294               perror("malloc");
295               exit(EXIT_FAILURE);
296           }
297
298           mycookie.allocated = INIT_BUF_SIZE;
299           mycookie.offset = 0;
300           mycookie.endpos = 0;
301
302           stream = fopencookie(&mycookie, "w+", memfile_func);
303           if (stream == NULL) {
304               perror("fopencookie");
305               exit(EXIT_FAILURE);
306           }
307
308           /* Write command-line arguments to our file. */
309
310           for (int j = 1; j < argc; j++)
311               if (fputs(argv[j], stream) == EOF) {
312                   perror("fputs");
313                   exit(EXIT_FAILURE);
314               }
315
316           /* Read two bytes out of every five, until EOF. */
317
318           for (long p = 0; ; p += 5) {
319               if (fseek(stream, p, SEEK_SET) == -1) {
320                   perror("fseek");
321                   exit(EXIT_FAILURE);
322               }
323               nread = fread(buf, 1, 2, stream);
324               if (nread == 0) {
325                   if (ferror(stream) != 0) {
326                       fprintf(stderr, "fread failed\n");
327                       exit(EXIT_FAILURE);
328                   }
329                   printf("Reached end of file\n");
330                   break;
331               }
332
333               printf("/%.*s/\n", (int) nread, buf);
334           }
335
336           exit(EXIT_SUCCESS);
337       }
338

SEE ALSO

340       fclose(3), fmemopen(3), fopen(3), fseek(3)
341

COLOPHON

343       This  page  is  part of release 5.13 of the Linux man-pages project.  A
344       description of the project, information about reporting bugs,  and  the
345       latest     version     of     this    page,    can    be    found    at
346       https://www.kernel.org/doc/man-pages/.
347
348
349
350Linux                             2021-03-22                    FOPENCOOKIE(3)
Impressum