1fopencookie(3) Library Functions Manual fopencookie(3)
2
3
4
6 fopencookie - opening a custom stream
7
9 Standard C library (libc, -lc)
10
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
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
144 On success fopencookie() returns a pointer to the new stream. On er‐
145 ror, NULL is returned.
146
148 For an explanation of the terms used in this section, see at‐
149 tributes(7).
150
151 ┌────────────────────────────────────────────┬───────────────┬─────────┐
152 │Interface │ Attribute │ Value │
153 ├────────────────────────────────────────────┼───────────────┼─────────┤
154 │fopencookie() │ Thread safety │ MT-Safe │
155 └────────────────────────────────────────────┴───────────────┴─────────┘
156
158 GNU.
159
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
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
350 fclose(3), fmemopen(3), fopen(3), fseek(3)
351
352
353
354Linux man-pages 6.05 2023-07-20 fopencookie(3)