share/mk/: build-pdf-book: Fix chapter bookmarks
[man-pages.git] / man3 / fopencookie.3
blobd1821772cbdecdb7d54c4813c2b557faf94b2a37
1 '\" t
2 .\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk
3 .\"      <mtk.manpages@gmail.com>
4 .\"
5 .\" SPDX-License-Identifier: Linux-man-pages-copyleft
6 .\"
7 .TH fopencookie 3 (date) "Linux man-pages (unreleased)"
8 .SH NAME
9 fopencookie \- open a custom stream
10 .SH LIBRARY
11 Standard C library
12 .RI ( libc ", " \-lc )
13 .SH SYNOPSIS
14 .nf
15 .BR "#define _GNU_SOURCE" "         /* See feature_test_macros(7) */"
16 .B #define _FILE_OFFSET_BITS 64
17 .B #include <stdio.h>
19 .BI "FILE *fopencookie(void *restrict " cookie ", const char *restrict " mode ,
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),
32 which provides a stream interface to data that is stored in a
33 buffer in memory.
35 In order to create a custom stream the programmer must:
36 .IP \[bu] 3
37 Implement four "hook" functions that are used internally by the
38 standard I/O library when performing I/O on the stream.
39 .IP \[bu]
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
45 .I void\~*
46 when passed to
47 .BR fopencookie ()),
48 but automatically supplies the cookie
49 as the first argument when calling the hook functions.
50 .IP \[bu]
51 Call
52 .BR fopencookie ()
53 to open a new stream and associate the cookie and hook functions
54 with that stream.
56 The
57 .BR fopencookie ()
58 function serves a purpose similar to
59 .BR fopen (3):
60 it opens a new stream and returns a pointer to a
61 .I FILE
62 object that is used to operate on that stream.
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.
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.
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
93 .in +4n
94 .EX
95 typedef struct {
96     cookie_read_function_t  *read;
97     cookie_write_function_t *write;
98     cookie_seek_function_t  *seek;
99     cookie_close_function_t *close;
100 } cookie_io_functions_t;
104 The four fields are as follows:
106 .I cookie_read_function_t *read
107 This function implements read operations for the stream.
108 When called, it receives three arguments:
110 .in +4n
112 ssize_t read(void *cookie, char *buf, size_t size);
117 .I buf
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.
128 .I read
129 function should update the stream offset appropriately.
132 .I *read
133 is a null pointer,
134 then reads from the custom stream always return end of file.
136 .I cookie_write_function_t *write
137 This function implements write operations for the stream.
138 When called, it receives three arguments:
140 .in +4n
142 ssize_t write(void *cookie, const char *buf, size_t size);
147 .I buf
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 ,
156 or 0 on error.
157 (The function must not return a negative value.)
159 .I write
160 function should update the stream offset appropriately.
163 .I *write
164 is a null pointer,
165 then output to the stream is discarded.
167 .I cookie_seek_function_t *seek
168 This function implements seek operations on the stream.
169 When called, it receives three arguments:
171 .in +4n
173 int seek(void *cookie, off_t *offset, int whence);
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 :
184 .B SEEK_SET
185 The stream offset should be set
186 .I *offset
187 bytes from the start of the stream.
189 .B SEEK_CUR
190 .I *offset
191 should be added to the current stream offset.
193 .B SEEK_END
194 The stream offset should be set to the size of the stream plus
195 .IR *offset .
198 Before returning, the
199 .I seek
200 function should update
201 .I *offset
202 to indicate the new stream offset.
204 As its function result, the
205 .I seek
206 function should return 0 on success, and \-1 on error.
209 .I *seek
210 is a null pointer,
211 then it is not possible to perform seek operations on the stream.
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:
219 .in +4n
221 int close(void *cookie);
226 .I cookie
227 argument is the cookie that the programmer supplied when calling
228 .BR fopencookie ().
230 As its function result, the
231 .I close
232 function should return 0 on success, and
233 .B EOF
234 on error.
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...
246 .SH ATTRIBUTES
247 For an explanation of the terms used in this section, see
248 .BR attributes (7).
250 allbox;
251 lbx lb lb
252 l l l.
253 Interface       Attribute       Value
257 .BR fopencookie ()
258 T}      Thread safety   MT-Safe
260 .SH STANDARDS
261 GNU.
262 .SH EXAMPLES
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:
272 .in +4n
274 .RB "$" " ./a.out \[aq]hello world\[aq]"
275 /he/
276 / w/
278 Reached end of file
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).
286 .SS Program source
288 .\" SRC BEGIN (fopencookie.c)
290 #define _GNU_SOURCE
291 #include <stdio.h>
292 #include <stdlib.h>
293 #include <string.h>
294 #include <sys/types.h>
295 #include <unistd.h>
297 #define INIT_BUF_SIZE 4
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 */
306 ssize_t
307 memfile_write(void *c, const char *buf, size_t size)
309     char *new_buff;
310     struct memfile_cookie *cookie = c;
312     /* Buffer too small? Keep doubling size until big enough. */
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         cookie\->allocated *= 2;
319         cookie\->buf = new_buff;
320     }
322     memcpy(cookie\->buf + cookie\->offset, buf, size);
324     cookie\->offset += size;
325     if (cookie\->offset > cookie\->endpos)
326         cookie\->endpos = cookie\->offset;
328     return size;
331 ssize_t
332 memfile_read(void *c, char *buf, size_t size)
334     ssize_t xbytes;
335     struct memfile_cookie *cookie = c;
337     /* Fetch minimum of bytes requested and bytes available. */
339     xbytes = size;
340     if (cookie\->offset + size > cookie\->endpos)
341         xbytes = cookie\->endpos \- cookie\->offset;
342     if (xbytes < 0)     /* offset may be past endpos */
343         xbytes = 0;
345     memcpy(buf, cookie\->buf + cookie\->offset, xbytes);
347     cookie\->offset += xbytes;
348     return xbytes;
352 memfile_seek(void *c, off_t *offset, int whence)
354     off_t new_offset;
355     struct memfile_cookie *cookie = c;
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;
366     if (new_offset < 0)
367         return \-1;
369     cookie\->offset = new_offset;
370     *offset = new_offset;
371     return 0;
375 memfile_close(void *c)
377     struct memfile_cookie *cookie = c;
379     free(cookie\->buf);
380     cookie\->allocated = 0;
381     cookie\->buf = NULL;
383     return 0;
387 main(int argc, char *argv[])
389     cookie_io_functions_t  memfile_func = {
390         .read  = memfile_read,
391         .write = memfile_write,
392         .seek  = memfile_seek,
393         .close = memfile_close
394     };
395     FILE *stream;
396     struct memfile_cookie mycookie;
397     size_t nread;
398     char buf[1000];
400     /* Set up the cookie before calling fopencookie(). */
402     mycookie.buf = malloc(INIT_BUF_SIZE);
403     if (mycookie.buf == NULL) {
404         perror("malloc");
405         exit(EXIT_FAILURE);
406     }
408     mycookie.allocated = INIT_BUF_SIZE;
409     mycookie.offset = 0;
410     mycookie.endpos = 0;
412     stream = fopencookie(&mycookie, "w+", memfile_func);
413     if (stream == NULL) {
414         perror("fopencookie");
415         exit(EXIT_FAILURE);
416     }
418     /* Write command\-line arguments to our file. */
420     for (size_t j = 1; j < argc; j++)
421         if (fputs(argv[j], stream) == EOF) {
422             perror("fputs");
423             exit(EXIT_FAILURE);
424         }
426     /* Read two bytes out of every five, until EOF. */
428     for (long p = 0; ; p += 5) {
429         if (fseek(stream, p, SEEK_SET) == \-1) {
430             perror("fseek");
431             exit(EXIT_FAILURE);
432         }
433         nread = fread(buf, 1, 2, stream);
434         if (nread == 0) {
435             if (ferror(stream) != 0) {
436                 fprintf(stderr, "fread failed\en");
437                 exit(EXIT_FAILURE);
438             }
439             printf("Reached end of file\en");
440             break;
441         }
443         printf("/%.*s/\en", (int) nread, buf);
444     }
446     free(mycookie.buf);
448     exit(EXIT_SUCCESS);
451 .\" SRC END
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.
462 .SH SEE ALSO
463 .BR fclose (3),
464 .BR fmemopen (3),
465 .BR fopen (3),
466 .BR fseek (3)