]>
Commit | Line | Data |
---|---|---|
79bf8cdc MK |
1 | .\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk |
2 | .\" <mtk.manpages@gmail.com> | |
3 | .\" | |
93015253 | 4 | .\" %%%LICENSE_START(VERBATIM) |
79bf8cdc MK |
5 | .\" Permission is granted to make and distribute verbatim copies of this |
6 | .\" manual provided the copyright notice and this permission notice are | |
7 | .\" preserved on all copies. | |
8 | .\" | |
9 | .\" Permission is granted to copy and distribute modified versions of this | |
10 | .\" manual under the conditions for verbatim copying, provided that the | |
11 | .\" entire resulting derived work is distributed under the terms of a | |
12 | .\" permission notice identical to this one. | |
13 | .\" | |
14 | .\" Since the Linux kernel and libraries are constantly changing, this | |
15 | .\" manual page may be incorrect or out-of-date. The author(s) assume no | |
16 | .\" responsibility for errors or omissions, or for damages resulting from | |
17 | .\" the use of the information contained herein. The author(s) may not | |
18 | .\" have taken the same level of care in the production of this manual, | |
19 | .\" which is licensed free of charge, as they might when working | |
20 | .\" professionally. | |
21 | .\" | |
22 | .\" Formatted or processed versions of this manual, if unaccompanied by | |
23 | .\" the source, must acknowledge the copyright and authors of this work. | |
4b72fb64 | 24 | .\" %%%LICENSE_END |
79bf8cdc | 25 | .\" |
4b8c67d9 | 26 | .TH FOPENCOOKIE 3 2017-09-15 "Linux" "Linux Programmer's Manual" |
79bf8cdc MK |
27 | .SH NAME |
28 | fopencookie \- opening a custom stream | |
29 | .SH SYNOPSIS | |
30 | .nf | |
b80f966b | 31 | .BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */" |
79bf8cdc | 32 | .B #include <stdio.h> |
dbfe9c70 | 33 | .PP |
79bf8cdc MK |
34 | .BI "FILE *fopencookie(void *" cookie ", const char *" mode , |
35 | .BI " cookie_io_functions_t " io_funcs ); | |
36 | .fi | |
37 | .SH DESCRIPTION | |
38 | The | |
39 | .BR fopencookie () | |
40 | function allows the programmer to create a custom implementation | |
41 | for a standard I/O stream. | |
42 | This implementation can store the stream's data at a location of | |
43 | its own choosing; for example, | |
44 | .BR fopencookie () | |
45 | is used to implement | |
46 | .BR fmemopen (3), | |
183d08ce | 47 | which provides a stream interface to data that is stored in a |
79bf8cdc | 48 | buffer in memory. |
847e0d88 | 49 | .PP |
79bf8cdc MK |
50 | In order to create a custom stream the programmer must: |
51 | .IP * 3 | |
52 | Implement four "hook" functions that are used internally by the | |
53 | standard I/O library when performing I/O on the stream. | |
54 | .IP * | |
55 | Define a "cookie" data type, | |
56 | a structure that provides bookkeeping information | |
57 | (e.g., where to store data) used by the aforementioned hook functions. | |
58 | The standard I/O package knows nothing about the contents of this cookie | |
59 | (thus it is typed as | |
60 | .IR "void\ *" | |
61 | when passed to | |
62 | .BR fopencookie ()), | |
63 | but automatically supplies the cookie | |
64 | as the first argument when calling the hook functions. | |
65 | .IP * | |
66 | Call | |
67 | .BR fopencookie () | |
68 | to open a new stream and associate the cookie and hook functions | |
69 | with that stream. | |
70 | .PP | |
71 | The | |
72 | .BR fopencookie () | |
73 | function serves a purpose similar to | |
74 | .BR fopen (3): | |
d4e9903b | 75 | it opens a new stream and returns a pointer to a |
79bf8cdc MK |
76 | .I FILE |
77 | object that is used to operate on that stream. | |
847e0d88 | 78 | .PP |
79bf8cdc MK |
79 | The |
80 | .I cookie | |
81 | argument is a pointer to the caller's cookie structure | |
82 | that is to be associated with the new stream. | |
83 | This pointer is supplied as the first argument when the standard I/O | |
84 | library invokes any of the hook functions described below. | |
847e0d88 | 85 | .PP |
79bf8cdc MK |
86 | The |
87 | .I mode | |
88 | argument serves the same purpose as for | |
89 | .BR fopen (3). | |
90 | The following modes are supported: | |
91 | .IR r , | |
92 | .IR w , | |
93 | .IR a , | |
94 | .IR r+ , | |
95 | .IR w+ , | |
96 | and | |
97 | .IR a+ . | |
98 | See | |
99 | .BR fopen (3) | |
100 | for details. | |
847e0d88 | 101 | .PP |
79bf8cdc MK |
102 | The |
103 | .I io_funcs | |
104 | argument is a structure that contains four fields pointing to the | |
105 | programmer-defined hook functions that are used to implement this stream. | |
106 | The structure is defined as follows | |
e646a1ba | 107 | .PP |
79bf8cdc | 108 | .in +4n |
e646a1ba | 109 | .EX |
70a97b16 | 110 | typedef struct { |
79bf8cdc MK |
111 | cookie_read_function_t *read; |
112 | cookie_write_function_t *write; | |
113 | cookie_seek_function_t *seek; | |
114 | cookie_close_function_t *close; | |
70a97b16 | 115 | } cookie_io_functions_t; |
e646a1ba | 116 | .EE |
79bf8cdc | 117 | .in |
e646a1ba | 118 | .PP |
79bf8cdc MK |
119 | The four fields are as follows: |
120 | .TP | |
121 | .I cookie_read_function_t *read | |
122 | This function implements read operations for the stream. | |
123 | When called, it receives three arguments: | |
847e0d88 | 124 | .IP |
79bf8cdc | 125 | ssize_t read(void *cookie, char *buf, size_t size); |
847e0d88 | 126 | .IP |
79bf8cdc MK |
127 | The |
128 | .I buf | |
129 | and | |
130 | .I size | |
131 | arguments are, respectively, | |
132 | a buffer into which input data can be placed and the size of that buffer. | |
133 | As its function result, the | |
134 | .I read | |
135 | function should return the number of bytes copied into | |
136 | .IR buf , | |
137 | 0 on end of file, or \-1 on error. | |
138 | The | |
139 | .I read | |
140 | function should update the stream offset appropriately. | |
847e0d88 | 141 | .IP |
79bf8cdc MK |
142 | If |
143 | .I *read | |
b437fdd9 | 144 | is a null pointer, |
79bf8cdc MK |
145 | then reads from the custom stream always return end of file. |
146 | .TP | |
147 | .I cookie_write_function_t *write | |
148 | This function implements write operations for the stream. | |
149 | When called, it receives three arguments: | |
847e0d88 | 150 | .IP |
79bf8cdc | 151 | ssize_t write(void *cookie, const char *buf, size_t size); |
847e0d88 | 152 | .IP |
79bf8cdc MK |
153 | The |
154 | .I buf | |
155 | and | |
156 | .I size | |
157 | arguments are, respectively, | |
158 | a buffer of data to be output to the stream and the size of that buffer. | |
159 | As its function result, the | |
160 | .I write | |
161 | function should return the number of bytes copied from | |
162 | .IR buf , | |
6cfe6225 MK |
163 | or 0 on error. |
164 | (The function must not return a negative value.) | |
79bf8cdc MK |
165 | The |
166 | .I write | |
167 | function should update the stream offset appropriately. | |
847e0d88 | 168 | .IP |
79bf8cdc MK |
169 | If |
170 | .I *write | |
b437fdd9 | 171 | is a null pointer, |
79bf8cdc MK |
172 | then output to the stream is discarded. |
173 | .TP | |
174 | .I cookie_seek_function_t *seek | |
175 | This function implements seek operations on the stream. | |
176 | When called, it receives three arguments: | |
847e0d88 | 177 | .IP |
79bf8cdc | 178 | int seek(void *cookie, off64_t *offset, int whence); |
847e0d88 | 179 | .IP |
79bf8cdc MK |
180 | The |
181 | .I *offset | |
182 | argument specifies the new file offset depending on which | |
183 | of the following three values is supplied in | |
184 | .IR whence : | |
185 | .RS | |
186 | .TP 10 | |
187 | .B SEEK_SET | |
188 | The stream offset should be set | |
189 | .I *offset | |
190 | bytes from the start of the stream. | |
191 | .TP | |
192 | .B SEEK_CUR | |
193 | .I *offset | |
194 | should be added to the current stream offset. | |
195 | .TP | |
196 | .B SEEK_END | |
197 | The stream offset should be set to the size of the stream plus | |
198 | .IR *offset . | |
199 | .RE | |
200 | .IP | |
201 | Before returning, the | |
202 | .I seek | |
203 | function should update | |
204 | .I *offset | |
205 | to indicate the new stream offset. | |
847e0d88 | 206 | .IP |
79bf8cdc MK |
207 | As its function result, the |
208 | .I seek | |
209 | function should return 0 on success, and \-1 on error. | |
847e0d88 | 210 | .IP |
79bf8cdc MK |
211 | If |
212 | .I *seek | |
b437fdd9 | 213 | is a null pointer, |
79bf8cdc MK |
214 | then it is not possible to perform seek operations on the stream. |
215 | .TP | |
216 | .I cookie_close_function_t *close | |
217 | This function closes the stream. | |
218 | The hook function can do things such as freeing buffers allocated | |
219 | for the stream. | |
220 | When called, it receives one argument: | |
847e0d88 | 221 | .IP |
79bf8cdc | 222 | int close(void *cookie); |
847e0d88 | 223 | .IP |
79bf8cdc MK |
224 | The |
225 | .I cookie | |
226 | argument is the cookie that the programmer supplied when calling | |
227 | .BR fopencookie (). | |
847e0d88 | 228 | .IP |
79bf8cdc MK |
229 | As its function result, the |
230 | .I close | |
231 | function should return 0 on success, and | |
232 | .B EOF | |
233 | on error. | |
847e0d88 | 234 | .IP |
79bf8cdc MK |
235 | If |
236 | .I *close | |
237 | is NULL, then no special action is performed when the stream is closed. | |
238 | .SH RETURN VALUE | |
239 | On success | |
240 | .BR fopencookie () | |
241 | returns a pointer to the new stream. | |
242 | On error, NULL is returned. | |
243 | .\" .SH ERRORS | |
244 | .\" It's not clear if errno ever gets set... | |
1c3ceb6a MS |
245 | .SH ATTRIBUTES |
246 | For an explanation of the terms used in this section, see | |
247 | .BR attributes (7). | |
248 | .TS | |
249 | allbox; | |
250 | lb lb lb | |
251 | l l l. | |
252 | Interface Attribute Value | |
253 | T{ | |
254 | .BR fopencookie () | |
255 | T} Thread safety MT-Safe | |
256 | .TE | |
79bf8cdc | 257 | .SH CONFORMING TO |
c8f2dd47 | 258 | This function is a nonstandard GNU extension. |
79bf8cdc MK |
259 | .SH EXAMPLE |
260 | The program below implements a custom stream whose functionality | |
261 | is similar (but not identical) to that available via | |
262 | .BR fmemopen (3). | |
263 | It implements a stream whose data is stored in a memory buffer. | |
264 | The program writes its command-line arguments to the stream, | |
265 | and then seeks through the stream reading two out of every | |
266 | five characters and writing them to standard output. | |
267 | The following shell session demonstrates the use of the program: | |
e646a1ba | 268 | .PP |
79bf8cdc | 269 | .in +4n |
e646a1ba | 270 | .EX |
b43a3b30 | 271 | .RB "$" " ./a.out \(aqhello world\(aq" |
79bf8cdc MK |
272 | /he/ |
273 | / w/ | |
274 | /d/ | |
275 | Reached end of file | |
e646a1ba | 276 | .EE |
79bf8cdc | 277 | .in |
e646a1ba | 278 | .PP |
79bf8cdc MK |
279 | Note that a more general version of the program below |
280 | could be improved to more robustly handle various error situations | |
281 | (e.g., opening a stream with a cookie that already has an open stream; | |
282 | closing a stream that has already been closed). | |
9c330504 | 283 | .SS Program source |
d84d0300 | 284 | \& |
e7d0bb47 | 285 | .EX |
79bf8cdc MK |
286 | #define _GNU_SOURCE |
287 | #include <sys/types.h> | |
288 | #include <stdio.h> | |
289 | #include <stdlib.h> | |
290 | #include <unistd.h> | |
291 | #include <string.h> | |
292 | ||
293 | #define INIT_BUF_SIZE 4 | |
294 | ||
295 | struct memfile_cookie { | |
296 | char *buf; /* Dynamically sized buffer for data */ | |
297 | size_t allocated; /* Size of buf */ | |
298 | size_t endpos; /* Number of characters in buf */ | |
299 | off_t offset; /* Current file offset in buf */ | |
300 | }; | |
301 | ||
302 | ssize_t | |
303 | memfile_write(void *c, const char *buf, size_t size) | |
304 | { | |
305 | char *new_buff; | |
306 | struct memfile_cookie *cookie = c; | |
307 | ||
308 | /* Buffer too small? Keep doubling size until big enough */ | |
309 | ||
72da9ef1 MK |
310 | while (size + cookie\->offset > cookie\->allocated) { |
311 | new_buff = realloc(cookie\->buf, cookie\->allocated * 2); | |
79bf8cdc MK |
312 | if (new_buff == NULL) { |
313 | return \-1; | |
314 | } else { | |
315 | cookie\->allocated *= 2; | |
316 | cookie\->buf = new_buff; | |
317 | } | |
318 | } | |
319 | ||
72da9ef1 | 320 | memcpy(cookie\->buf + cookie\->offset, buf, size); |
79bf8cdc MK |
321 | |
322 | cookie\->offset += size; | |
72da9ef1 MK |
323 | if (cookie\->offset > cookie\->endpos) |
324 | cookie\->endpos = cookie\->offset; | |
79bf8cdc MK |
325 | |
326 | return size; | |
327 | } | |
328 | ||
329 | ssize_t | |
330 | memfile_read(void *c, char *buf, size_t size) | |
331 | { | |
332 | ssize_t xbytes; | |
333 | struct memfile_cookie *cookie = c; | |
334 | ||
335 | /* Fetch minimum of bytes requested and bytes available */ | |
336 | ||
337 | xbytes = size; | |
72da9ef1 MK |
338 | if (cookie\->offset + size > cookie\->endpos) |
339 | xbytes = cookie\->endpos \- cookie\->offset; | |
79bf8cdc MK |
340 | if (xbytes < 0) /* offset may be past endpos */ |
341 | xbytes = 0; | |
342 | ||
72da9ef1 | 343 | memcpy(buf, cookie\->buf + cookie\->offset, xbytes); |
79bf8cdc MK |
344 | |
345 | cookie\->offset += xbytes; | |
346 | return xbytes; | |
347 | } | |
348 | ||
349 | int | |
350 | memfile_seek(void *c, off64_t *offset, int whence) | |
351 | { | |
352 | off64_t new_offset; | |
353 | struct memfile_cookie *cookie = c; | |
354 | ||
355 | if (whence == SEEK_SET) | |
356 | new_offset = *offset; | |
357 | else if (whence == SEEK_END) | |
358 | new_offset = cookie\->endpos + *offset; | |
359 | else if (whence == SEEK_CUR) | |
360 | new_offset = cookie\->offset + *offset; | |
361 | else | |
362 | return \-1; | |
363 | ||
364 | if (new_offset < 0) | |
365 | return \-1; | |
366 | ||
367 | cookie\->offset = new_offset; | |
368 | *offset = new_offset; | |
369 | return 0; | |
370 | } | |
371 | ||
372 | int | |
373 | memfile_close(void *c) | |
374 | { | |
375 | struct memfile_cookie *cookie = c; | |
376 | ||
377 | free(cookie\->buf); | |
378 | cookie\->allocated = 0; | |
379 | cookie\->buf = NULL; | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | int | |
385 | main(int argc, char *argv[]) | |
386 | { | |
387 | cookie_io_functions_t memfile_func = { | |
388 | .read = memfile_read, | |
389 | .write = memfile_write, | |
390 | .seek = memfile_seek, | |
391 | .close = memfile_close | |
392 | }; | |
d41abf5d | 393 | FILE *stream; |
79bf8cdc MK |
394 | struct memfile_cookie mycookie; |
395 | ssize_t nread; | |
396 | long p; | |
397 | int j; | |
398 | char buf[1000]; | |
399 | ||
400 | /* Set up the cookie before calling fopencookie() */ | |
401 | ||
402 | mycookie.buf = malloc(INIT_BUF_SIZE); | |
403 | if (mycookie.buf == NULL) { | |
404 | perror("malloc"); | |
405 | exit(EXIT_FAILURE); | |
406 | } | |
407 | ||
408 | mycookie.allocated = INIT_BUF_SIZE; | |
409 | mycookie.offset = 0; | |
410 | mycookie.endpos = 0; | |
411 | ||
d41abf5d MK |
412 | stream = fopencookie(&mycookie,"w+", memfile_func); |
413 | if (stream == NULL) { | |
79bf8cdc MK |
414 | perror("fopencookie"); |
415 | exit(EXIT_FAILURE); | |
416 | } | |
417 | ||
d4e9903b | 418 | /* Write command\-line arguments to our file */ |
79bf8cdc MK |
419 | |
420 | for (j = 1; j < argc; j++) | |
d41abf5d | 421 | if (fputs(argv[j], stream) == EOF) { |
79bf8cdc MK |
422 | perror("fputs"); |
423 | exit(EXIT_FAILURE); | |
424 | } | |
425 | ||
426 | /* Read two bytes out of every five, until EOF */ | |
427 | ||
428 | for (p = 0; ; p += 5) { | |
d41abf5d | 429 | if (fseek(stream, p, SEEK_SET) == \-1) { |
79bf8cdc MK |
430 | perror("fseek"); |
431 | exit(EXIT_FAILURE); | |
432 | } | |
d41abf5d | 433 | nread = fread(buf, 1, 2, stream); |
79bf8cdc MK |
434 | if (nread == \-1) { |
435 | perror("fread"); | |
436 | exit(EXIT_FAILURE); | |
437 | } | |
438 | if (nread == 0) { | |
439 | printf("Reached end of file\\n"); | |
440 | break; | |
441 | } | |
442 | ||
443 | printf("/%.*s/\\n", nread, buf); | |
444 | } | |
445 | ||
446 | exit(EXIT_SUCCESS); | |
447 | } | |
e7d0bb47 | 448 | .EE |
79bf8cdc MK |
449 | .SH SEE ALSO |
450 | .BR fclose (3), | |
451 | .BR fmemopen (3), | |
452 | .BR fopen (3), | |
0a4f8b7b | 453 | .BR fseek (3) |