.TH MPU_FOPENCOOKIE 3 "September 2026" "libmpuio" "LIBMPUIO Programmer's Manual" .SH NAME mpu_fopencookie \- open an arbitrary callback object as a LIBMPUIO stream .SH SYNOPSIS .nf #include typedef ssize_t mpu_cookie_read_function_t( void *cookie, char *buf, size_t size ); typedef ssize_t mpu_cookie_write_function_t( void *cookie, const char *buf, size_t size ); typedef int mpu_cookie_seek_function_t( void *cookie, off_t *position, int whence ); typedef int mpu_cookie_close_function_t( void *cookie ); typedef struct mpu_cookie_io_functions_t { mpu_cookie_read_function_t *read; mpu_cookie_write_function_t *write; mpu_cookie_seek_function_t *seek; mpu_cookie_close_function_t *close; } mpu_cookie_io_functions_t; mpu_FILE *mpu_fopencookie( void *cookie, const char *mode, mpu_cookie_io_functions_t io_functions ); .fi .SH DESCRIPTION .B mpu_fopencookie creates a LIBMPUIO stream whose external byte transport is supplied by caller callbacks. The opaque .I cookie pointer is stored by the stream and passed unchanged to every callback. .PP The cookie backend is a third, independent LIBMPUIO backend. It does not replace either the descriptor-backed file backend or the native UCS-2 string backend used by .BR mpu_fmemopen (3) and .BR mpu_open_memstream (3). The three backends share the common .B mpu_FILE formatted, buffering, locking, and status machinery through the internal FILE_plus jump-table abstraction. .PP The .I mode syntax is the same strict r/w/a, optional +, optional b grammar accepted by .BR mpu_fopen (3). A readable mode requires a non-NULL .I read callback and a writable mode requires a non-NULL .I write callback. Append mode additionally requires .I seek so LIBMPUIO can place each newly entered output sequence at the logical end. .SH CALLBACKS The .I read callback copies at most .I size external bytes to .I buf and returns the number of bytes produced. Zero denotes end of input. A negative return denotes failure and should leave a useful value in .BR errno . Returning more bytes than requested is treated as an I/O error. .PP The .I write callback consumes at most .I size bytes from .I buf and returns the number accepted. A negative return denotes failure. A zero return for a nonzero request, or a return larger than the request, is treated as .BR EIO . LIBMPUIO may call the callback repeatedly to complete buffered output. .PP The .I seek callback receives the requested position in .I *position and one of .BR MPU_SEEK_SET , .BR MPU_SEEK_CUR , or .BR MPU_SEEK_END . On success it stores the new external-byte position back into .I *position and returns zero. A nonzero result denotes failure. If no seek callback was supplied, positioning operations fail with .BR ESPIPE . .PP The optional .I close callback is invoked once by .BR mpu_fclose (3). A zero return denotes success; a nonzero return makes close fail. The user cookie itself remains caller-defined: LIBMPUIO frees only its private callback descriptor, not the object pointed to by .IR cookie . .SH TEXT AND RAW BYTE MODEL Cookie callbacks are byte oriented. They operate at the same external representation boundary as the file backend. Consequently text functions such as .BR mpu_fgetc (3), .BR mpu_fputs (3), and formatted I/O decode or encode UTF-8 between the callbacks and strict UCS-2 .BR __mpu_char16_t . Invalid UTF-8, surrogate values, and scalar values above U+FFFF retain the normal LIBMPUIO .B EILSEQ semantics. .PP .BR mpu_fread (3) and .BR mpu_fwrite (3) operate directly on the callback byte stream and do not perform text conversion. .SH BUFFERING Cookie streams use the ordinary LIBMPUIO buffering machinery and support .BR mpu_setvbuf (3), .BR mpu_setbuf (3), and .BR mpu_setlinebuf (3). The callback transfer sizes are therefore not required to match individual public read/write calls. .PP When a readable update stream has unread buffered input, switching to output requires the seek callback so the underlying object can be restored to the logical position before writing. .SH POSITIONING If .I seek is supplied, .BR mpu_fseek (3), .BR mpu_fseeko (3), .BR mpu_ftell (3), .BR mpu_ftello (3), .BR mpu_fgetpos (3), and .BR mpu_fsetpos (3) operate in external byte positions. Buffered read-ahead, pending output, and UCS-2 pushback are accounted for by the same logical-position rules used by real UTF-8 files. .PP Cookie streams have no POSIX file descriptor; therefore .BR mpu_fileno (3) fails with .BR EBADF . .SH RETURN VALUE .B mpu_fopencookie returns a new stream on success and NULL on failure. .SH ERRORS .B EINVAL is reported for an invalid mode or for callback sets that cannot satisfy the requested access mode. Append mode without a seek callback is also rejected. .B ESPIPE is reported when positioning is requested on a nonseekable cookie. .B EIO is used for impossible callback transfer counts. Callback failures otherwise preserve the callback's .B errno value and set the stream error indicator where appropriate. .PP A callback that reports failure is responsible for setting .B errno to the reason for that failure. In particular, a failing close callback's errno value is preserved by mpu_fclose() when it is the first close-path error. .SH THREAD SAFETY Ordinary operations on a cookie stream acquire the normal recursive stream mutex. This serializes LIBMPUIO access to the callback set for one stream; it does not make the caller's cookie object safe against unrelated direct access or against access through another stream. .SH NOTES Unlike an implementation that builds memory streams on top of a public cookie API, LIBMPUIO deliberately keeps three independent backends: .IP 1. 3 file backend - native external UTF-8 file I/O; .IP 2. 3 string backend - native UCS-2 memory I/O; .IP 3. 3 cookie backend - arbitrary user-defined byte I/O. .PP In particular, .B mpu_fmemopen is not implemented through .BR mpu_fopencookie . Both are clients of the lower-level internal FILE_plus/jump-table abstraction. .SH CALLBACK REENTRANCY LIBMPUIO does not hold its global stream-list lock while invoking cookie read, write, seek, or close callbacks. A callback may therefore create or close other LIBMPUIO streams without deadlocking global all-stream traversal. Streams already captured by such a traversal are lifetime-pinned until that traversal moves past them. .PP A callback must not recursively close or otherwise re-enter the same cookie stream whose backend operation is currently executing. .SH EXAMPLE The following small program builds a write-only cookie stream that forwards its external bytes to standard output while counting them. It is useful for showing the LIBMPUIO text boundary: the formatter counts UCS-2 characters, while the cookie callback sees the encoded UTF-8 bytes. .PP .nf #include #include #include #include #include struct counting_sink { size_t bytes; }; static ssize_t counting_write( void *cookie, const char *buf, size_t size ) { struct counting_sink *sink = cookie; size_t done = 0; while( done < size ) { ssize_t n = write(STDOUT_FILENO, buf + done, size - done); if( n < 0 ) { if (errno == EINTR) continue; return -1; } if( n == 0 ) { errno = EIO; return -1; } done += (size_t)n; } sink->bytes += done; return (ssize_t)done; } int main(void) { struct counting_sink sink = { 0 }; mpu_cookie_io_functions_t io = { .read = NULL, .write = counting_write, .seek = NULL, .close = NULL }; mpu_FILE *fp; int nchars; __mpu_init(); fp = mpu_fopencookie( &sink, "w", io ); if( fp == NULL ) return 1; nchars = mpu_fprintf( fp, MPU_UCS2("value = %d, pi = %c\\n"), 42, (int)0x03c0 ); if( nchars < 0 || mpu_fclose(fp) != 0 ) return 1; mpu_printf( MPU_UCS2("UCS-2 chars: %d, UTF-8 bytes: %ju\\n"), nchars, (uintmax_t)sink.bytes ); __mpu_free_context(); return 0; } .fi .PP For the formatted line above, the character count is 19 while the callback observes 20 external bytes because U+03C0 is encoded as a two-byte UTF-8 sequence. The example deliberately uses a write-only nonseekable cookie; no seek callback is needed for mode .BR "w" . .SH SEE ALSO .BR libmpuio (3), .BR mpu_fopen (3), .BR mpu_fmemopen (3), .BR mpu_fread (3), .BR mpu_fseek (3), .BR mpu_printf (3), .BR mpu_scanf (3)