]>
Commit | Line | Data |
---|---|---|
79bf8cdc MK |
1 | .\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk |
2 | .\" <mtk.manpages@gmail.com> | |
3 | .\" | |
4b72fb64 | 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 | .\" |
6cfe6225 | 26 | .TH FOPENCOOKIE 3 2012-04-30 "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 MK |
32 | .B #include <stdio.h> |
33 | ||
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 MK |
48 | buffer in memory. |
49 | ||
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. | |
78 | ||
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. | |
85 | ||
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. | |
101 | ||
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 | |
107 | .in +4n | |
108 | .nf | |
109 | ||
110 | struct cookie_io_functions_t { | |
111 | cookie_read_function_t *read; | |
112 | cookie_write_function_t *write; | |
113 | cookie_seek_function_t *seek; | |
114 | cookie_close_function_t *close; | |
115 | }; | |
116 | ||
117 | .fi | |
118 | .in | |
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: | |
124 | ||
125 | ssize_t read(void *cookie, char *buf, size_t size); | |
126 | ||
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. | |
141 | ||
142 | If | |
143 | .I *read | |
144 | is a NULL pointer, | |
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: | |
150 | ||
151 | ssize_t write(void *cookie, const char *buf, size_t size); | |
152 | ||
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. | |
168 | ||
169 | If | |
170 | .I *write | |
171 | is a NULL pointer, | |
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: | |
177 | ||
178 | int seek(void *cookie, off64_t *offset, int whence); | |
179 | ||
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. | |
206 | ||
207 | As its function result, the | |
208 | .I seek | |
209 | function should return 0 on success, and \-1 on error. | |
210 | ||
211 | If | |
212 | .I *seek | |
213 | is a NULL pointer, | |
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: | |
221 | ||
222 | int close(void *cookie); | |
223 | ||
224 | The | |
225 | .I cookie | |
226 | argument is the cookie that the programmer supplied when calling | |
227 | .BR fopencookie (). | |
228 | ||
229 | As its function result, the | |
230 | .I close | |
231 | function should return 0 on success, and | |
232 | .B EOF | |
233 | on error. | |
234 | ||
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... | |
245 | .SH CONFORMING TO | |
c8f2dd47 | 246 | This function is a nonstandard GNU extension. |
79bf8cdc MK |
247 | .SH EXAMPLE |
248 | The program below implements a custom stream whose functionality | |
249 | is similar (but not identical) to that available via | |
250 | .BR fmemopen (3). | |
251 | It implements a stream whose data is stored in a memory buffer. | |
252 | The program writes its command-line arguments to the stream, | |
253 | and then seeks through the stream reading two out of every | |
254 | five characters and writing them to standard output. | |
255 | The following shell session demonstrates the use of the program: | |
256 | .in +4n | |
257 | .nf | |
258 | ||
b43a3b30 | 259 | .RB "$" " ./a.out \(aqhello world\(aq" |
79bf8cdc MK |
260 | /he/ |
261 | / w/ | |
262 | /d/ | |
263 | Reached end of file | |
264 | ||
265 | .fi | |
266 | .in | |
267 | Note that a more general version of the program below | |
268 | could be improved to more robustly handle various error situations | |
269 | (e.g., opening a stream with a cookie that already has an open stream; | |
270 | closing a stream that has already been closed). | |
9c330504 | 271 | .SS Program source |
d84d0300 | 272 | \& |
79bf8cdc | 273 | .nf |
79bf8cdc MK |
274 | #define _GNU_SOURCE |
275 | #include <sys/types.h> | |
276 | #include <stdio.h> | |
277 | #include <stdlib.h> | |
278 | #include <unistd.h> | |
279 | #include <string.h> | |
280 | ||
281 | #define INIT_BUF_SIZE 4 | |
282 | ||
283 | struct memfile_cookie { | |
284 | char *buf; /* Dynamically sized buffer for data */ | |
285 | size_t allocated; /* Size of buf */ | |
286 | size_t endpos; /* Number of characters in buf */ | |
287 | off_t offset; /* Current file offset in buf */ | |
288 | }; | |
289 | ||
290 | ssize_t | |
291 | memfile_write(void *c, const char *buf, size_t size) | |
292 | { | |
293 | char *new_buff; | |
294 | struct memfile_cookie *cookie = c; | |
295 | ||
296 | /* Buffer too small? Keep doubling size until big enough */ | |
297 | ||
72da9ef1 MK |
298 | while (size + cookie\->offset > cookie\->allocated) { |
299 | new_buff = realloc(cookie\->buf, cookie\->allocated * 2); | |
79bf8cdc MK |
300 | if (new_buff == NULL) { |
301 | return \-1; | |
302 | } else { | |
303 | cookie\->allocated *= 2; | |
304 | cookie\->buf = new_buff; | |
305 | } | |
306 | } | |
307 | ||
72da9ef1 | 308 | memcpy(cookie\->buf + cookie\->offset, buf, size); |
79bf8cdc MK |
309 | |
310 | cookie\->offset += size; | |
72da9ef1 MK |
311 | if (cookie\->offset > cookie\->endpos) |
312 | cookie\->endpos = cookie\->offset; | |
79bf8cdc MK |
313 | |
314 | return size; | |
315 | } | |
316 | ||
317 | ssize_t | |
318 | memfile_read(void *c, char *buf, size_t size) | |
319 | { | |
320 | ssize_t xbytes; | |
321 | struct memfile_cookie *cookie = c; | |
322 | ||
323 | /* Fetch minimum of bytes requested and bytes available */ | |
324 | ||
325 | xbytes = size; | |
72da9ef1 MK |
326 | if (cookie\->offset + size > cookie\->endpos) |
327 | xbytes = cookie\->endpos \- cookie\->offset; | |
79bf8cdc MK |
328 | if (xbytes < 0) /* offset may be past endpos */ |
329 | xbytes = 0; | |
330 | ||
72da9ef1 | 331 | memcpy(buf, cookie\->buf + cookie\->offset, xbytes); |
79bf8cdc MK |
332 | |
333 | cookie\->offset += xbytes; | |
334 | return xbytes; | |
335 | } | |
336 | ||
337 | int | |
338 | memfile_seek(void *c, off64_t *offset, int whence) | |
339 | { | |
340 | off64_t new_offset; | |
341 | struct memfile_cookie *cookie = c; | |
342 | ||
343 | if (whence == SEEK_SET) | |
344 | new_offset = *offset; | |
345 | else if (whence == SEEK_END) | |
346 | new_offset = cookie\->endpos + *offset; | |
347 | else if (whence == SEEK_CUR) | |
348 | new_offset = cookie\->offset + *offset; | |
349 | else | |
350 | return \-1; | |
351 | ||
352 | if (new_offset < 0) | |
353 | return \-1; | |
354 | ||
355 | cookie\->offset = new_offset; | |
356 | *offset = new_offset; | |
357 | return 0; | |
358 | } | |
359 | ||
360 | int | |
361 | memfile_close(void *c) | |
362 | { | |
363 | struct memfile_cookie *cookie = c; | |
364 | ||
365 | free(cookie\->buf); | |
366 | cookie\->allocated = 0; | |
367 | cookie\->buf = NULL; | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
372 | int | |
373 | main(int argc, char *argv[]) | |
374 | { | |
375 | cookie_io_functions_t memfile_func = { | |
376 | .read = memfile_read, | |
377 | .write = memfile_write, | |
378 | .seek = memfile_seek, | |
379 | .close = memfile_close | |
380 | }; | |
381 | FILE *fp; | |
382 | struct memfile_cookie mycookie; | |
383 | ssize_t nread; | |
384 | long p; | |
385 | int j; | |
386 | char buf[1000]; | |
387 | ||
388 | /* Set up the cookie before calling fopencookie() */ | |
389 | ||
390 | mycookie.buf = malloc(INIT_BUF_SIZE); | |
391 | if (mycookie.buf == NULL) { | |
392 | perror("malloc"); | |
393 | exit(EXIT_FAILURE); | |
394 | } | |
395 | ||
396 | mycookie.allocated = INIT_BUF_SIZE; | |
397 | mycookie.offset = 0; | |
398 | mycookie.endpos = 0; | |
399 | ||
400 | fp = fopencookie(&mycookie,"w+", memfile_func); | |
401 | if (fp == NULL) { | |
402 | perror("fopencookie"); | |
403 | exit(EXIT_FAILURE); | |
404 | } | |
405 | ||
d4e9903b | 406 | /* Write command\-line arguments to our file */ |
79bf8cdc MK |
407 | |
408 | for (j = 1; j < argc; j++) | |
409 | if (fputs(argv[j], fp) == EOF) { | |
410 | perror("fputs"); | |
411 | exit(EXIT_FAILURE); | |
412 | } | |
413 | ||
414 | /* Read two bytes out of every five, until EOF */ | |
415 | ||
416 | for (p = 0; ; p += 5) { | |
417 | if (fseek(fp, p, SEEK_SET) == \-1) { | |
418 | perror("fseek"); | |
419 | exit(EXIT_FAILURE); | |
420 | } | |
421 | nread = fread(buf, 1, 2, fp); | |
422 | if (nread == \-1) { | |
423 | perror("fread"); | |
424 | exit(EXIT_FAILURE); | |
425 | } | |
426 | if (nread == 0) { | |
427 | printf("Reached end of file\\n"); | |
428 | break; | |
429 | } | |
430 | ||
431 | printf("/%.*s/\\n", nread, buf); | |
432 | } | |
433 | ||
434 | exit(EXIT_SUCCESS); | |
435 | } | |
436 | .fi | |
437 | .SH SEE ALSO | |
438 | .BR fclose (3), | |
439 | .BR fmemopen (3), | |
440 | .BR fopen (3), | |
0a4f8b7b | 441 | .BR fseek (3) |