]> git.ipfire.org Git - thirdparty/man-pages.git/blame - man/man3/fopencookie.3
man/, share/mk/: Move man*/ to man/
[thirdparty/man-pages.git] / man / man3 / fopencookie.3
CommitLineData
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 9fopencookie \- open a custom stream
cfba38f4
AC
10.SH LIBRARY
11Standard 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
23The
24.BR fopencookie ()
25function allows the programmer to create a custom implementation
26for a standard I/O stream.
27This implementation can store the stream's data at a location of
28its own choosing; for example,
29.BR fopencookie ()
30is used to implement
31.BR fmemopen (3),
183d08ce 32which provides a stream interface to data that is stored in a
79bf8cdc 33buffer in memory.
c6d039a3 34.P
79bf8cdc 35In order to create a custom stream the programmer must:
cdede5cd 36.IP \[bu] 3
79bf8cdc
MK
37Implement four "hook" functions that are used internally by the
38standard I/O library when performing I/O on the stream.
cdede5cd 39.IP \[bu]
79bf8cdc
MK
40Define a "cookie" data type,
41a structure that provides bookkeeping information
42(e.g., where to store data) used by the aforementioned hook functions.
43The standard I/O package knows nothing about the contents of this cookie
44(thus it is typed as
1ae6b2c7 45.I void\~*
79bf8cdc
MK
46when passed to
47.BR fopencookie ()),
48but automatically supplies the cookie
49as the first argument when calling the hook functions.
cdede5cd 50.IP \[bu]
79bf8cdc
MK
51Call
52.BR fopencookie ()
53to open a new stream and associate the cookie and hook functions
54with that stream.
c6d039a3 55.P
79bf8cdc
MK
56The
57.BR fopencookie ()
58function serves a purpose similar to
59.BR fopen (3):
d4e9903b 60it opens a new stream and returns a pointer to a
79bf8cdc
MK
61.I FILE
62object that is used to operate on that stream.
c6d039a3 63.P
79bf8cdc
MK
64The
65.I cookie
66argument is a pointer to the caller's cookie structure
67that is to be associated with the new stream.
68This pointer is supplied as the first argument when the standard I/O
69library invokes any of the hook functions described below.
c6d039a3 70.P
79bf8cdc
MK
71The
72.I mode
73argument serves the same purpose as for
74.BR fopen (3).
75The following modes are supported:
76.IR r ,
77.IR w ,
78.IR a ,
79.IR r+ ,
80.IR w+ ,
81and
82.IR a+ .
83See
84.BR fopen (3)
85for details.
c6d039a3 86.P
79bf8cdc
MK
87The
88.I io_funcs
89argument is a structure that contains four fields pointing to the
90programmer-defined hook functions that are used to implement this stream.
91The structure is defined as follows
c6d039a3 92.P
79bf8cdc 93.in +4n
e646a1ba 94.EX
70a97b16 95typedef 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
104The four fields are as follows:
105.TP
106.I cookie_read_function_t *read
107This function implements read operations for the stream.
108When called, it receives three arguments:
847e0d88 109.IP
1ae6b2c7
AC
110.in +4n
111.EX
112ssize_t read(void *cookie, char *buf, size_t size);
113.EE
114.in
847e0d88 115.IP
79bf8cdc
MK
116The
117.I buf
118and
119.I size
120arguments are, respectively,
121a buffer into which input data can be placed and the size of that buffer.
122As its function result, the
123.I read
124function should return the number of bytes copied into
125.IR buf ,
1260 on end of file, or \-1 on error.
127The
128.I read
129function should update the stream offset appropriately.
847e0d88 130.IP
79bf8cdc
MK
131If
132.I *read
b437fdd9 133is a null pointer,
79bf8cdc
MK
134then reads from the custom stream always return end of file.
135.TP
136.I cookie_write_function_t *write
137This function implements write operations for the stream.
138When called, it receives three arguments:
847e0d88 139.IP
1ae6b2c7
AC
140.in +4n
141.EX
142ssize_t write(void *cookie, const char *buf, size_t size);
143.EE
144.in
847e0d88 145.IP
79bf8cdc
MK
146The
147.I buf
148and
149.I size
150arguments are, respectively,
151a buffer of data to be output to the stream and the size of that buffer.
152As its function result, the
153.I write
154function should return the number of bytes copied from
155.IR buf ,
6cfe6225
MK
156or 0 on error.
157(The function must not return a negative value.)
79bf8cdc
MK
158The
159.I write
160function should update the stream offset appropriately.
847e0d88 161.IP
79bf8cdc
MK
162If
163.I *write
b437fdd9 164is a null pointer,
79bf8cdc
MK
165then output to the stream is discarded.
166.TP
167.I cookie_seek_function_t *seek
168This function implements seek operations on the stream.
169When called, it receives three arguments:
847e0d88 170.IP
1ae6b2c7
AC
171.in +4n
172.EX
d4a1b742 173int seek(void *cookie, off_t *offset, int whence);
1ae6b2c7
AC
174.EE
175.in
847e0d88 176.IP
79bf8cdc
MK
177The
178.I *offset
179argument specifies the new file offset depending on which
180of the following three values is supplied in
181.IR whence :
182.RS
0019177e 183.TP
79bf8cdc
MK
184.B SEEK_SET
185The stream offset should be set
186.I *offset
187bytes from the start of the stream.
188.TP
189.B SEEK_CUR
190.I *offset
191should be added to the current stream offset.
192.TP
193.B SEEK_END
194The stream offset should be set to the size of the stream plus
195.IR *offset .
196.RE
197.IP
198Before returning, the
199.I seek
200function should update
201.I *offset
202to indicate the new stream offset.
847e0d88 203.IP
79bf8cdc
MK
204As its function result, the
205.I seek
206function should return 0 on success, and \-1 on error.
847e0d88 207.IP
79bf8cdc
MK
208If
209.I *seek
b437fdd9 210is a null pointer,
79bf8cdc
MK
211then it is not possible to perform seek operations on the stream.
212.TP
213.I cookie_close_function_t *close
214This function closes the stream.
215The hook function can do things such as freeing buffers allocated
216for the stream.
217When called, it receives one argument:
847e0d88 218.IP
1ae6b2c7
AC
219.in +4n
220.EX
221int close(void *cookie);
222.EE
223.in
847e0d88 224.IP
79bf8cdc
MK
225The
226.I cookie
227argument is the cookie that the programmer supplied when calling
228.BR fopencookie ().
847e0d88 229.IP
79bf8cdc
MK
230As its function result, the
231.I close
232function should return 0 on success, and
233.B EOF
234on error.
847e0d88 235.IP
79bf8cdc
MK
236If
237.I *close
238is NULL, then no special action is performed when the stream is closed.
239.SH RETURN VALUE
240On success
241.BR fopencookie ()
242returns a pointer to the new stream.
243On error, NULL is returned.
244.\" .SH ERRORS
245.\" It's not clear if errno ever gets set...
1c3ceb6a
MS
246.SH ATTRIBUTES
247For an explanation of the terms used in this section, see
248.BR attributes (7).
249.TS
250allbox;
c466875e 251lbx lb lb
1c3ceb6a
MS
252l l l.
253Interface Attribute Value
254T{
9e54434e
BR
255.na
256.nh
1c3ceb6a
MS
257.BR fopencookie ()
258T} Thread safety MT-Safe
259.TE
3113c7f3 260.SH STANDARDS
4131356c 261GNU.
a14af333 262.SH EXAMPLES
79bf8cdc
MK
263The program below implements a custom stream whose functionality
264is similar (but not identical) to that available via
265.BR fmemopen (3).
266It implements a stream whose data is stored in a memory buffer.
267The program writes its command-line arguments to the stream,
268and then seeks through the stream reading two out of every
269five characters and writing them to standard output.
270The 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/
278Reached end of file
e646a1ba 279.EE
79bf8cdc 280.in
c6d039a3 281.P
79bf8cdc
MK
282Note that a more general version of the program below
283could be improved to more robustly handle various error situations
284(e.g., opening a stream with a cookie that already has an open stream;
285closing 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
299struct 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
306ssize_t
307memfile_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
331ssize_t
332memfile_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 351int
d4a1b742 352memfile_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
374int
375memfile_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
386int
387main(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
454should be defined to be 64 in code that uses non-null
455.I seek
456or that takes the address of
457.BR fopencookie ,
458if the code is intended to be portable
459to traditional 32-bit x86 and ARM platforms where
460.BR off_t 's
461width 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)