summaryrefslogtreecommitdiffstats
path: root/man3/fopencookie.3
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--man3/fopencookie.3467
1 files changed, 467 insertions, 0 deletions
diff --git a/man3/fopencookie.3 b/man3/fopencookie.3
new file mode 100644
index 0000000..61f77bc
--- /dev/null
+++ b/man3/fopencookie.3
@@ -0,0 +1,467 @@
+'\" t
+.\" Copyright (c) 2008, Linux Foundation, written by Michael Kerrisk
+.\" <mtk.manpages@gmail.com>
+.\"
+.\" SPDX-License-Identifier: Linux-man-pages-copyleft
+.\"
+.TH fopencookie 3 2023-07-20 "Linux man-pages 6.05.01"
+.SH NAME
+fopencookie \- opening a custom stream
+.SH LIBRARY
+Standard C library
+.RI ( libc ", " \-lc )
+.SH SYNOPSIS
+.nf
+.BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */"
+.B #define _FILE_OFFSET_BITS 64
+.B #include <stdio.h>
+.PP
+.BI "FILE *fopencookie(void *restrict " cookie ", const char *restrict " mode ,
+.BI " cookie_io_functions_t " io_funcs );
+.fi
+.SH DESCRIPTION
+The
+.BR fopencookie ()
+function allows the programmer to create a custom implementation
+for a standard I/O stream.
+This implementation can store the stream's data at a location of
+its own choosing; for example,
+.BR fopencookie ()
+is used to implement
+.BR fmemopen (3),
+which provides a stream interface to data that is stored in a
+buffer in memory.
+.PP
+In order to create a custom stream the programmer must:
+.IP \[bu] 3
+Implement four "hook" functions that are used internally by the
+standard I/O library when performing I/O on the stream.
+.IP \[bu]
+Define a "cookie" data type,
+a structure that provides bookkeeping information
+(e.g., where to store data) used by the aforementioned hook functions.
+The standard I/O package knows nothing about the contents of this cookie
+(thus it is typed as
+.I void\~*
+when passed to
+.BR fopencookie ()),
+but automatically supplies the cookie
+as the first argument when calling the hook functions.
+.IP \[bu]
+Call
+.BR fopencookie ()
+to open a new stream and associate the cookie and hook functions
+with that stream.
+.PP
+The
+.BR fopencookie ()
+function serves a purpose similar to
+.BR fopen (3):
+it opens a new stream and returns a pointer to a
+.I FILE
+object that is used to operate on that stream.
+.PP
+The
+.I cookie
+argument is a pointer to the caller's cookie structure
+that is to be associated with the new stream.
+This pointer is supplied as the first argument when the standard I/O
+library invokes any of the hook functions described below.
+.PP
+The
+.I mode
+argument serves the same purpose as for
+.BR fopen (3).
+The following modes are supported:
+.IR r ,
+.IR w ,
+.IR a ,
+.IR r+ ,
+.IR w+ ,
+and
+.IR a+ .
+See
+.BR fopen (3)
+for details.
+.PP
+The
+.I io_funcs
+argument is a structure that contains four fields pointing to the
+programmer-defined hook functions that are used to implement this stream.
+The structure is defined as follows
+.PP
+.in +4n
+.EX
+typedef struct {
+ cookie_read_function_t *read;
+ cookie_write_function_t *write;
+ cookie_seek_function_t *seek;
+ cookie_close_function_t *close;
+} cookie_io_functions_t;
+.EE
+.in
+.PP
+The four fields are as follows:
+.TP
+.I cookie_read_function_t *read
+This function implements read operations for the stream.
+When called, it receives three arguments:
+.IP
+.in +4n
+.EX
+ssize_t read(void *cookie, char *buf, size_t size);
+.EE
+.in
+.IP
+The
+.I buf
+and
+.I size
+arguments are, respectively,
+a buffer into which input data can be placed and the size of that buffer.
+As its function result, the
+.I read
+function should return the number of bytes copied into
+.IR buf ,
+0 on end of file, or \-1 on error.
+The
+.I read
+function should update the stream offset appropriately.
+.IP
+If
+.I *read
+is a null pointer,
+then reads from the custom stream always return end of file.
+.TP
+.I cookie_write_function_t *write
+This function implements write operations for the stream.
+When called, it receives three arguments:
+.IP
+.in +4n
+.EX
+ssize_t write(void *cookie, const char *buf, size_t size);
+.EE
+.in
+.IP
+The
+.I buf
+and
+.I size
+arguments are, respectively,
+a buffer of data to be output to the stream and the size of that buffer.
+As its function result, the
+.I write
+function should return the number of bytes copied from
+.IR buf ,
+or 0 on error.
+(The function must not return a negative value.)
+The
+.I write
+function should update the stream offset appropriately.
+.IP
+If
+.I *write
+is a null pointer,
+then output to the stream is discarded.
+.TP
+.I cookie_seek_function_t *seek
+This function implements seek operations on the stream.
+When called, it receives three arguments:
+.IP
+.in +4n
+.EX
+int seek(void *cookie, off_t *offset, int whence);
+.EE
+.in
+.IP
+The
+.I *offset
+argument specifies the new file offset depending on which
+of the following three values is supplied in
+.IR whence :
+.RS
+.TP
+.B SEEK_SET
+The stream offset should be set
+.I *offset
+bytes from the start of the stream.
+.TP
+.B SEEK_CUR
+.I *offset
+should be added to the current stream offset.
+.TP
+.B SEEK_END
+The stream offset should be set to the size of the stream plus
+.IR *offset .
+.RE
+.IP
+Before returning, the
+.I seek
+function should update
+.I *offset
+to indicate the new stream offset.
+.IP
+As its function result, the
+.I seek
+function should return 0 on success, and \-1 on error.
+.IP
+If
+.I *seek
+is a null pointer,
+then it is not possible to perform seek operations on the stream.
+.TP
+.I cookie_close_function_t *close
+This function closes the stream.
+The hook function can do things such as freeing buffers allocated
+for the stream.
+When called, it receives one argument:
+.IP
+.in +4n
+.EX
+int close(void *cookie);
+.EE
+.in
+.IP
+The
+.I cookie
+argument is the cookie that the programmer supplied when calling
+.BR fopencookie ().
+.IP
+As its function result, the
+.I close
+function should return 0 on success, and
+.B EOF
+on error.
+.IP
+If
+.I *close
+is NULL, then no special action is performed when the stream is closed.
+.SH RETURN VALUE
+On success
+.BR fopencookie ()
+returns a pointer to the new stream.
+On error, NULL is returned.
+.\" .SH ERRORS
+.\" It's not clear if errno ever gets set...
+.SH ATTRIBUTES
+For an explanation of the terms used in this section, see
+.BR attributes (7).
+.TS
+allbox;
+lbx lb lb
+l l l.
+Interface Attribute Value
+T{
+.na
+.nh
+.BR fopencookie ()
+T} Thread safety MT-Safe
+.TE
+.sp 1
+.SH STANDARDS
+GNU.
+.SH EXAMPLES
+The program below implements a custom stream whose functionality
+is similar (but not identical) to that available via
+.BR fmemopen (3).
+It implements a stream whose data is stored in a memory buffer.
+The program writes its command-line arguments to the stream,
+and then seeks through the stream reading two out of every
+five characters and writing them to standard output.
+The following shell session demonstrates the use of the program:
+.PP
+.in +4n
+.EX
+.RB "$" " ./a.out \[aq]hello world\[aq]"
+/he/
+/ w/
+/d/
+Reached end of file
+.EE
+.in
+.PP
+Note that a more general version of the program below
+could be improved to more robustly handle various error situations
+(e.g., opening a stream with a cookie that already has an open stream;
+closing a stream that has already been closed).
+.SS Program source
+\&
+.\" SRC BEGIN (fopencookie.c)
+.EX
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+\&
+#define INIT_BUF_SIZE 4
+\&
+struct memfile_cookie {
+ char *buf; /* Dynamically sized buffer for data */
+ size_t allocated; /* Size of buf */
+ size_t endpos; /* Number of characters in buf */
+ off_t offset; /* Current file offset in buf */
+};
+\&
+ssize_t
+memfile_write(void *c, const char *buf, size_t size)
+{
+ char *new_buff;
+ struct memfile_cookie *cookie = c;
+\&
+ /* Buffer too small? Keep doubling size until big enough. */
+\&
+ while (size + cookie\->offset > cookie\->allocated) {
+ new_buff = realloc(cookie\->buf, cookie\->allocated * 2);
+ if (new_buff == NULL)
+ return \-1;
+ cookie\->allocated *= 2;
+ cookie\->buf = new_buff;
+ }
+\&
+ memcpy(cookie\->buf + cookie\->offset, buf, size);
+\&
+ cookie\->offset += size;
+ if (cookie\->offset > cookie\->endpos)
+ cookie\->endpos = cookie\->offset;
+\&
+ return size;
+}
+\&
+ssize_t
+memfile_read(void *c, char *buf, size_t size)
+{
+ ssize_t xbytes;
+ struct memfile_cookie *cookie = c;
+\&
+ /* Fetch minimum of bytes requested and bytes available. */
+\&
+ xbytes = size;
+ if (cookie\->offset + size > cookie\->endpos)
+ xbytes = cookie\->endpos \- cookie\->offset;
+ if (xbytes < 0) /* offset may be past endpos */
+ xbytes = 0;
+\&
+ memcpy(buf, cookie\->buf + cookie\->offset, xbytes);
+\&
+ cookie\->offset += xbytes;
+ return xbytes;
+}
+\&
+int
+memfile_seek(void *c, off_t *offset, int whence)
+{
+ off_t new_offset;
+ struct memfile_cookie *cookie = c;
+\&
+ if (whence == SEEK_SET)
+ new_offset = *offset;
+ else if (whence == SEEK_END)
+ new_offset = cookie\->endpos + *offset;
+ else if (whence == SEEK_CUR)
+ new_offset = cookie\->offset + *offset;
+ else
+ return \-1;
+\&
+ if (new_offset < 0)
+ return \-1;
+\&
+ cookie\->offset = new_offset;
+ *offset = new_offset;
+ return 0;
+}
+\&
+int
+memfile_close(void *c)
+{
+ struct memfile_cookie *cookie = c;
+\&
+ free(cookie\->buf);
+ cookie\->allocated = 0;
+ cookie\->buf = NULL;
+\&
+ return 0;
+}
+\&
+int
+main(int argc, char *argv[])
+{
+ cookie_io_functions_t memfile_func = {
+ .read = memfile_read,
+ .write = memfile_write,
+ .seek = memfile_seek,
+ .close = memfile_close
+ };
+ FILE *stream;
+ struct memfile_cookie mycookie;
+ size_t nread;
+ char buf[1000];
+\&
+ /* Set up the cookie before calling fopencookie(). */
+\&
+ mycookie.buf = malloc(INIT_BUF_SIZE);
+ if (mycookie.buf == NULL) {
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+\&
+ mycookie.allocated = INIT_BUF_SIZE;
+ mycookie.offset = 0;
+ mycookie.endpos = 0;
+\&
+ stream = fopencookie(&mycookie, "w+", memfile_func);
+ if (stream == NULL) {
+ perror("fopencookie");
+ exit(EXIT_FAILURE);
+ }
+\&
+ /* Write command\-line arguments to our file. */
+\&
+ for (size_t j = 1; j < argc; j++)
+ if (fputs(argv[j], stream) == EOF) {
+ perror("fputs");
+ exit(EXIT_FAILURE);
+ }
+\&
+ /* Read two bytes out of every five, until EOF. */
+\&
+ for (long p = 0; ; p += 5) {
+ if (fseek(stream, p, SEEK_SET) == \-1) {
+ perror("fseek");
+ exit(EXIT_FAILURE);
+ }
+ nread = fread(buf, 1, 2, stream);
+ if (nread == 0) {
+ if (ferror(stream) != 0) {
+ fprintf(stderr, "fread failed\en");
+ exit(EXIT_FAILURE);
+ }
+ printf("Reached end of file\en");
+ break;
+ }
+\&
+ printf("/%.*s/\en", (int) nread, buf);
+ }
+\&
+ free(mycookie.buf);
+\&
+ exit(EXIT_SUCCESS);
+}
+.EE
+.\" SRC END
+.SH NOTES
+.B _FILE_OFFSET_BITS
+should be defined to be 64 in code that uses non-null
+.I seek
+or that takes the address of
+.BR fopencookie ,
+if the code is intended to be portable
+to traditional 32-bit x86 and ARM platforms where
+.BR off_t 's
+width defaults to 32 bits.
+.SH SEE ALSO
+.BR fclose (3),
+.BR fmemopen (3),
+.BR fopen (3),
+.BR fseek (3)