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 #include <stdio.h>
14
15 FILE *fopencookie(void *restrict cookie, const char *restrict mode,
16 cookie_io_functions_t io_funcs);
17
19 The fopencookie() function allows the programmer to create a custom im‐
20 plementation for a standard I/O stream. This implementation can store
21 the stream's data at a location of its own choosing; for example,
22 fopencookie() is used to implement fmemopen(3), which provides a stream
23 interface to data that is stored in a buffer in memory.
24
25 In order to create a custom stream the programmer must:
26
27 • Implement four "hook" functions that are used internally by the
28 standard I/O library when performing I/O on the stream.
29
30 • Define a "cookie" data type, a structure that provides bookkeeping
31 information (e.g., where to store data) used by the aforementioned
32 hook functions. The standard I/O package knows nothing about the
33 contents of this cookie (thus it is typed as void * when passed to
34 fopencookie()), but automatically supplies the cookie as the first
35 argument when calling the hook functions.
36
37 • Call fopencookie() to open a new stream and associate the cookie and
38 hook functions with that stream.
39
40 The fopencookie() function serves a purpose similar to fopen(3): it
41 opens a new stream and returns a pointer to a FILE object that is used
42 to operate on that stream.
43
44 The cookie argument is a pointer to the caller's cookie structure that
45 is to be associated with the new stream. This pointer is supplied as
46 the first argument when the standard I/O library invokes any of the
47 hook functions described below.
48
49 The mode argument serves the same purpose as for fopen(3). The follow‐
50 ing modes are supported: r, w, a, r+, w+, and a+. See fopen(3) for de‐
51 tails.
52
53 The io_funcs argument is a structure that contains four fields pointing
54 to the programmer-defined hook functions that are used to implement
55 this stream. The structure is defined as follows
56
57 typedef struct {
58 cookie_read_function_t *read;
59 cookie_write_function_t *write;
60 cookie_seek_function_t *seek;
61 cookie_close_function_t *close;
62 } cookie_io_functions_t;
63
64 The four fields are as follows:
65
66 cookie_read_function_t *read
67 This function implements read operations for the stream. When
68 called, it receives three arguments:
69
70 ssize_t read(void *cookie, char *buf, size_t size);
71
72 The buf and size arguments are, respectively, a buffer into
73 which input data can be placed and the size of that buffer. As
74 its function result, the read function should return the number
75 of bytes copied into buf, 0 on end of file, or -1 on error. The
76 read function should update the stream offset appropriately.
77
78 If *read is a null pointer, then reads from the custom stream
79 always return end of file.
80
81 cookie_write_function_t *write
82 This function implements write operations for the stream. When
83 called, it receives three arguments:
84
85 ssize_t write(void *cookie, const char *buf, size_t size);
86
87 The buf and size arguments are, respectively, a buffer of data
88 to be output to the stream and the size of that buffer. As its
89 function result, the write function should return the number of
90 bytes copied from buf, or 0 on error. (The function must not
91 return a negative value.) The write function should update the
92 stream offset appropriately.
93
94 If *write is a null pointer, then output to the stream is dis‐
95 carded.
96
97 cookie_seek_function_t *seek
98 This function implements seek operations on the stream. When
99 called, it receives three arguments:
100
101 int seek(void *cookie, off64_t *offset, int whence);
102
103 The *offset argument specifies the new file offset depending on
104 which of the following three values is supplied in whence:
105
106 SEEK_SET
107 The stream offset should be set *offset bytes from the
108 start of the stream.
109
110 SEEK_CUR
111 *offset should be added to the current stream offset.
112
113 SEEK_END
114 The stream offset should be set to the size of the stream
115 plus *offset.
116
117 Before returning, the seek function should update *offset to in‐
118 dicate the new stream offset.
119
120 As its function result, the seek function should return 0 on
121 success, and -1 on error.
122
123 If *seek is a null pointer, then it is not possible to perform
124 seek operations on the stream.
125
126 cookie_close_function_t *close
127 This function closes the stream. The hook function can do
128 things such as freeing buffers allocated for the stream. When
129 called, it receives one argument:
130
131 int close(void *cookie);
132
133 The cookie argument is the cookie that the programmer supplied
134 when calling fopencookie().
135
136 As its function result, the close function should return 0 on
137 success, and EOF on error.
138
139 If *close is NULL, then no special action is performed when the
140 stream is closed.
141
143 On success fopencookie() returns a pointer to the new stream. On er‐
144 ror, NULL is returned.
145
147 For an explanation of the terms used in this section, see at‐
148 tributes(7).
149
150 ┌────────────────────────────────────────────┬───────────────┬─────────┐
151 │Interface │ Attribute │ Value │
152 ├────────────────────────────────────────────┼───────────────┼─────────┤
153 │fopencookie() │ Thread safety │ MT-Safe │
154 └────────────────────────────────────────────┴───────────────┴─────────┘
155
157 GNU.
158
160 The program below implements a custom stream whose functionality is
161 similar (but not identical) to that available via fmemopen(3). It im‐
162 plements a stream whose data is stored in a memory buffer. The program
163 writes its command-line arguments to the stream, and then seeks through
164 the stream reading two out of every five characters and writing them to
165 standard output. The following shell session demonstrates the use of
166 the program:
167
168 $ ./a.out 'hello world'
169 /he/
170 / w/
171 /d/
172 Reached end of file
173
174 Note that a more general version of the program below could be improved
175 to more robustly handle various error situations (e.g., opening a
176 stream with a cookie that already has an open stream; closing a stream
177 that has already been closed).
178
179 Program source
180
181 #define _GNU_SOURCE
182 #include <stdio.h>
183 #include <stdlib.h>
184 #include <string.h>
185 #include <sys/types.h>
186 #include <unistd.h>
187
188 #define INIT_BUF_SIZE 4
189
190 struct memfile_cookie {
191 char *buf; /* Dynamically sized buffer for data */
192 size_t allocated; /* Size of buf */
193 size_t endpos; /* Number of characters in buf */
194 off_t offset; /* Current file offset in buf */
195 };
196
197 ssize_t
198 memfile_write(void *c, const char *buf, size_t size)
199 {
200 char *new_buff;
201 struct memfile_cookie *cookie = c;
202
203 /* Buffer too small? Keep doubling size until big enough. */
204
205 while (size + cookie->offset > cookie->allocated) {
206 new_buff = realloc(cookie->buf, cookie->allocated * 2);
207 if (new_buff == NULL)
208 return -1;
209 cookie->allocated *= 2;
210 cookie->buf = new_buff;
211 }
212
213 memcpy(cookie->buf + cookie->offset, buf, size);
214
215 cookie->offset += size;
216 if (cookie->offset > cookie->endpos)
217 cookie->endpos = cookie->offset;
218
219 return size;
220 }
221
222 ssize_t
223 memfile_read(void *c, char *buf, size_t size)
224 {
225 ssize_t xbytes;
226 struct memfile_cookie *cookie = c;
227
228 /* Fetch minimum of bytes requested and bytes available. */
229
230 xbytes = size;
231 if (cookie->offset + size > cookie->endpos)
232 xbytes = cookie->endpos - cookie->offset;
233 if (xbytes < 0) /* offset may be past endpos */
234 xbytes = 0;
235
236 memcpy(buf, cookie->buf + cookie->offset, xbytes);
237
238 cookie->offset += xbytes;
239 return xbytes;
240 }
241
242 int
243 memfile_seek(void *c, off64_t *offset, int whence)
244 {
245 off64_t new_offset;
246 struct memfile_cookie *cookie = c;
247
248 if (whence == SEEK_SET)
249 new_offset = *offset;
250 else if (whence == SEEK_END)
251 new_offset = cookie->endpos + *offset;
252 else if (whence == SEEK_CUR)
253 new_offset = cookie->offset + *offset;
254 else
255 return -1;
256
257 if (new_offset < 0)
258 return -1;
259
260 cookie->offset = new_offset;
261 *offset = new_offset;
262 return 0;
263 }
264
265 int
266 memfile_close(void *c)
267 {
268 struct memfile_cookie *cookie = c;
269
270 free(cookie->buf);
271 cookie->allocated = 0;
272 cookie->buf = NULL;
273
274 return 0;
275 }
276
277 int
278 main(int argc, char *argv[])
279 {
280 cookie_io_functions_t memfile_func = {
281 .read = memfile_read,
282 .write = memfile_write,
283 .seek = memfile_seek,
284 .close = memfile_close
285 };
286 FILE *stream;
287 struct memfile_cookie mycookie;
288 size_t nread;
289 char buf[1000];
290
291 /* Set up the cookie before calling fopencookie(). */
292
293 mycookie.buf = malloc(INIT_BUF_SIZE);
294 if (mycookie.buf == NULL) {
295 perror("malloc");
296 exit(EXIT_FAILURE);
297 }
298
299 mycookie.allocated = INIT_BUF_SIZE;
300 mycookie.offset = 0;
301 mycookie.endpos = 0;
302
303 stream = fopencookie(&mycookie, "w+", memfile_func);
304 if (stream == NULL) {
305 perror("fopencookie");
306 exit(EXIT_FAILURE);
307 }
308
309 /* Write command-line arguments to our file. */
310
311 for (size_t j = 1; j < argc; j++)
312 if (fputs(argv[j], stream) == EOF) {
313 perror("fputs");
314 exit(EXIT_FAILURE);
315 }
316
317 /* Read two bytes out of every five, until EOF. */
318
319 for (long p = 0; ; p += 5) {
320 if (fseek(stream, p, SEEK_SET) == -1) {
321 perror("fseek");
322 exit(EXIT_FAILURE);
323 }
324 nread = fread(buf, 1, 2, stream);
325 if (nread == 0) {
326 if (ferror(stream) != 0) {
327 fprintf(stderr, "fread failed\n");
328 exit(EXIT_FAILURE);
329 }
330 printf("Reached end of file\n");
331 break;
332 }
333
334 printf("/%.*s/\n", (int) nread, buf);
335 }
336
337 free(mycookie.buf);
338
339 exit(EXIT_SUCCESS);
340 }
341
343 fclose(3), fmemopen(3), fopen(3), fseek(3)
344
345
346
347Linux man-pages 6.04 2023-03-30 fopencookie(3)