1FOPENCOOKIE(3) Linux Programmer's Manual FOPENCOOKIE(3)
2
3
4
6 fopencookie - opening a custom stream
7
9 #define _GNU_SOURCE /* See feature_test_macros(7) */
10 #include <stdio.h>
11
12 FILE *fopencookie(void *cookie, const char *mode,
13 cookie_io_functions_t io_funcs);
14
16 The fopencookie() function allows the programmer to create a custom
17 implementation for a standard I/O stream. This implementation can
18 store 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
48 details.
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 The stream offset should be set *offset bytes from the
104 start of the stream.
105
106 SEEK_CUR *offset should be added to the current stream offset.
107
108 SEEK_END The stream offset should be set to the size of the
109 stream plus *offset.
110
111 Before returning, the seek function should update *offset to
112 indicate the new stream offset.
113
114 As its function result, the seek function should return 0 on
115 success, and -1 on error.
116
117 If *seek is a NULL pointer, then it is not possible to perform
118 seek operations on the stream.
119
120 cookie_close_function_t *close
121 This function closes the stream. The hook function can do
122 things such as freeing buffers allocated for the stream. When
123 called, it receives one argument:
124
125 int close(void *cookie);
126
127 The cookie argument is the cookie that the programmer supplied
128 when calling fopencookie().
129
130 As its function result, the close function should return 0 on
131 success, and EOF on error.
132
133 If *close is NULL, then no special action is performed when the
134 stream is closed.
135
137 On success fopencookie() returns a pointer to the new stream. On
138 error, NULL is returned.
139
141 This function is a nonstandard GNU extension.
142
144 The program below implements a custom stream whose functionality is
145 similar (but not identical) to that available via fmemopen(3). It
146 implements a stream whose data is stored in a memory buffer. The pro‐
147 gram writes its command-line arguments to the stream, and then seeks
148 through the stream reading two out of every five characters and writing
149 them to standard output. The following shell session demonstrates the
150 use of the program:
151
152 $ ./a.out 'hello world'
153 /he/
154 / w/
155 /d/
156 Reached end of file
157
158 Note that a more general version of the program below could be improved
159 to more robustly handle various error situations (e.g., opening a
160 stream with a cookie that already has an open stream; closing a stream
161 that has already been closed).
162
163 Program source
164
165 #define _GNU_SOURCE
166 #include <sys/types.h>
167 #include <stdio.h>
168 #include <stdlib.h>
169 #include <unistd.h>
170 #include <string.h>
171
172 #define INIT_BUF_SIZE 4
173
174 struct memfile_cookie {
175 char *buf; /* Dynamically sized buffer for data */
176 size_t allocated; /* Size of buf */
177 size_t endpos; /* Number of characters in buf */
178 off_t offset; /* Current file offset in buf */
179 };
180
181 ssize_t
182 memfile_write(void *c, const char *buf, size_t size)
183 {
184 char *new_buff;
185 struct memfile_cookie *cookie = c;
186
187 /* Buffer too small? Keep doubling size until big enough */
188
189 while (size + cookie->offset > cookie->allocated) {
190 new_buff = realloc(cookie->buf, cookie->allocated * 2);
191 if (new_buff == NULL) {
192 return -1;
193 } else {
194 cookie->allocated *= 2;
195 cookie->buf = new_buff;
196 }
197 }
198
199 memcpy(cookie->buf + cookie->offset, buf, size);
200
201 cookie->offset += size;
202 if (cookie->offset > cookie->endpos)
203 cookie->endpos = cookie->offset;
204
205 return size;
206 }
207
208 ssize_t
209 memfile_read(void *c, char *buf, size_t size)
210 {
211 ssize_t xbytes;
212 struct memfile_cookie *cookie = c;
213
214 /* Fetch minimum of bytes requested and bytes available */
215
216 xbytes = size;
217 if (cookie->offset + size > cookie->endpos)
218 xbytes = cookie->endpos - cookie->offset;
219 if (xbytes < 0) /* offset may be past endpos */
220 xbytes = 0;
221
222 memcpy(buf, cookie->buf + cookie->offset, xbytes);
223
224 cookie->offset += xbytes;
225 return xbytes;
226 }
227
228 int
229 memfile_seek(void *c, off64_t *offset, int whence)
230 {
231 off64_t new_offset;
232 struct memfile_cookie *cookie = c;
233
234 if (whence == SEEK_SET)
235 new_offset = *offset;
236 else if (whence == SEEK_END)
237 new_offset = cookie->endpos + *offset;
238 else if (whence == SEEK_CUR)
239 new_offset = cookie->offset + *offset;
240 else
241 return -1;
242
243 if (new_offset < 0)
244 return -1;
245
246 cookie->offset = new_offset;
247 *offset = new_offset;
248 return 0;
249 }
250
251 int
252 memfile_close(void *c)
253 {
254 struct memfile_cookie *cookie = c;
255
256 free(cookie->buf);
257 cookie->allocated = 0;
258 cookie->buf = NULL;
259
260 return 0;
261 }
262
263 int
264 main(int argc, char *argv[])
265 {
266 cookie_io_functions_t memfile_func = {
267 .read = memfile_read,
268 .write = memfile_write,
269 .seek = memfile_seek,
270 .close = memfile_close
271 };
272 FILE *fp;
273 struct memfile_cookie mycookie;
274 ssize_t nread;
275 long p;
276 int j;
277 char buf[1000];
278
279 /* Set up the cookie before calling fopencookie() */
280
281 mycookie.buf = malloc(INIT_BUF_SIZE);
282 if (mycookie.buf == NULL) {
283 perror("malloc");
284 exit(EXIT_FAILURE);
285 }
286
287 mycookie.allocated = INIT_BUF_SIZE;
288 mycookie.offset = 0;
289 mycookie.endpos = 0;
290
291 fp = fopencookie(&mycookie,"w+", memfile_func);
292 if (fp == NULL) {
293 perror("fopencookie");
294 exit(EXIT_FAILURE);
295 }
296
297 /* Write command-line arguments to our file */
298
299 for (j = 1; j < argc; j++)
300 if (fputs(argv[j], fp) == EOF) {
301 perror("fputs");
302 exit(EXIT_FAILURE);
303 }
304
305 /* Read two bytes out of every five, until EOF */
306
307 for (p = 0; ; p += 5) {
308 if (fseek(fp, p, SEEK_SET) == -1) {
309 perror("fseek");
310 exit(EXIT_FAILURE);
311 }
312 nread = fread(buf, 1, 2, fp);
313 if (nread == -1) {
314 perror("fread");
315 exit(EXIT_FAILURE);
316 }
317 if (nread == 0) {
318 printf("Reached end of file\n");
319 break;
320 }
321
322 printf("/%.*s/\n", nread, buf);
323 }
324
325 exit(EXIT_SUCCESS);
326 }
327
329 fclose(3), fmemopen(3), fopen(3), fseek(3)
330
332 This page is part of release 3.53 of the Linux man-pages project. A
333 description of the project, and information about reporting bugs, can
334 be found at http://www.kernel.org/doc/man-pages/.
335
336
337
338Linux 2013-03-17 FOPENCOOKIE(3)