1fopencookie(3)             Library Functions Manual             fopencookie(3)
2
3
4

NAME

6       fopencookie - opening a custom stream
7

LIBRARY

9       Standard C library (libc, -lc)
10

SYNOPSIS

12       #define _GNU_SOURCE         /* See feature_test_macros(7) */
13       #define _FILE_OFFSET_BITS 64
14       #include <stdio.h>
15
16       FILE *fopencookie(void *restrict cookie, const char *restrict mode,
17                         cookie_io_functions_t io_funcs);
18

DESCRIPTION

20       The fopencookie() function allows the programmer to create a custom im‐
21       plementation for a standard I/O stream.  This implementation can  store
22       the  stream's  data  at  a  location  of its own choosing; for example,
23       fopencookie() is used to implement fmemopen(3), which provides a stream
24       interface to data that is stored in a buffer in memory.
25
26       In order to create a custom stream the programmer must:
27
28       •  Implement  four  "hook"  functions  that  are used internally by the
29          standard I/O library when performing I/O on the stream.
30
31       •  Define a "cookie" data type, a structure that  provides  bookkeeping
32          information  (e.g.,  where to store data) used by the aforementioned
33          hook functions.  The standard I/O package knows  nothing  about  the
34          contents  of  this cookie (thus it is typed as void * when passed to
35          fopencookie()), but automatically supplies the cookie as  the  first
36          argument when calling the hook functions.
37
38       •  Call fopencookie() to open a new stream and associate the cookie and
39          hook functions with that stream.
40
41       The fopencookie() function serves a purpose  similar  to  fopen(3):  it
42       opens  a new stream and returns a pointer to a FILE object that is used
43       to operate on that stream.
44
45       The cookie argument is a pointer to the caller's cookie structure  that
46       is  to  be associated with the new stream.  This pointer is supplied as
47       the first argument when the standard I/O library  invokes  any  of  the
48       hook functions described below.
49
50       The mode argument serves the same purpose as for fopen(3).  The follow‐
51       ing modes are supported: r, w, a, r+, w+, and a+.  See fopen(3) for de‐
52       tails.
53
54       The io_funcs argument is a structure that contains four fields pointing
55       to the programmer-defined hook functions that  are  used  to  implement
56       this stream.  The structure is defined as follows
57
58           typedef struct {
59               cookie_read_function_t  *read;
60               cookie_write_function_t *write;
61               cookie_seek_function_t  *seek;
62               cookie_close_function_t *close;
63           } cookie_io_functions_t;
64
65       The four fields are as follows:
66
67       cookie_read_function_t *read
68              This  function  implements read operations for the stream.  When
69              called, it receives three arguments:
70
71                  ssize_t read(void *cookie, char *buf, size_t size);
72
73              The buf and size arguments  are,  respectively,  a  buffer  into
74              which  input data can be placed and the size of that buffer.  As
75              its function result, the read function should return the  number
76              of bytes copied into buf, 0 on end of file, or -1 on error.  The
77              read function should update the stream offset appropriately.
78
79              If *read is a null pointer, then reads from  the  custom  stream
80              always return end of file.
81
82       cookie_write_function_t *write
83              This  function implements write operations for the stream.  When
84              called, it receives three arguments:
85
86                  ssize_t write(void *cookie, const char *buf, size_t size);
87
88              The buf and size arguments are, respectively, a buffer  of  data
89              to  be output to the stream and the size of that buffer.  As its
90              function result, the write function should return the number  of
91              bytes  copied  from  buf, or 0 on error.  (The function must not
92              return a negative value.)  The write function should update  the
93              stream offset appropriately.
94
95              If  *write  is a null pointer, then output to the stream is dis‐
96              carded.
97
98       cookie_seek_function_t *seek
99              This function implements seek operations on  the  stream.   When
100              called, it receives three arguments:
101
102                  int seek(void *cookie, off_t *offset, int whence);
103
104              The  *offset argument specifies the new file offset depending on
105              which of the following three values is supplied in whence:
106
107              SEEK_SET
108                     The stream offset should be set *offset  bytes  from  the
109                     start of the stream.
110
111              SEEK_CUR
112                     *offset should be added to the current stream offset.
113
114              SEEK_END
115                     The stream offset should be set to the size of the stream
116                     plus *offset.
117
118              Before returning, the seek function should update *offset to in‐
119              dicate the new stream offset.
120
121              As  its  function  result,  the seek function should return 0 on
122              success, and -1 on error.
123
124              If *seek is a null pointer, then it is not possible  to  perform
125              seek operations on the stream.
126
127       cookie_close_function_t *close
128              This  function  closes  the  stream.   The  hook function can do
129              things such as freeing buffers allocated for the  stream.   When
130              called, it receives one argument:
131
132                  int close(void *cookie);
133
134              The  cookie  argument is the cookie that the programmer supplied
135              when calling fopencookie().
136
137              As its function result, the close function should  return  0  on
138              success, and EOF on error.
139
140              If  *close is NULL, then no special action is performed when the
141              stream is closed.
142

RETURN VALUE

144       On success fopencookie() returns a pointer to the new stream.   On  er‐
145       ror, NULL is returned.
146

ATTRIBUTES

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

STANDARDS

158       GNU.
159

EXAMPLES

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

NOTES

344       _FILE_OFFSET_BITS should be defined to be 64 in code that uses non-null
345       seek or that takes the address of fopencookie, if the code is  intended
346       to  be  portable  to  traditional  32-bit  x86  and ARM platforms where
347       off_t's width defaults to 32 bits.
348

SEE ALSO

350       fclose(3), fmemopen(3), fopen(3), fseek(3)
351
352
353
354Linux man-pages 6.05              2023-07-20                    fopencookie(3)
Impressum