# fopencookie

## NAME



           fopencookie - open a custom stream
    


## LIBRARY



           Standard C library (_libc_ , _-lc_)
    


## SYNOPSIS



           **#define _GNU_SOURCE** /* See feature_test_macros(7) */
           **#define _FILE_OFFSET_BITS 64**
           **#include <stdio.h>**
    
           **FILE *fopencookie(void *restrict** _cookie_**, const char *restrict** _mode_**,**
                             **cookie_io_functions_t** _io_funcs_**);**
    


## DESCRIPTION



           The **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, **fopencookie**() is used to implement
           fmemopen(3), which provides a stream interface to data that is
           stored in a buffer in memory.
    
           In order to create a custom stream the programmer must:
    
           •  Implement four "hook" functions that are used internally by the
              standard I/O library when performing I/O on the stream.
    
           •  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
              _void *_ when passed to **fopencookie**()), but automatically
              supplies the cookie as the first argument when calling the hook
              functions.
    
           •  Call **fopencookie**() to open a new stream and associate the
              cookie and hook functions with that stream.
    
           The **fopencookie**() function serves a purpose similar to fopen(3):
           it opens a new stream and returns a pointer to a _FILE_ object that
           is used to operate on that stream.
    
           The _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.
    
           The _mode_ argument serves the same purpose as for fopen(3).  The
           following modes are supported: _r_ , _w_ , _a_ , _r+_ , _w+_ , and _a+_.  See
           fopen(3) for details.
    
           The _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
    
               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;
    
           The four fields are as follows:
    
           _cookie_read_function_t *read_
                  This function implements read operations for the stream.
                  When called, it receives three arguments:
    
                      ssize_t read(void *cookie, char *buf, size_t size);
    
                  The _buf_ and _size_ arguments are, respectively, a buffer into
                  which input data can be placed and the size of that buffer.
                  As its function result, the _read_ function should return the
                  number of bytes copied into _buf_ , 0 on end of file, or -1 on
                  error.  The _read_ function should update the stream offset
                  appropriately.
    
                  If _*read_ is a null pointer, then reads from the custom
                  stream always return end of file.
    
           _cookie_write_function_t *write_
                  This function implements write operations for the stream.
                  When called, it receives three arguments:
    
                      ssize_t write(void *cookie, const char *buf, size_t size);
    
                  The _buf_ and _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 _write_ function should
                  return the number of bytes copied from _buf_ , or 0 on error.
                  (The function must not return a negative value.)  The _write_
                  function should update the stream offset appropriately.
    
                  If _*write_ is a null pointer, then output to the stream is
                  discarded.
    
           _cookie_seek_function_t *seek_
                  This function implements seek operations on the stream.
                  When called, it receives three arguments:
    
                      int seek(void *cookie, off_t *offset, int whence);
    
                  The _*offset_ argument specifies the new file offset
                  depending on which of the following three values is
                  supplied in _whence_ :
    
                  **SEEK_SET**
                         The stream offset should be set _*offset_ bytes from
                         the start of the stream.
    
                  **SEEK_CUR**
                         _*offset_ should be added to the current stream
                         offset.
    
                  **SEEK_END**
                         The stream offset should be set to the size of the
                         stream plus _*offset_.
    
                  Before returning, the _seek_ function should update _*offset_
                  to indicate the new stream offset.
    
                  As its function result, the _seek_ function should return 0
                  on success, and -1 on error.
    
                  If _*seek_ is a null pointer, then it is not possible to
                  perform seek operations on the stream.
    
           _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:
    
                      int close(void *cookie);
    
                  The _cookie_ argument is the cookie that the programmer
                  supplied when calling **fopencookie**().
    
                  As its function result, the _close_ function should return 0
                  on success, and **EOF** on error.
    
                  If _*close_ is NULL, then no special action is performed when
                  the stream is closed.
    


## RETURN VALUE



           On success **fopencookie**() returns a pointer to the new stream.  On
           error, NULL is returned.
    


## ATTRIBUTES



           For an explanation of the terms used in this section, see
           attributes(7).
           ┌──────────────────────────────────────┬───────────────┬─────────┐
           │ **Interface** │ **Attribute** │ **Value** │
           ├──────────────────────────────────────┼───────────────┼─────────┤
           │ **fopencookie**()                        │ Thread safety │ MT-Safe │
           └──────────────────────────────────────┴───────────────┴─────────┘
    


## STANDARDS



           GNU.
    


## EXAMPLES



           The program below implements a custom stream whose functionality
           is similar (but not identical) to that available via 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:
    
               $ **./a.out 'hello world'**
               /he/
               / w/
               /d/
               Reached end of file
    
           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).
    
       **Program source**
    
           #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 */
           };
    
           static 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;
           }
    
           static 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;
           }
    
           static 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;
           }
    
           static 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\n");
                           exit(EXIT_FAILURE);
                       }
                       printf("Reached end of file\n");
                       break;
                   }
    
                   printf("/%.*s/\n", (int) nread, buf);
               }
    
               free(mycookie.buf);
    
               exit(EXIT_SUCCESS);
           }
    


## NOTES



           **_FILE_OFFSET_BITS** should be defined to be 64 in code that uses
           non-null _seek_ or that takes the address of **fopencookie** , if the
           code is intended to be portable to traditional 32-bit x86 and ARM
           platforms where **off_t** 's width defaults to 32 bits.
    


* * *