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