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