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