]> git.ipfire.org Git - thirdparty/man-pages.git/blob - man3/fopencookie.3
9dfb33df3831ede4780e67b9f78f5dc7360b84b8
[thirdparty/man-pages.git] / man3 / fopencookie.3
1 .\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk
2 .\" <mtk.manpages@gmail.com>
3 .\"
4 .\" SPDX-License-Identifier: Linux-man-pages-copyleft
5 .\"
6 .TH FOPENCOOKIE 3 2021-03-22 "Linux man-pages (unreleased)" "Linux Programmer's Manual"
7 .SH NAME
8 fopencookie \- opening a custom stream
9 .SH LIBRARY
10 Standard C library
11 .RI ( libc ", " \-lc )
12 .SH SYNOPSIS
13 .nf
14 .BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */"
15 .B #include <stdio.h>
16 .PP
17 .BI "FILE *fopencookie(void *restrict " cookie ", const char *restrict " mode ,
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),
30 which provides a stream interface to data that is stored in a
31 buffer in memory.
32 .PP
33 In order to create a custom stream the programmer must:
34 .IP * 3
35 Implement four "hook" functions that are used internally by the
36 standard I/O library when performing I/O on the stream.
37 .IP *
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
43 .I void\~*
44 when passed to
45 .BR fopencookie ()),
46 but automatically supplies the cookie
47 as the first argument when calling the hook functions.
48 .IP *
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):
58 it opens a new stream and returns a pointer to a
59 .I FILE
60 object that is used to operate on that stream.
61 .PP
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.
68 .PP
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.
84 .PP
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
90 .PP
91 .in +4n
92 .EX
93 typedef struct {
94 cookie_read_function_t *read;
95 cookie_write_function_t *write;
96 cookie_seek_function_t *seek;
97 cookie_close_function_t *close;
98 } cookie_io_functions_t;
99 .EE
100 .in
101 .PP
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:
107 .IP
108 .in +4n
109 .EX
110 ssize_t read(void *cookie, char *buf, size_t size);
111 .EE
112 .in
113 .IP
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.
128 .IP
129 If
130 .I *read
131 is a null pointer,
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:
137 .IP
138 .in +4n
139 .EX
140 ssize_t write(void *cookie, const char *buf, size_t size);
141 .EE
142 .in
143 .IP
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 ,
154 or 0 on error.
155 (The function must not return a negative value.)
156 The
157 .I write
158 function should update the stream offset appropriately.
159 .IP
160 If
161 .I *write
162 is a null pointer,
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:
168 .IP
169 .in +4n
170 .EX
171 int seek(void *cookie, off64_t *offset, int whence);
172 .EE
173 .in
174 .IP
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
181 .TP
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.
201 .IP
202 As its function result, the
203 .I seek
204 function should return 0 on success, and \-1 on error.
205 .IP
206 If
207 .I *seek
208 is a null pointer,
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:
216 .IP
217 .in +4n
218 .EX
219 int close(void *cookie);
220 .EE
221 .in
222 .IP
223 The
224 .I cookie
225 argument is the cookie that the programmer supplied when calling
226 .BR fopencookie ().
227 .IP
228 As its function result, the
229 .I close
230 function should return 0 on success, and
231 .B EOF
232 on error.
233 .IP
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...
244 .SH ATTRIBUTES
245 For an explanation of the terms used in this section, see
246 .BR attributes (7).
247 .ad l
248 .nh
249 .TS
250 allbox;
251 lbx lb lb
252 l l l.
253 Interface Attribute Value
254 T{
255 .BR fopencookie ()
256 T} Thread safety MT-Safe
257 .TE
258 .hy
259 .ad
260 .sp 1
261 .SH STANDARDS
262 This function is a nonstandard GNU extension.
263 .SH EXAMPLES
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:
272 .PP
273 .in +4n
274 .EX
275 .RB "$" " ./a.out \(aqhello world\(aq"
276 /he/
277 / w/
278 /d/
279 Reached end of file
280 .EE
281 .in
282 .PP
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).
287 .SS Program source
288 \&
289 .EX
290 #define _GNU_SOURCE
291 #include <sys/types.h>
292 #include <stdio.h>
293 #include <stdlib.h>
294 #include <unistd.h>
295 #include <string.h>
296
297 #define INIT_BUF_SIZE 4
298
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 };
305
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;
311
312 /* Buffer too small? Keep doubling size until big enough. */
313
314 while (size + cookie\->offset > cookie\->allocated) {
315 new_buff = realloc(cookie\->buf, cookie\->allocated * 2);
316 if (new_buff == NULL) {
317 return \-1;
318 } else {
319 cookie\->allocated *= 2;
320 cookie\->buf = new_buff;
321 }
322 }
323
324 memcpy(cookie\->buf + cookie\->offset, buf, size);
325
326 cookie\->offset += size;
327 if (cookie\->offset > cookie\->endpos)
328 cookie\->endpos = cookie\->offset;
329
330 return size;
331 }
332
333 ssize_t
334 memfile_read(void *c, char *buf, size_t size)
335 {
336 ssize_t xbytes;
337 struct memfile_cookie *cookie = c;
338
339 /* Fetch minimum of bytes requested and bytes available. */
340
341 xbytes = size;
342 if (cookie\->offset + size > cookie\->endpos)
343 xbytes = cookie\->endpos \- cookie\->offset;
344 if (xbytes < 0) /* offset may be past endpos */
345 xbytes = 0;
346
347 memcpy(buf, cookie\->buf + cookie\->offset, xbytes);
348
349 cookie\->offset += xbytes;
350 return xbytes;
351 }
352
353 int
354 memfile_seek(void *c, off64_t *offset, int whence)
355 {
356 off64_t new_offset;
357 struct memfile_cookie *cookie = c;
358
359 if (whence == SEEK_SET)
360 new_offset = *offset;
361 else if (whence == SEEK_END)
362 new_offset = cookie\->endpos + *offset;
363 else if (whence == SEEK_CUR)
364 new_offset = cookie\->offset + *offset;
365 else
366 return \-1;
367
368 if (new_offset < 0)
369 return \-1;
370
371 cookie\->offset = new_offset;
372 *offset = new_offset;
373 return 0;
374 }
375
376 int
377 memfile_close(void *c)
378 {
379 struct memfile_cookie *cookie = c;
380
381 free(cookie\->buf);
382 cookie\->allocated = 0;
383 cookie\->buf = NULL;
384
385 return 0;
386 }
387
388 int
389 main(int argc, char *argv[])
390 {
391 cookie_io_functions_t memfile_func = {
392 .read = memfile_read,
393 .write = memfile_write,
394 .seek = memfile_seek,
395 .close = memfile_close
396 };
397 FILE *stream;
398 struct memfile_cookie mycookie;
399 size_t nread;
400 char buf[1000];
401
402 /* Set up the cookie before calling fopencookie(). */
403
404 mycookie.buf = malloc(INIT_BUF_SIZE);
405 if (mycookie.buf == NULL) {
406 perror("malloc");
407 exit(EXIT_FAILURE);
408 }
409
410 mycookie.allocated = INIT_BUF_SIZE;
411 mycookie.offset = 0;
412 mycookie.endpos = 0;
413
414 stream = fopencookie(&mycookie, "w+", memfile_func);
415 if (stream == NULL) {
416 perror("fopencookie");
417 exit(EXIT_FAILURE);
418 }
419
420 /* Write command\-line arguments to our file. */
421
422 for (int j = 1; j < argc; j++)
423 if (fputs(argv[j], stream) == EOF) {
424 perror("fputs");
425 exit(EXIT_FAILURE);
426 }
427
428 /* Read two bytes out of every five, until EOF. */
429
430 for (long p = 0; ; p += 5) {
431 if (fseek(stream, p, SEEK_SET) == \-1) {
432 perror("fseek");
433 exit(EXIT_FAILURE);
434 }
435 nread = fread(buf, 1, 2, stream);
436 if (nread == 0) {
437 if (ferror(stream) != 0) {
438 fprintf(stderr, "fread failed\en");
439 exit(EXIT_FAILURE);
440 }
441 printf("Reached end of file\en");
442 break;
443 }
444
445 printf("/%.*s/\en", (int) nread, buf);
446 }
447
448 exit(EXIT_SUCCESS);
449 }
450 .EE
451 .SH SEE ALSO
452 .BR fclose (3),
453 .BR fmemopen (3),
454 .BR fopen (3),
455 .BR fseek (3)