/* PDCLib I/O support <_PDCLIB_io.h>12This file is part of the Public Domain C Library (PDCLib).3Permission is granted to use, modify, and / or redistribute at will.4*/56#ifndef __PDCLIB_IO_H7#define __PDCLIB_IO_H __PDCLIB_IO_H89#include "_PDCLIB_int.h"10#include "_PDCLIB_threadconfig.h"1112/* Flags for representing mode (see fopen()). Note these must fit the same13status field as the _IO?BF flags in <stdio.h> and the internal flags below.14*/15#define _PDCLIB_FREAD 8u16#define _PDCLIB_FWRITE 16u17#define _PDCLIB_FAPPEND 32u18#define _PDCLIB_FRW 64u19#define _PDCLIB_FBIN 128u2021/* Internal flags, made to fit the same status field as the flags above. */22/* -------------------------------------------------------------------------- */23/* free() the buffer memory on closing (false for user-supplied buffer) */24#define _PDCLIB_FREEBUFFER 512u25/* stream has encountered error / EOF */26#define _PDCLIB_ERRORFLAG 1024u27#define _PDCLIB_EOFFLAG 2048u28/* stream is wide-oriented */29#define _PDCLIB_WIDESTREAM 4096u30/* stream is byte-oriented */31#define _PDCLIB_BYTESTREAM 8192u32/* file associated with stream should be remove()d on closing (tmpfile()) */33#define _PDCLIB_DELONCLOSE 16384u34/* stream handle should not be free()d on close (stdin, stdout, stderr) */35#define _PDCLIB_STATIC 32768u3637union _PDCLIB_fd38{39#if defined(_PDCLIB_OSFD_T)40_PDCLIB_OSFD_T osfd;41#endif42void * pointer;43_PDCLIB_uintptr_t uval;44_PDCLIB_intptr_t sval;45};4647/******************************************************************************/48/* Internal functions */49/******************************************************************************/5051/* The worker for all printf() type of functions. The pointer spec should point52to the introducing '%' of a conversion specifier. The status structure is to53be that of the current printf() function, of which the members n, s, stream54and arg will be preserved; i will be updated; and all others will be trashed55by the function.56Returns the number of characters parsed as a conversion specifier (0 if none57parsed); returns -1 if the underlying I/O callback returns failure.58*/59int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status );6061/* The worker for all scanf() type of functions. The pointer spec should point62to the introducing '%' of a conversion specifier. The status structure is to63be that of the current scanf() function, of which the member stream will be64preserved; n, i, and s will be updated; and all others will be trashed by65the function.66Returns a pointer to the first character not parsed as conversion specifier,67or NULL in case of error.68FIXME: Should distinguish between matching and input error69*/70const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status );7172/* Parsing any fopen() style filemode string into a number of flags. */73unsigned int _PDCLIB_filemode( const char * mode );7475/* Sanity checking and preparing of read buffer, should be called first thing76by any stdio read-data function.77Returns 0 on success, EOF on error.78On error, EOF / error flags and errno are set appropriately.79*/80int _PDCLIB_prepread( _PDCLIB_file_t * stream );8182/* Sanity checking, should be called first thing by any stdio write-data83function.84Returns 0 on success, EOF on error.85On error, error flags and errno are set appropriately.86*/87int _PDCLIB_prepwrite( _PDCLIB_file_t * stream );8889/* Closing all streams on program exit */90void _PDCLIB_closeall( void );9192/* Writes a stream's buffer.93Returns 0 on success, EOF on write error.94Sets stream error flags and errno appropriately on error.95*/96int _PDCLIB_flushbuffer( _PDCLIB_file_t * stream );9798/* Fills a stream's buffer.99Returns 0 on success, EOF on read error / EOF.100Sets stream EOF / error flags and errno appropriately on error.101*/102int _PDCLIB_fillbuffer( _PDCLIB_file_t * stream );103104/* Repositions within a file. Returns new offset on success,105-1 / errno on error.106*/107_PDCLIB_int_fast64_t _PDCLIB_seek( _PDCLIB_file_t * stream,108_PDCLIB_int_fast64_t offset, int whence );109110/* File backend I/O operations111*112* PDCLib will call through to these methods as needed to implement the stdio113* functions.114*/115struct _PDCLIB_fileops116{117/*! Read length bytes from the file into buf; returning the number of bytes118* actually read in *numBytesRead.119*120* Returns true if bytes were read successfully; on end of file, returns121* true with *numBytesRead == 0.122*123* On error, returns false and sets errno appropriately. *numBytesRead is124* ignored in this situation.125*/126_PDCLIB_bool (*read)( _PDCLIB_fd_t self,127void * buf,128_PDCLIB_size_t length,129_PDCLIB_size_t * numBytesRead );130131/*! Write length bytes to the file from buf; returning the number of bytes132* actually written in *numBytesWritten133*134* Returns true if bytes were written successfully. On error, returns false135* and setss errno appropriately (as with read, *numBytesWritten is136* ignored)137*/138_PDCLIB_bool (*write)( _PDCLIB_fd_t self, const void * buf,139_PDCLIB_size_t length, _PDCLIB_size_t * numBytesWritten );140141/* Seek to the file offset specified by offset, from location whence, which142* may be one of the standard constants SEEK_SET/SEEK_CUR/SEEK_END143*/144_PDCLIB_bool (*seek)( _PDCLIB_fd_t self, _PDCLIB_int_fast64_t offset,145int whence, _PDCLIB_int_fast64_t *newPos );146147void (*close)( _PDCLIB_fd_t self );148149/*! Behaves as read does, except for wide characters. Both length and150* *numCharsRead represent counts of characters, not bytes.151*152* This function is optional; if missing, PDCLib will buffer the character153* data as bytes and perform translation directly into the user's buffers.154* It is useful if your backend can directly take wide characters (for155* example, the Windows console)156*/157_PDCLIB_bool (*wread)( _PDCLIB_fd_t self, _PDCLIB_wchar_t * buf,158_PDCLIB_size_t length, _PDCLIB_size_t * numCharsRead );159160/* Behaves as write does, except for wide characters. As with wread, both161* length and *numCharsWritten are character counts.162*163* This function is also optional; if missing, PDCLib will buffer the164* character data as bytes and do translation directly from the user's165* buffers. You only need to implement this if your backend can directly166* take wide characters (for example, the Windows console)167*/168_PDCLIB_bool (*wwrite)( _PDCLIB_fd_t self, const _PDCLIB_wchar_t * buf,169_PDCLIB_size_t length, _PDCLIB_size_t * numCharsWritten );170};171172/* struct _PDCLIB_file structure */173struct _PDCLIB_file174{175const _PDCLIB_fileops_t * ops;176_PDCLIB_fd_t handle; /* OS file handle */177_PDCLIB_MTX_T lock; /* file lock */178char * buffer; /* Pointer to buffer memory */179_PDCLIB_size_t bufsize; /* Size of buffer */180_PDCLIB_size_t bufidx; /* Index of current position in buffer */181_PDCLIB_size_t bufend; /* Index of last pre-read character in buffer */182#ifdef _PDCLIB_NEED_EOL_TRANSLATION183_PDCLIB_size_t bufnlexp; /* Current position of buffer newline expansion */184#endif185_PDCLIB_size_t ungetidx; /* Number of ungetc()'ed characters */186unsigned char * ungetbuf; /* ungetc() buffer */187unsigned int status; /* Status flags; see above */188/* multibyte parsing status to be added later */189_PDCLIB_fpos_t pos; /* Offset and multibyte parsing state */190char * filename; /* Name the current stream has been opened with */191_PDCLIB_file_t * next; /* Pointer to next struct (internal) */192};193194static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n,195int stopchar,196_PDCLIB_file_t * stream )197{198_PDCLIB_size_t i = 0;199int c;200while ( stream->ungetidx > 0 && i != n )201{202c = (unsigned char)203( out[ i++ ] = stream->ungetbuf[ --(stream->ungetidx) ] );204if( c == stopchar )205return i;206}207208while ( i != n )209{210while ( stream->bufidx != stream->bufend && i != n)211{212c = (unsigned char) stream->buffer[ stream->bufidx++ ];213#ifdef _PDCLIB_NEED_EOL_TRANSLATION214if ( !( stream->status & _PDCLIB_FBIN ) && c == '\r' )215{216if ( stream->bufidx == stream->bufend )217break;218219if ( stream->buffer[ stream->bufidx ] == '\n' )220{221c = '\n';222stream->bufidx++;223}224}225#endif226out[ i++ ] = c;227228if( c == stopchar )229return i;230}231232if ( i != n )233{234if( _PDCLIB_fillbuffer( stream ) == -1 )235{236break;237}238}239}240241#ifdef _PDCLIB_NEED_EOL_TRANSLATION242if ( i != n && stream->bufidx != stream->bufend )243{244// we must have EOF'd immediately after a \r245out[ i++ ] = stream->buffer[ stream->bufidx++ ];246}247#endif248249return i;250}251252/* Unlocked functions - internal names253*254* We can't use the functions using their "normal" names internally because that255* would cause namespace leakage. Therefore, we use them by prefixed internal256* names257*/258void _PDCLIB_flockfile(struct _PDCLIB_file *file) _PDCLIB_nothrow;259int _PDCLIB_ftrylockfile(struct _PDCLIB_file *file) _PDCLIB_nothrow;260void _PDCLIB_funlockfile(struct _PDCLIB_file *file) _PDCLIB_nothrow;261262int _PDCLIB_getc_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;263int _PDCLIB_getchar_unlocked(void) _PDCLIB_nothrow;264int _PDCLIB_putc_unlocked(int c, struct _PDCLIB_file *stream) _PDCLIB_nothrow;265int _PDCLIB_putchar_unlocked(int c) _PDCLIB_nothrow;266void _PDCLIB_clearerr_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;267int _PDCLIB_feof_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;268int _PDCLIB_ferror_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;269int _PDCLIB_fflush_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;270int _PDCLIB_fgetc_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;271int _PDCLIB_fputc_unlocked(int c, struct _PDCLIB_file *stream) _PDCLIB_nothrow;272_PDCLIB_size_t _PDCLIB_fread_unlocked(void *ptr, _PDCLIB_size_t size, _PDCLIB_size_t n, struct _PDCLIB_file *stream) _PDCLIB_nothrow;273_PDCLIB_size_t _PDCLIB_fwrite_unlocked(const void *ptr, _PDCLIB_size_t size, _PDCLIB_size_t n, struct _PDCLIB_file *stream) _PDCLIB_nothrow;274char *_PDCLIB_fgets_unlocked(char *s, int n, struct _PDCLIB_file *stream) _PDCLIB_nothrow;275int _PDCLIB_fputs_unlocked(const char *s, struct _PDCLIB_file *stream) _PDCLIB_nothrow;276int _PDCLIB_fgetpos_unlocked( struct _PDCLIB_file * _PDCLIB_restrict stream, _PDCLIB_fpos_t * _PDCLIB_restrict pos ) _PDCLIB_nothrow;277int _PDCLIB_fsetpos_unlocked( struct _PDCLIB_file * stream, const _PDCLIB_fpos_t * pos ) _PDCLIB_nothrow;278long int _PDCLIB_ftell_unlocked( struct _PDCLIB_file * stream ) _PDCLIB_nothrow;279int _PDCLIB_fseek_unlocked( struct _PDCLIB_file * stream, long int offset, int whence ) _PDCLIB_nothrow;280void _PDCLIB_rewind_unlocked( struct _PDCLIB_file * stream ) _PDCLIB_nothrow;281282int _PDCLIB_puts_unlocked( const char * s ) _PDCLIB_nothrow;283int _PDCLIB_ungetc_unlocked( int c, struct _PDCLIB_file * stream ) _PDCLIB_nothrow;284285286int _PDCLIB_printf_unlocked( const char * _PDCLIB_restrict format, ... ) _PDCLIB_nothrow;287int _PDCLIB_vprintf_unlocked( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg ) _PDCLIB_nothrow;288int _PDCLIB_fprintf_unlocked( struct _PDCLIB_file * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... ) _PDCLIB_nothrow;289int _PDCLIB_vfprintf_unlocked( struct _PDCLIB_file * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg ) _PDCLIB_nothrow;290int _PDCLIB_scanf_unlocked( const char * _PDCLIB_restrict format, ... ) _PDCLIB_nothrow;291int _PDCLIB_vscanf_unlocked( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg ) _PDCLIB_nothrow;292int _PDCLIB_fscanf_unlocked( struct _PDCLIB_file * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, ... ) _PDCLIB_nothrow;293int _PDCLIB_vfscanf_unlocked( struct _PDCLIB_file * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, _PDCLIB_va_list arg ) _PDCLIB_nothrow;294295#endif296297298