/*************************************************************** MPU-COOKIE.C This file contains source code of functions for user-defined cookie stream operations. PART OF : MPUIO - library . USAGE : Internal and external . NOTE : Cookie callbacks transfer external raw bytes. Text I/O uses UTF-8 at the cookie boundary. Copyright (C) 2000 - 2026 by Andrey V.Kosteltsev. All Rights Reserved. ***************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include /*************************************************************** Return the private callback descriptor attached to STREAM. ***************************************************************/ static __mpu_IO_cookie_file *__mpu_IO_cookie_data( mpu_FILE *stream ) { return( (__mpu_IO_cookie_file *)stream->_cookie ); } /* End of __mpu_IO_cookie_data() */ /*************************************************************** Invoke the cookie read callback, retrying EINTR reports. ***************************************************************/ static ssize_t __mpu_IO_cookie_read( mpu_FILE *stream, void *buf, size_t size ) { __mpu_IO_cookie_file *cf = __mpu_IO_cookie_data( stream ); ssize_t ret; if( cf == (__mpu_IO_cookie_file *)0 || cf->io.read == (mpu_cookie_read_function_t *)0 ) { errno = EBADF; return( -1 ); } do { ret = (*cf->io.read)( cf->user_cookie, (char *)buf, size ); } while( ret < 0 && errno == EINTR ); if( ret > 0 && (size_t)ret > size ) { errno = EIO; return( -1 ); } return( ret ); } /* End of __mpu_IO_cookie_read() */ /*************************************************************** Invoke the cookie write callback until SIZE bytes are written. ***************************************************************/ static size_t __mpu_IO_cookie_write_bytes( mpu_FILE *stream, const unsigned char *buf, size_t size, int *complete ) { __mpu_IO_cookie_file *cf = __mpu_IO_cookie_data( stream ); size_t done = 0; *complete = 0; if( cf == (__mpu_IO_cookie_file *)0 || cf->io.write == (mpu_cookie_write_function_t *)0 ) { errno = EBADF; stream->_flags |= __MPU_IO_ERR_SEEN; return( 0 ); } while( done < size ) { ssize_t n; do { n = (*cf->io.write)( cf->user_cookie, (const char *)buf + done, size - done ); } while( n < 0 && errno == EINTR ); if( n < 0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( done ); } if( n == 0 || (size_t)n > size - done ) { errno = EIO; stream->_flags |= __MPU_IO_ERR_SEEN; return( done ); } done += (size_t)n; } *complete = 1; return( done ); } /* End of __mpu_IO_cookie_write_bytes() */ /*************************************************************** Reposition the underlying cookie by invoking its seek callback. ***************************************************************/ static off_t __mpu_IO_cookie_call_seek( mpu_FILE *stream, off_t offset, int whence ) { __mpu_IO_cookie_file *cf = __mpu_IO_cookie_data( stream ); off_t pos = offset; if( cf == (__mpu_IO_cookie_file *)0 || cf->io.seek == (mpu_cookie_seek_function_t *)0 ) { errno = ESPIPE; return( (off_t)-1 ); } if( (*cf->io.seek)( cf->user_cookie, &pos, whence ) != 0 ) return( (off_t)-1 ); return( pos ); } /* End of __mpu_IO_cookie_call_seek() */ /*************************************************************** Synchronize unread buffered bytes with a seekable cookie. ***************************************************************/ static int __mpu_IO_cookie_sync_input_unlocked( mpu_FILE *stream ) { off_t unread = 0; if( !(stream->_flags & __MPU_IO_READING) ) return( 0 ); if( stream->_read_ptr != (unsigned char *)0 && stream->_read_end != (unsigned char *)0 ) unread = (off_t)(stream->_read_end - stream->_read_ptr); if( unread != 0 && __mpu_IO_cookie_call_seek( stream, -unread, MPU_SEEK_CUR ) == (off_t)-1 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base; return( 0 ); } /* End of __mpu_IO_cookie_sync_input_unlocked() */ /*************************************************************** Flush buffered cookie output or synchronize buffered input. ***************************************************************/ static int __mpu_IO_cookie_flush_unlocked( mpu_FILE *stream ) { size_t size; if( stream->_flags & __MPU_IO_WRITING ) { size = 0; if( stream->_write_base != (unsigned char *)0 && stream->_write_ptr != (unsigned char *)0 ) size = (size_t)(stream->_write_ptr - stream->_write_base); if( size != 0 ) { size_t done; int complete; done = __mpu_IO_cookie_write_bytes( stream, stream->_write_base, size, &complete ); if( !complete ) { if( done != 0 && done < size ) memmove( stream->_write_base, stream->_write_base + done, size - done ); stream->_write_ptr = stream->_write_base + (size - done); stream->_write_end = stream->_buf_end; return( mpu_EOF ); } } stream->_write_base = stream->_buf_base; stream->_write_ptr = stream->_buf_base; stream->_write_end = stream->_buf_end; } else if( stream->_flags & __MPU_IO_READING ) { if( __mpu_IO_cookie_sync_input_unlocked( stream ) == mpu_EOF ) return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_cookie_flush_unlocked() */ /*************************************************************** Switch a cookie stream to input mode. ***************************************************************/ static int __mpu_IO_cookie_switch_to_read( mpu_FILE *stream ) { if( stream->_flags & __MPU_IO_NO_READS ) { errno = EBADF; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } if( stream->_flags & __MPU_IO_WRITING ) { if( __mpu_IO_cookie_flush_unlocked( stream ) == mpu_EOF ) return( mpu_EOF ); stream->_flags &= ~__MPU_IO_WRITING; } if( __mpu_IO_doallocbuf( stream ) == mpu_EOF ) return( mpu_EOF ); if( !(stream->_flags & __MPU_IO_READING) ) { stream->_flags |= __MPU_IO_READING; stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base; } return( 0 ); } /* End of __mpu_IO_cookie_switch_to_read() */ /*************************************************************** Switch a cookie stream to output mode. ***************************************************************/ static int __mpu_IO_cookie_switch_to_write( mpu_FILE *stream ) { if( stream->_flags & __MPU_IO_NO_WRITES ) { errno = EBADF; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } if( stream->_flags & __MPU_IO_READING ) { if( __mpu_IO_cookie_sync_input_unlocked( stream ) == mpu_EOF ) return( mpu_EOF ); stream->_flags &= ~__MPU_IO_READING; } if( __mpu_IO_doallocbuf( stream ) == mpu_EOF ) return( mpu_EOF ); if( !(stream->_flags & __MPU_IO_WRITING) ) { if( (stream->_flags & __MPU_IO_APPEND) && __mpu_IO_cookie_call_seek( stream, (off_t)0, MPU_SEEK_END ) == (off_t)-1 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } stream->_flags |= __MPU_IO_WRITING; stream->_write_base = stream->_buf_base; stream->_write_ptr = stream->_buf_base; stream->_write_end = stream->_buf_end; } return( 0 ); } /* End of __mpu_IO_cookie_switch_to_write() */ /*************************************************************** Refill a cookie input buffer and return the next byte. ***************************************************************/ static int __mpu_IO_cookie_underflow_unlocked( mpu_FILE *stream ) { ssize_t n; if( __mpu_IO_cookie_switch_to_read( stream ) == mpu_EOF ) return( mpu_EOF ); if( stream->_read_ptr < stream->_read_end ) return( (int)*stream->_read_ptr ); n = __mpu_IO_cookie_read( stream, stream->_buf_base, (size_t)(stream->_buf_end - stream->_buf_base) ); if( n < 0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } if( n == 0 ) { stream->_flags |= __MPU_IO_EOF_SEEN; return( mpu_EOF ); } stream->_flags &= ~__MPU_IO_EOF_SEEN; stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base + n; return( (int)*stream->_read_ptr ); } /* End of __mpu_IO_cookie_underflow_unlocked() */ /*************************************************************** Flush cookie output and optionally append one byte. ***************************************************************/ static int __mpu_IO_cookie_overflow_unlocked( mpu_FILE *stream, int c ) { unsigned char ch; if( __mpu_IO_cookie_switch_to_write( stream ) == mpu_EOF ) return( mpu_EOF ); if( stream->_write_ptr != stream->_write_base && __mpu_IO_cookie_flush_unlocked( stream ) == mpu_EOF ) return( mpu_EOF ); if( c == mpu_EOF ) return( 0 ); ch = (unsigned char)c; if( stream->_flags & __MPU_IO_UNBUFFERED ) { int complete; return( __mpu_IO_cookie_write_bytes( stream, &ch, 1, &complete ) == 1 && complete ? c : mpu_EOF ); } *stream->_write_ptr++ = ch; if( (stream->_flags & __MPU_IO_LINE_BUF) && ch == '\n' && __mpu_IO_cookie_flush_unlocked( stream ) == mpu_EOF ) return( mpu_EOF ); return( c ); } /* End of __mpu_IO_cookie_overflow_unlocked() */ /*************************************************************** Synchronize buffered cookie state. ***************************************************************/ static int __mpu_IO_cookie_sync_unlocked( mpu_FILE *stream ) { return( __mpu_IO_cookie_flush_unlocked( stream ) ); } /* End of __mpu_IO_cookie_sync_unlocked() */ /*************************************************************** Read one raw byte from a cookie stream. ***************************************************************/ static int __mpu_IO_cookie_get_byte_unlocked( mpu_FILE *stream ) { int c; if( __mpu_IO_cookie_switch_to_read( stream ) == mpu_EOF ) return( mpu_EOF ); if( stream->_read_ptr >= stream->_read_end ) { c = __mpu_IO_cookie_underflow_unlocked( stream ); if( c == mpu_EOF ) return( mpu_EOF ); } return( (int)*stream->_read_ptr++ ); } /* End of __mpu_IO_cookie_get_byte_unlocked() */ /*************************************************************** Write one raw byte to a cookie stream. ***************************************************************/ static int __mpu_IO_cookie_put_byte_unlocked( unsigned char c, mpu_FILE *stream ) { if( __mpu_IO_cookie_switch_to_write( stream ) == mpu_EOF ) return( mpu_EOF ); if( stream->_flags & __MPU_IO_UNBUFFERED ) return( __mpu_IO_cookie_overflow_unlocked( stream, (int)c ) ); if( stream->_write_ptr == stream->_write_end && __mpu_IO_cookie_overflow_unlocked( stream, mpu_EOF ) == mpu_EOF ) return( mpu_EOF ); *stream->_write_ptr++ = c; if( (stream->_flags & __MPU_IO_LINE_BUF) && c == '\n' && __mpu_IO_cookie_overflow_unlocked( stream, mpu_EOF ) == mpu_EOF ) return( mpu_EOF ); return( (int)c ); } /* End of __mpu_IO_cookie_put_byte_unlocked() */ /*************************************************************** Read SIZE raw bytes through a cookie callback. ***************************************************************/ static size_t __mpu_IO_cookie_xsgetn_unlocked( mpu_FILE *stream, void *data, size_t size ) { unsigned char *p = (unsigned char *)data; size_t done = 0; if( __mpu_IO_cookie_switch_to_read( stream ) == mpu_EOF ) return( 0 ); while( done < size ) { size_t avail = (size_t)(stream->_read_end - stream->_read_ptr); size_t want; if( avail != 0 ) { want = size - done; if( want > avail ) want = avail; memcpy( p + done, stream->_read_ptr, want ); stream->_read_ptr += want; done += want; continue; } want = size - done; if( want >= (size_t)(stream->_buf_end - stream->_buf_base) ) { ssize_t n = __mpu_IO_cookie_read( stream, p + done, want ); if( n < 0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; break; } if( n == 0 ) { stream->_flags |= __MPU_IO_EOF_SEEN; break; } done += (size_t)n; continue; } { ssize_t n = __mpu_IO_cookie_read( stream, stream->_buf_base, (size_t)(stream->_buf_end - stream->_buf_base) ); if( n < 0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; break; } if( n == 0 ) { stream->_flags |= __MPU_IO_EOF_SEEN; break; } stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base + n; } } return( done ); } /* End of __mpu_IO_cookie_xsgetn_unlocked() */ /*************************************************************** Write SIZE raw bytes through a cookie callback. ***************************************************************/ static size_t __mpu_IO_cookie_xsputn_unlocked( mpu_FILE *stream, const void *data, size_t size ) { const unsigned char *p = (const unsigned char *)data; size_t done = 0; if( __mpu_IO_cookie_switch_to_write( stream ) == mpu_EOF ) return( 0 ); if( stream->_flags & (__MPU_IO_UNBUFFERED | __MPU_IO_LINE_BUF) ) { while( done < size ) { if( __mpu_IO_cookie_put_byte_unlocked( p[done], stream ) == mpu_EOF ) break; ++done; } return( done ); } while( done < size ) { size_t avail = (size_t)(stream->_write_end - stream->_write_ptr); size_t want; if( avail != 0 ) { want = size - done; if( want > avail ) want = avail; memcpy( stream->_write_ptr, p + done, want ); stream->_write_ptr += want; done += want; if( done == size ) break; } if( stream->_write_ptr == stream->_write_end && __mpu_IO_cookie_flush_unlocked( stream ) == mpu_EOF ) break; want = size - done; if( want >= (size_t)(stream->_buf_end - stream->_buf_base) ) { size_t n; int complete; n = __mpu_IO_cookie_write_bytes( stream, p + done, want, &complete ); done += n; if( !complete ) break; } } return( done ); } /* End of __mpu_IO_cookie_xsputn_unlocked() */ /*************************************************************** Finish a cookie stream. Output is flushed; buffered input is discarded because close must work for non-seekable cookies. ***************************************************************/ static int __mpu_IO_cookie_finish_unlocked( mpu_FILE *stream ) { if( stream->_flags & __MPU_IO_WRITING ) return( __mpu_IO_cookie_flush_unlocked( stream ) ); stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base; stream->_unget_count = 0; return( 0 ); } /* End of __mpu_IO_cookie_finish_unlocked() */ /*************************************************************** Configure buffering for a cookie stream. ***************************************************************/ static int __mpu_IO_cookie_setbuf_unlocked( mpu_FILE *stream, unsigned char *buf, int mode, size_t size ) { unsigned char *newbuf = buf; unsigned char *oldbuf; int newbuf_owned = 0; if( mode != MPU_IOFBF && mode != MPU_IOLBF && mode != MPU_IONBF ) { errno = EINVAL; return( -1 ); } if( mode != MPU_IONBF && size == 0 ) { errno = EINVAL; return( -1 ); } /* Allocate replacement storage before touching the current cookie buffering state. A failed allocation leaves that state usable. */ if( mode == MPU_IONBF ) { size = 1; newbuf = (unsigned char *)malloc( 1 ); if( newbuf == (unsigned char *)0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( -1 ); } newbuf_owned = 1; } else if( newbuf == (unsigned char *)0 ) { newbuf = (unsigned char *)malloc( size ); if( newbuf == (unsigned char *)0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( -1 ); } newbuf_owned = 1; } if( __mpu_IO_cookie_sync_unlocked( stream ) == mpu_EOF ) { if( newbuf_owned ) free( newbuf ); return( -1 ); } oldbuf = stream->_buf_base; if( oldbuf != (unsigned char *)0 && !(stream->_flags & __MPU_IO_USER_BUF) ) free( oldbuf ); stream->_buf_base = newbuf; stream->_buf_end = newbuf + size; stream->_read_base = (unsigned char *)0; stream->_read_ptr = (unsigned char *)0; stream->_read_end = (unsigned char *)0; stream->_write_base = (unsigned char *)0; stream->_write_ptr = (unsigned char *)0; stream->_write_end = (unsigned char *)0; stream->_flags &= ~(__MPU_IO_USER_BUF | __MPU_IO_LINE_BUF | __MPU_IO_UNBUFFERED | __MPU_IO_READING | __MPU_IO_WRITING); if( mode == MPU_IONBF ) stream->_flags |= __MPU_IO_UNBUFFERED; else if( !newbuf_owned ) stream->_flags |= __MPU_IO_USER_BUF; if( mode == MPU_IOLBF ) stream->_flags |= __MPU_IO_LINE_BUF; return( 0 ); } /* End of __mpu_IO_cookie_setbuf_unlocked() */ /*************************************************************** Seek a cookie stream and reset all buffered state. ***************************************************************/ static int __mpu_IO_cookie_seek_unlocked( mpu_FILE *stream, off_t offset, int whence ) { off_t pos; if( stream->_flags & __MPU_IO_WRITING ) { if( __mpu_IO_cookie_flush_unlocked( stream ) == mpu_EOF ) return( -1 ); } else if( stream->_flags & __MPU_IO_READING ) { /* Discard buffered input: the requested offset is interpreted from the logical position by seekoff() when WHENCE is SEEK_CUR. */ stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base; } pos = __mpu_IO_cookie_call_seek( stream, offset, whence ); if( pos == (off_t)-1 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( -1 ); } stream->_unget_count = 0; stream->_flags &= ~(__MPU_IO_READING | __MPU_IO_WRITING | __MPU_IO_EOF_SEEN); stream->_read_base = stream->_buf_base; stream->_read_ptr = stream->_buf_base; stream->_read_end = stream->_buf_base; stream->_write_base = stream->_buf_base; stream->_write_ptr = stream->_buf_base; stream->_write_end = stream->_buf_end; return( 0 ); } /* End of __mpu_IO_cookie_seek_unlocked() */ /*************************************************************** Return the logical external-byte position of a cookie stream. ***************************************************************/ static off_t __mpu_IO_cookie_tell_unlocked( mpu_FILE *stream ) { off_t pos; pos = __mpu_IO_cookie_call_seek( stream, (off_t)0, MPU_SEEK_CUR ); if( pos == (off_t)-1 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( (off_t)-1 ); } if( stream->_flags & __MPU_IO_READING ) { unsigned int i; if( stream->_read_ptr != (unsigned char *)0 && stream->_read_end != (unsigned char *)0 ) pos -= (off_t)(stream->_read_end - stream->_read_ptr); for( i = 0; i < stream->_unget_count; ++i ) { uint32_t u = (uint32_t)stream->_unget_chars[i]; pos -= (off_t)(u < 0x80U ? 1 : (u < 0x800U ? 2 : 3)); } } else if( stream->_flags & __MPU_IO_WRITING ) { if( stream->_write_base != (unsigned char *)0 && stream->_write_ptr != (unsigned char *)0 ) pos += (off_t)(stream->_write_ptr - stream->_write_base); } return( pos ); } /* End of __mpu_IO_cookie_tell_unlocked() */ /*************************************************************** Seek relative to the logical cookie stream position. ***************************************************************/ static off_t __mpu_IO_cookie_seekoff_unlocked( mpu_FILE *stream, off_t offset, int whence ) { off_t base; if( whence == MPU_SEEK_CUR ) { base = __mpu_IO_cookie_tell_unlocked( stream ); if( base == (off_t)-1 ) return( (off_t)-1 ); if( (offset > 0 && base > (off_t)(INT64_MAX - offset)) || (offset < 0 && base < (off_t)(INT64_MIN - offset)) ) { errno = EOVERFLOW; stream->_flags |= __MPU_IO_ERR_SEEN; return( (off_t)-1 ); } offset = base + offset; whence = MPU_SEEK_SET; } if( __mpu_IO_cookie_seek_unlocked( stream, offset, whence ) != 0 ) return( (off_t)-1 ); return( __mpu_IO_cookie_tell_unlocked( stream ) ); } /* End of __mpu_IO_cookie_seekoff_unlocked() */ /*************************************************************** Seek a cookie stream to an absolute position. ***************************************************************/ static off_t __mpu_IO_cookie_seekpos_unlocked( mpu_FILE *stream, off_t position ) { return( __mpu_IO_cookie_seekoff_unlocked( stream, position, MPU_SEEK_SET ) ); } /* End of __mpu_IO_cookie_seekpos_unlocked() */ /*************************************************************** Invoke the user close callback and release private cookie state. ***************************************************************/ static int __mpu_IO_cookie_close_unlocked( mpu_FILE *stream ) { __mpu_IO_cookie_file *cf = __mpu_IO_cookie_data( stream ); int ret = 0; if( cf != (__mpu_IO_cookie_file *)0 && cf->io.close != (mpu_cookie_close_function_t *)0 ) ret = (*cf->io.close)( cf->user_cookie ); free( cf ); stream->_cookie = (void *)0; if( ret != 0 ) { stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_cookie_close_unlocked() */ const struct __mpu_IO_jump_t __mpu_IO_cookie_jumps = { .finish = __mpu_IO_cookie_finish_unlocked, .underflow = __mpu_IO_cookie_underflow_unlocked, .overflow = __mpu_IO_cookie_overflow_unlocked, .sync = __mpu_IO_cookie_sync_unlocked, .setbuf = __mpu_IO_cookie_setbuf_unlocked, .seekoff = __mpu_IO_cookie_seekoff_unlocked, .seekpos = __mpu_IO_cookie_seekpos_unlocked, .flush = __mpu_IO_cookie_flush_unlocked, .close = __mpu_IO_cookie_close_unlocked, .seek = __mpu_IO_cookie_seek_unlocked, .tell = __mpu_IO_cookie_tell_unlocked, .get_byte = __mpu_IO_cookie_get_byte_unlocked, .put_byte = __mpu_IO_cookie_put_byte_unlocked, .xsgetn = __mpu_IO_cookie_xsgetn_unlocked, .xsputn = __mpu_IO_cookie_xsputn_unlocked, .get_char = __mpu_UTF8_decode_unlocked, .put_char = __mpu_UTF8_encode_unlocked }; /*************************************************************** Open an arbitrary byte-oriented callback object as an MPU stream. Text functions decode/encode UTF-8 at this boundary. ***************************************************************/ mpu_FILE *mpu_fopencookie( void *cookie, const char *mode, mpu_cookie_io_functions_t io_functions ) { struct __mpu_IO_FILE_plus *plus; __mpu_IO_cookie_file *cf; mpu_FILE *stream; unsigned int flags; int oflags; if( __mpu_IO_parse_mode( mode, &flags, &oflags, 0 ) < 0 ) return( (mpu_FILE *)0 ); (void)oflags; if( (!(flags & __MPU_IO_NO_READS) && io_functions.read == (mpu_cookie_read_function_t *)0) || (!(flags & __MPU_IO_NO_WRITES) && io_functions.write == (mpu_cookie_write_function_t *)0) || ((flags & __MPU_IO_APPEND) && io_functions.seek == (mpu_cookie_seek_function_t *)0) ) { errno = EINVAL; return( (mpu_FILE *)0 ); } plus = (struct __mpu_IO_FILE_plus *)calloc( 1, sizeof(*plus) ); if( plus == (struct __mpu_IO_FILE_plus *)0 ) return( (mpu_FILE *)0 ); cf = (__mpu_IO_cookie_file *)calloc( 1, sizeof(*cf) ); if( cf == (__mpu_IO_cookie_file *)0 ) { free( plus ); return( (mpu_FILE *)0 ); } stream = &plus->file; plus->vtable = &__mpu_IO_cookie_jumps; cf->user_cookie = cookie; cf->io = io_functions; stream->_magic = __MPU_IO_MAGIC; stream->_flags = flags | __MPU_IO_COOKIE; stream->_fileno = -1; stream->_cookie = cf; { int lock_error = __mpu_IO_lock_init( &stream->_lock ); if( lock_error != 0 ) { free( cf ); free( plus ); errno = lock_error; return( (mpu_FILE *)0 ); } } if( (flags & __MPU_IO_APPEND) && io_functions.seek != (mpu_cookie_seek_function_t *)0 ) { off_t pos = (off_t)0; if( (*io_functions.seek)( cookie, &pos, MPU_SEEK_END ) != 0 ) { int saved_errno = errno; pthread_mutex_destroy( &stream->_lock ); free( cf ); free( plus ); errno = saved_errno; return( (mpu_FILE *)0 ); } } __mpu_IO_link_in( stream ); return( stream ); } /* End of mpu_fopencookie() */ /*************************************************************** Hide internal symbols: ***************************************************************/ __mpu_hidden_decl(__mpu_IO_cookie_jumps); /* End of hide internal symbols. ***************************************************************/ /* End of mpu-cookie.c */