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