diff options
Diffstat (limited to 'man3/fopencookie.3')
-rw-r--r-- | man3/fopencookie.3 | 467 |
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) |