From 3309ec3ad8a5db735eaa2de7f5dc6a331d8e7319 Mon Sep 17 00:00:00 2001 From: Owen Shepherd Date: Tue, 7 Oct 2014 23:19:34 +0100 Subject: [PATCH] Add _cbprintf/_vcbprintf (callback based printf formatters) --- functions/stdio/_PDCLIB_print.c | 258 ++++++++++++++++++++------------ functions/stdio/_PDCLIB_scan.c | 4 +- functions/stdio/_cbprintf.c | 56 +++++++ functions/stdio/_vcbprintf.c | 126 ++++++++++++++++ functions/stdio/vfprintf.c | 44 ++---- functions/stdio/vscanf.c | 2 +- functions/stdio/vsnprintf.c | 66 ++++---- includes/stdio.h | 30 ++-- internals/_PDCLIB_int.h | 23 ++- internals/_PDCLIB_io.h | 47 +++--- 10 files changed, 443 insertions(+), 213 deletions(-) create mode 100644 functions/stdio/_cbprintf.c create mode 100644 functions/stdio/_vcbprintf.c diff --git a/functions/stdio/_PDCLIB_print.c b/functions/stdio/_PDCLIB_print.c index 21177af..0362394 100644 --- a/functions/stdio/_PDCLIB_print.c +++ b/functions/stdio/_PDCLIB_print.c @@ -48,47 +48,85 @@ #define E_TYPES (E_char | E_short | E_long | E_llong | E_intmax \ | E_size | E_ptrdiff | E_intptr) -/* This macro delivers a given character to either a memory buffer or a stream, - depending on the contents of 'status' (struct _PDCLIB_status_t). - x - the character to be delivered - i - pointer to number of characters already delivered in this call - n - pointer to maximum number of characters to be delivered in this call - s - the buffer into which the character shall be delivered -*/ -#define PUT( x ) \ -do { \ - int character = x; \ - if ( status->i < status->n ) { \ - if ( status->stream != NULL ) \ - _PDCLIB_putc_unlocked( character, status->stream ); \ - else \ - status->s[status->i] = character; \ - } \ - ++(status->i); \ -} while ( 0 ) - -/* Maximum number of output characters = - * number of bits in (u)intmax_t / number of bits per character in smallest +/* returns true if callback-based output succeeded; else false */ +static inline bool cbout( + struct _PDCLIB_status_t * status, + const void * buf, + size_t size ) +{ + size_t rv = status->write( status->ctx, buf, size ); + status->i += rv; + status->current += rv; + return rv == size; +} + +/* repeated output of a single character */ +static inline bool cbrept( + struct _PDCLIB_status_t * status, + char c, + size_t times ) +{ + if ( sizeof(size_t) == 8 && CHAR_BIT == 8) + { + uint64_t spread = UINT64_C(0x0101010101010101) * c; + while ( times ) + { + size_t n = times > 8 ? 8 : times; + if ( !cbout( status, &spread, n ) ) + return false; + times -= n; + } + return true; + } + else if ( sizeof(size_t) == 4 && CHAR_BIT == 8) + { + uint32_t spread = UINT32_C(0x01010101) * c; + while ( times ) + { + size_t n = times > 4 ? 4 : times; + if ( !cbout( status, &spread, n ) ) + return false; + times -= n; + } + return true; + } + else + { + while ( times ) + { + if ( !cbout( status, &c, 1) ) + return false; + times--; + } + return true; + } +} + + +/* Maximum number of output characters = + * number of bits in (u)intmax_t / number of bits per character in smallest * base. Smallest base is octal, 3 bits/char. * * Additionally require 2 extra characters for prefixes + * + * Returns false if an I/O error occured. */ static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1; -static void int2base( uintmax_t value, struct _PDCLIB_status_t * status ) +static bool int2base( uintmax_t value, struct _PDCLIB_status_t * status ) { char sign = 0; - if ( ! ( status->flags & E_unsigned ) ) + if ( ! ( status->flags & E_unsigned ) ) { intmax_t signval = (intmax_t) value; bool negative = signval < 0; value = signval < 0 ? -signval : signval; - if ( negative ) + if ( negative ) { sign = '-'; - } - else if ( status->flags & E_plus ) + } + else if ( status->flags & E_plus ) { sign = '+'; } @@ -98,7 +136,7 @@ static void int2base( uintmax_t value, struct _PDCLIB_status_t * status ) } } - // The user could theoretically ask for a silly buffer length here. + // The user could theoretically ask for a silly buffer length here. // Perhaps after a certain size we should malloc? Or do we refuse to protect // them from their own stupidity? size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2; @@ -108,7 +146,7 @@ static void int2base( uintmax_t value, struct _PDCLIB_status_t * status ) // Build up our output string - backwards { - const char * digits = (status->flags & E_lower) ? + const char * digits = (status->flags & E_lower) ? _PDCLIB_digits : _PDCLIB_Xdigits; uintmax_t remaining = value; if(status->prec != 0 || remaining != 0) do { @@ -125,9 +163,9 @@ static void int2base( uintmax_t value, struct _PDCLIB_status_t * status ) // If a field width specified, and zero padding was requested, then pad to // the field width unsigned padding = 0; - if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) + if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) { - while( written < (int) status->width ) + while( written < (int) status->width ) { outend[-++written] = '0'; padding++; @@ -167,77 +205,88 @@ static void int2base( uintmax_t value, struct _PDCLIB_status_t * status ) } // Write output - status->current = written; - while ( written ) - PUT( outend[-written--] ); + return cbout( status, outend - written, written ); } -static void printstr( const char * str, struct _PDCLIB_status_t * status ) +/* print a string. returns false if an I/O error occured */ +static bool printstr( const char * str, struct _PDCLIB_status_t * status ) { + size_t len = status->prec >= 0 ? strnlen( str, status-> prec) + : strlen(str); + if ( status->width == 0 || status->flags & E_minus ) { // Simple case or left justification - while ( str[status->current] && - ( status->prec < 0 || (long)status->current < status->prec ) ) + if ( status->prec > 0 ) { - PUT( str[status->current++] ); + len = (unsigned) status->prec < len ? (unsigned) status->prec : len; } - while( status->current < status->width ) - { - PUT( ' ' ); - status->current++; + if ( !cbout( status, str, len ) ) + return false; + + /* right padding */ + if ( status->width > status->current ) { + len = status->width - status->current; + + if ( !cbrept( status, ' ', len ) ) + return false; } } else { // Right justification - size_t len = status->prec >= 0 ? strnlen( str, status->prec ) - : strlen( str ); - int padding = status->width - len; - while((long)status->current < padding) - { - PUT( ' ' ); - status->current++; - } - for( size_t i = 0; i != len; i++ ) - { - PUT( str[i] ); - status->current++; + if ( status->width > len ) { + size_t padding = status->width - len; + + if ( !cbrept( status, ' ', padding )) + return false; } + + if ( !cbout( status, str, len ) ) + return false; } + + return true; } -static void printchar( char chr, struct _PDCLIB_status_t * status ) +static bool printchar( char chr, struct _PDCLIB_status_t * status ) { if( ! ( status->flags & E_minus ) ) { // Right justification - for( ; status->current + 1 < status->width; status->current++) - { - PUT( ' ' ); + if ( status-> width ) { + size_t justification = status->width - status->current - 1; + if ( !cbrept( status, ' ', justification )) + return false; } - PUT( chr ); - status->current++; + + if ( !cbout( status, &chr, 1 )) + return false; } else { // Left justification - PUT( chr ); - status->current++; - for( ; status->current < status->width; status->current++) - { - PUT( ' ' ); + if ( !cbout( status, &chr, 1 )) + return false; + + if ( status->width > status->current ) { + if ( !cbrept( status, ' ', status->width - status->current ) ) + return false; } } + + return true; } -const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ) +int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ) { const char * orig_spec = spec; if ( *(++spec) == '%' ) { /* %% -> print single '%' */ - PUT( *spec ); - return ++spec; + if ( !cbout(status, spec, 1) ) + return -1; + ++spec; + return (spec - orig_spec); } /* Initializing status structure */ status->flags = 0; @@ -419,14 +468,18 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status break; case 'c': /* TODO: wide chars. */ - printchar( va_arg( status->arg, int ), status ); - return ++spec; + if ( !printchar( va_arg( status->arg, int ), status ) ) + return -1; + ++spec; + return (spec - orig_spec); case 's': /* TODO: wide chars. */ { char * s = va_arg( status->arg, char * ); - printstr( s, status ); - return ++spec; + if ( !printstr( s, status ) ) + return -1; + ++spec; + return (spec - orig_spec); } case 'p': status->base = 16; @@ -436,11 +489,12 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status { int * val = va_arg( status->arg, int * ); *val = status->i; - return ++spec; + ++spec; + return (spec - orig_spec); } default: /* No conversion specifier. Bad conversion. */ - return orig_spec; + return 0; } /* Do the actual output based on our findings */ if ( status->base != 0 ) @@ -479,55 +533,55 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status case E_intmax: value = va_arg( status->arg, uintmax_t ); } - int2base( value, status ); + if ( !int2base( value, status ) ) + return -1; } else { + intmax_t value; switch ( status->flags & E_TYPES ) { case E_char: - int2base( (intmax_t)(char)va_arg( status->arg, int ), status ); + value = (intmax_t)(char)va_arg( status->arg, int ); break; case E_short: - int2base( (intmax_t)(short)va_arg( status->arg, int ), status ); + value = (intmax_t)(short)va_arg( status->arg, int ); break; case 0: - int2base( (intmax_t)va_arg( status->arg, int ), status ); + value = (intmax_t)va_arg( status->arg, int ); break; case E_long: - int2base( (intmax_t)va_arg( status->arg, long ), status ); + value = (intmax_t)va_arg( status->arg, long ); break; case E_llong: - int2base( (intmax_t)va_arg( status->arg, long long ), status ); + value = (intmax_t)va_arg( status->arg, long long ); break; case E_size: - int2base( (intmax_t)va_arg( status->arg, size_t ), status ); + value = (intmax_t)va_arg( status->arg, size_t ); break; case E_intptr: - int2base( (intmax_t)va_arg( status->arg, intptr_t ), status ); + value = (intmax_t)va_arg( status->arg, intptr_t ); break; case E_ptrdiff: - int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status ); + value = (intmax_t)va_arg( status->arg, ptrdiff_t ); break; case E_intmax: - int2base( va_arg( status->arg, intmax_t ), status ); + value = va_arg( status->arg, intmax_t ); break; } + + if (!int2base( value, status ) ) + return -1; } - if ( status->flags & E_minus ) - { - while ( status->current < status->width ) - { - PUT( ' ' ); - ++(status->current); - } - } - if ( status->i >= status->n && status->n > 0 ) + + if ( status->flags & E_minus && status->current < status->width ) { - status->s[status->n - 1] = '\0'; + if (!cbrept( status, ' ', status->width - status->current )) + return -1; } } - return ++spec; + ++spec; + return spec - orig_spec; } #endif @@ -539,22 +593,30 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status #include <_PDCLIB_test.h> #ifndef REGTEST +static size_t testcb( void *p, const char *buf, size_t size ) +{ + char **destbuf = p; + memcpy(*destbuf, buf, size); + *destbuf += size; + return size; +} + static int testprintf( char * buffer, const char * format, ... ) { - /* Members: base, flags, n, i, current, s, width, prec, stream, arg */ + /* Members: base, flags, n, i, current, width, prec, ctx, cb, arg */ struct _PDCLIB_status_t status; status.base = 0; status.flags = 0; status.n = 100; status.i = 0; status.current = 0; - status.s = buffer; status.width = 0; status.prec = 0; - status.stream = NULL; + status.ctx = &buffer; + status.write = testcb; va_start( status.arg, format ); memset( buffer, '\0', 100 ); - if ( *(_PDCLIB_print( format, &status )) != '\0' ) + if ( _PDCLIB_print( format, &status ) != strlen( format ) ) { printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format ); ++TEST_RESULTS; diff --git a/functions/stdio/_PDCLIB_scan.c b/functions/stdio/_PDCLIB_scan.c index 615d82e..6a31ff0 100644 --- a/functions/stdio/_PDCLIB_scan.c +++ b/functions/stdio/_PDCLIB_scan.c @@ -287,7 +287,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) case 's': { char * c = va_arg( status->arg, char * ); - while ( ( status->current < status->width ) && + while ( ( status->current < status->width ) && ( ( rc = GET( status ) ) != EOF ) ) { if ( isspace( rc ) ) @@ -347,7 +347,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) } while ( *endspec != ']' ); // read according to scanlist, equiv. to %s above char * c = va_arg( status->arg, char * ); - while ( ( status->current < status->width ) && + while ( ( status->current < status->width ) && ( ( rc = GET( status ) ) != EOF ) ) { if ( negative_scanlist ) diff --git a/functions/stdio/_cbprintf.c b/functions/stdio/_cbprintf.c new file mode 100644 index 0000000..c0192fe --- /dev/null +++ b/functions/stdio/_cbprintf.c @@ -0,0 +1,56 @@ +/* _cbprintf( void *, size_t (*)( void*, const char *, size_t ), const char *, ... ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +#ifndef REGTEST + +int _cbprintf( + void * p, + size_t (*cb)( void*, const char*, size_t ), + const char * _PDCLIB_restrict format, + ...) +{ + int rc; + va_list ap; + va_start( ap, format ); + rc = _vcbprintf( p, cb, format, ap ); + va_end( ap ); + return rc; +} + +#endif + +#ifdef TEST +#define _PDCLIB_FILEID "stdio/sprintf.c" +#define _PDCLIB_STRINGIO +#include + +#include <_PDCLIB_test.h> + +static char * bufptr; +static size_t testcb( void *p, const char *buf, size_t size ) +{ + memcpy(bufptr, buf, size); + bufptr += size; + *bufptr = '\0'; + return size; +} + +#define testprintf( s, ... ) _cbprintf( bufptr = s, testcb, __VA_ARGS__ ) + +int main( void ) +{ + char target[100]; +#ifndef REGTEST +#include "printf_testcases.h" +#endif + return TEST_RESULTS; +} + +#endif diff --git a/functions/stdio/_vcbprintf.c b/functions/stdio/_vcbprintf.c new file mode 100644 index 0000000..dbb2171 --- /dev/null +++ b/functions/stdio/_vcbprintf.c @@ -0,0 +1,126 @@ +/* $Id$ */ + +/* vsnprintf( char *, size_t, const char *, va_list ap ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include +#include + +#ifndef REGTEST +#include <_PDCLIB_io.h> + +/* returns true if callback-based output succeeded; else false */ +static inline bool cbout( + struct _PDCLIB_status_t * status, + const char *buf, + size_t size ) +{ + size_t rv = status->write( status->ctx, buf, size ); + status->i += rv; + return rv == size; +} + +int _vcbprintf( + void *p, + size_t ( *cb ) ( void *p, const char *buf, size_t size ), + const char *format, + va_list arg ) +{ + struct _PDCLIB_status_t status; + status.base = 0; + status.flags = 0; + status.n = 0; + status.i = 0; + status.current = 0; + status.width = 0; + status.prec = 0; + status.ctx = p; + status.write = cb; + va_copy( status.arg, arg ); + + /* Alternate between outputing runs of verbatim text and conversions */ + while ( *format != '\0' ) + { + const char *mark = format; + while ( *format != '\0' && *format != '%') + { + format++; + } + + if ( mark != format ) + { + if ( !cbout(&status, mark, format - mark) ) + return -1; + } + + if ( *format == '%' ) { + int consumed = _PDCLIB_print( format, &status ); + if ( consumed > 0 ) + { + format += consumed; + } + else if ( consumed == 0 ) + { + /* not a conversion specifier, print verbatim */ + if ( !cbout(&status, format++, 1) ) + return -1; + } + else + { + /* I/O callback error */ + return -1; + } + } + } + + va_end( status.arg ); + return status.i; +} + +#endif + +#ifdef TEST +#define _PDCLIB_FILEID "stdio/_vcbprintf.c" +#define _PDCLIB_STRINGIO +#include +#include +#include <_PDCLIB_test.h> + +#ifndef REGTEST + +static size_t testcb( void *p, const char *buf, size_t size ) +{ + char **destbuf = p; + memcpy(*destbuf, buf, size); + *destbuf += size; + return size; +} + +static int testprintf( char * s, const char * format, ... ) +{ + int i; + va_list arg; + va_start( arg, format ); + i = _vcbprintf( &s, testcb, format, arg ); + *s = 0; + va_end( arg ); + return i; +} + +#endif + +int main( void ) +{ + char target[100]; +#ifndef REGTEST +#include "printf_testcases.h" +#endif + return TEST_RESULTS; +} + +#endif + diff --git a/functions/stdio/vfprintf.c b/functions/stdio/vfprintf.c index 7dd7f2d..0aa6648 100644 --- a/functions/stdio/vfprintf.c +++ b/functions/stdio/vfprintf.c @@ -14,44 +14,20 @@ #ifndef REGTEST #include <_PDCLIB_io.h> -int _PDCLIB_vfprintf_unlocked( FILE * _PDCLIB_restrict stream, - const char * _PDCLIB_restrict format, - va_list arg ) +static size_t filecb(void *p, const char *buf, size_t size) { - /* TODO: This function should interpret format as multibyte characters. */ - struct _PDCLIB_status_t status; - status.base = 0; - status.flags = 0; - status.n = UINT_MAX; - status.i = 0; - status.current = 0; - status.s = NULL; - status.width = 0; - status.prec = 0; - status.stream = stream; - va_copy( status.arg, arg ); + return _PDCLIB_fwrite_unlocked( buf, 1, size, (FILE*) p ); +} - while ( *format != '\0' ) - { - const char * rc; - if ( ( *format != '%' ) || ( ( rc = _PDCLIB_print( format, &status ) ) == format ) ) - { - /* No conversion specifier, print verbatim */ - _PDCLIB_putc_unlocked( *(format++), stream ); - status.i++; - } - else - { - /* Continue parsing after conversion specifier */ - format = rc; - } - } - va_end( status.arg ); - return status.i; +int _PDCLIB_vfprintf_unlocked( FILE * _PDCLIB_restrict stream, + const char * _PDCLIB_restrict format, + va_list arg ) +{ + return _vcbprintf(stream, filecb, format, arg); } -int vfprintf( FILE * _PDCLIB_restrict stream, - const char * _PDCLIB_restrict format, +int vfprintf( FILE * _PDCLIB_restrict stream, + const char * _PDCLIB_restrict format, va_list arg ) { _PDCLIB_flockfile( stream ); diff --git a/functions/stdio/vscanf.c b/functions/stdio/vscanf.c index 3e4ada4..148c828 100644 --- a/functions/stdio/vscanf.c +++ b/functions/stdio/vscanf.c @@ -12,7 +12,7 @@ #ifndef REGTEST #include <_PDCLIB_io.h> -int _PDCLIB_vscanf_unlocked( const char * _PDCLIB_restrict format, +int _PDCLIB_vscanf_unlocked( const char * _PDCLIB_restrict format, _PDCLIB_va_list arg ) { return _PDCLIB_vfscanf_unlocked( stdin, format, arg ); diff --git a/functions/stdio/vsnprintf.c b/functions/stdio/vsnprintf.c index cda7ab6..6cc233d 100644 --- a/functions/stdio/vsnprintf.c +++ b/functions/stdio/vsnprintf.c @@ -11,50 +11,38 @@ #ifndef REGTEST #include <_PDCLIB_io.h> +#include -int vsnprintf( char * _PDCLIB_restrict s, - size_t n, - const char * _PDCLIB_restrict format, - _PDCLIB_va_list arg ) +struct state { + size_t bufrem; + char *bufp; +}; + +static size_t strout( void *p, const char *buf, size_t sz ) { - /* TODO: This function should interpret format as multibyte characters. */ - struct _PDCLIB_status_t status; - status.base = 0; - status.flags = 0; - status.n = n; - status.i = 0; - status.current = 0; - status.s = s; - status.width = 0; - status.prec = 0; - status.stream = NULL; - va_copy( status.arg, arg ); + struct state *s = p; + size_t copy = s->bufrem >= sz ? sz : s->bufrem; + memcpy( s->bufp, buf, copy ); + s->bufrem -= copy; + s->bufp += copy; + return sz; +} - while ( *format != '\0' ) - { - const char * rc; - if ( ( *format != '%' ) || ( ( rc = _PDCLIB_print( format, &status ) ) == format ) ) - { - /* No conversion specifier, print verbatim */ - if ( status.i < n ) - { - s[ status.i ] = *format; - } - status.i++; - format++; - } - else - { - /* Continue parsing after conversion specifier */ - format = rc; - } - } - if ( status.i < n ) +int vsnprintf( char * _PDCLIB_restrict s, + size_t n, + const char * _PDCLIB_restrict format, + _PDCLIB_va_list arg ) +{ + struct state st; + st.bufrem = n; + st.bufp = s; + int r = _vcbprintf( &st, strout, format, arg ); + if ( st.bufrem ) { - s[ status.i ] = '\0'; + *st.bufp = 0; } - va_end( status.arg ); - return status.i; + + return r; } #endif diff --git a/includes/stdio.h b/includes/stdio.h index b10891f..e6e178e 100644 --- a/includes/stdio.h +++ b/includes/stdio.h @@ -36,7 +36,7 @@ typedef _PDCLIB_file_t FILE; #define L_tmpnam _PDCLIB_L_tmpnam #define TMP_MAX _PDCLIB_TMP_MAX -/* See fseek(), third argument +/* See fseek(), third argument * * Some system headers (e.g. windows) also define the SEEK_* values. Check for * this and validate that they're the same value @@ -74,7 +74,7 @@ extern FILE * stderr; int remove( const char * filename ) _PDCLIB_nothrow; /* Rename the given old file to the given new name. - Returns zero if successful, non-zero otherwise. + Returns zero if successful, non-zero otherwise. This implementation does detect if the old filename corresponds to an open file, and fails the rename in this case. If there already is a file with the new filename, behaviour is defined by @@ -171,13 +171,13 @@ int fflush( FILE * stream ) _PDCLIB_nothrow; Returns a pointer to the stream handle if successfull, NULL otherwise. */ -FILE * fopen( const char * _PDCLIB_restrict filename, +FILE * fopen( const char * _PDCLIB_restrict filename, const char * _PDCLIB_restrict mode ) _PDCLIB_nothrow; /* Creates a stream connected to the file descriptor \p fd with mode \p mode. Mode must match the mode with which the file descriptor was opened. */ -FILE * _PDCLIB_fvopen( _PDCLIB_fd_t fd, const _PDCLIB_fileops_t * ops, +FILE * _PDCLIB_fvopen( _PDCLIB_fd_t fd, const _PDCLIB_fileops_t * ops, int mode, const char * filename ) _PDCLIB_nothrow; /* Close any file currently associated with the given stream. Open the file @@ -691,7 +691,7 @@ int getchar( void ) _PDCLIB_nothrow; and NULL is returned. If a read error occurs, the contents of s are indeter- minate, and NULL is returned. - This function is dangerous and has been a great source of security + This function is dangerous and has been a great source of security vulnerabilities. Do not use it. It was removed by C11. */ char * gets( char * s ) _PDCLIB_DEPRECATED _PDCLIB_nothrow; @@ -823,7 +823,7 @@ int ferror( FILE * stream ) _PDCLIB_nothrow; */ void perror( const char * s ) _PDCLIB_nothrow; -/* Unlocked I/O +/* Unlocked I/O * * Since threading was introduced in C11, FILE objects have had implicit locks * to prevent data races and inconsistent output. @@ -835,7 +835,7 @@ void perror( const char * s ) _PDCLIB_nothrow; * the behaviour of the _unlocked variant is the same except that it will not * take the lock associated with the stream. * - * flockfile, ftrylockfile and funlockfile can be used to manually manipulate + * flockfile, ftrylockfile and funlockfile can be used to manually manipulate * the stream locks. The behaviour of the _unlocked functions if called when the * stream isn't locked by the calling thread is implementation defined. */ @@ -851,7 +851,7 @@ int putchar_unlocked(int c) _PDCLIB_nothrow; #endif #if _PDCLIB_BSD_SOURCE || _PDCLIB_SVID_SOURCE -void clearerr_unlocked(FILE *stream) _PDCLIB_nothrow; +void clearerr_unlocked(FILE *stream) _PDCLIB_nothrow; int feof_unlocked(FILE *stream) _PDCLIB_nothrow; int ferror_unlocked(FILE *stream) _PDCLIB_nothrow; int fflush_unlocked(FILE *stream) _PDCLIB_nothrow; @@ -866,7 +866,19 @@ char *fgets_unlocked(char *s, int n, FILE *stream) _PDCLIB_nothrow; int fputs_unlocked(const char *s, FILE *stream) _PDCLIB_nothrow; #endif -#if defined(_PDCLIB_EXTENSIONS) +#if _PDCLIB_EXTENSIONS +int _vcbprintf( + void *p, + _PDCLIB_size_t ( *cb ) ( void *p, const char *buf, _PDCLIB_size_t size ), + const char *format, + _PDCLIB_va_list arg ); + +int _cbprintf( + void *p, + size_t ( *cb ) ( void *p, const char *buf, size_t size ), + const char *format, + ... ); + int fgetpos_unlocked( FILE * _PDCLIB_restrict stream, fpos_t * _PDCLIB_restrict pos ) _PDCLIB_nothrow; int fsetpos_unlocked( FILE * stream, const fpos_t * pos ) _PDCLIB_nothrow; long int ftell_unlocked( FILE * stream ) _PDCLIB_nothrow; diff --git a/internals/_PDCLIB_int.h b/internals/_PDCLIB_int.h index 134123a..c200419 100644 --- a/internals/_PDCLIB_int.h +++ b/internals/_PDCLIB_int.h @@ -338,13 +338,13 @@ typedef struct _PDCLIB_mbstate { */ _PDCLIB_uint16_t _Surrogate; - /* In cases where the underlying codec is capable of regurgitating a + /* In cases where the underlying codec is capable of regurgitating a * character without consuming any extra input (e.g. a surrogate pair in a - * UCS-4 to UTF-16 conversion) then these fields are used to track that + * UCS-4 to UTF-16 conversion) then these fields are used to track that * state. In particular, they are used to buffer/fake the input for mbrtowc * and similar functions. * - * See _PDCLIB_encoding.h for values of _PendState and the resultant value + * See _PDCLIB_encoding.h for values of _PendState and the resultant value * in _PendChar. */ unsigned char _PendState; @@ -375,17 +375,26 @@ typedef struct _PDCLIB_file _PDCLIB_file_t; // Rename to _PDCLIB_FILE? /* Status structure required by _PDCLIB_print(). */ struct _PDCLIB_status_t { + /* XXX This structure is horrible now. scanf needs its own */ + int base; /* base to which the value shall be converted */ _PDCLIB_int_fast32_t flags; /* flags and length modifiers */ - unsigned n; /* print: maximum characters to be written */ + unsigned n; /* print: maximum characters to be written (snprintf) */ /* scan: number matched conversion specifiers */ unsigned i; /* number of characters read/written */ unsigned current;/* chars read/written in the CURRENT conversion */ - char * s; /* *sprintf(): target buffer */ - /* *sscanf(): source string */ unsigned width; /* specified field width */ int prec; /* specified field precision */ - _PDCLIB_file_t * stream; /* *fprintf() / *fscanf() stream */ + + union { + void * ctx; /* context for callback */ + const char * s; /* input string for scanf */ + }; + + union { + _PDCLIB_size_t ( *write ) ( void *p, const char *buf, _PDCLIB_size_t size ); + _PDCLIB_file_t *stream; /* for scanf */ + }; _PDCLIB_va_list arg; /* argument stack */ }; diff --git a/internals/_PDCLIB_io.h b/internals/_PDCLIB_io.h index 65b6589..78e747c 100644 --- a/internals/_PDCLIB_io.h +++ b/internals/_PDCLIB_io.h @@ -53,9 +53,10 @@ union _PDCLIB_fd be that of the current printf() function, of which the members n, s, stream and arg will be preserved; i will be updated; and all others will be trashed by the function. - Returns a pointer to the first character not parsed as conversion specifier. + Returns the number of characters parsed as a conversion specifier (0 if none + parsed); returns -1 if the underlying I/O callback returns failure. */ -const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ); +int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ); /* The worker for all scanf() type of functions. The pointer spec should point to the introducing '%' of a conversion specifier. The status structure is to @@ -71,7 +72,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) /* Parsing any fopen() style filemode string into a number of flags. */ unsigned int _PDCLIB_filemode( const char * mode ); -/* Sanity checking and preparing of read buffer, should be called first thing +/* Sanity checking and preparing of read buffer, should be called first thing by any stdio read-data function. Returns 0 on success, EOF on error. On error, EOF / error flags and errno are set appropriately. @@ -103,7 +104,7 @@ int _PDCLIB_fillbuffer( _PDCLIB_file_t * stream ); /* Repositions within a file. Returns new offset on success, -1 / errno on error. */ -_PDCLIB_int_fast64_t _PDCLIB_seek( _PDCLIB_file_t * stream, +_PDCLIB_int_fast64_t _PDCLIB_seek( _PDCLIB_file_t * stream, _PDCLIB_int_fast64_t offset, int whence ); /* File backend I/O operations @@ -122,49 +123,49 @@ struct _PDCLIB_fileops * On error, returns false and sets errno appropriately. *numBytesRead is * ignored in this situation. */ - _PDCLIB_bool (*read)( _PDCLIB_fd_t self, - void * buf, - _PDCLIB_size_t length, + _PDCLIB_bool (*read)( _PDCLIB_fd_t self, + void * buf, + _PDCLIB_size_t length, _PDCLIB_size_t * numBytesRead ); /*! Write length bytes to the file from buf; returning the number of bytes * actually written in *numBytesWritten * * Returns true if bytes were written successfully. On error, returns false - * and setss errno appropriately (as with read, *numBytesWritten is + * and setss errno appropriately (as with read, *numBytesWritten is * ignored) */ - _PDCLIB_bool (*write)( _PDCLIB_fd_t self, const void * buf, + _PDCLIB_bool (*write)( _PDCLIB_fd_t self, const void * buf, _PDCLIB_size_t length, _PDCLIB_size_t * numBytesWritten ); /* Seek to the file offset specified by offset, from location whence, which * may be one of the standard constants SEEK_SET/SEEK_CUR/SEEK_END */ - _PDCLIB_bool (*seek)( _PDCLIB_fd_t self, _PDCLIB_int_fast64_t offset, + _PDCLIB_bool (*seek)( _PDCLIB_fd_t self, _PDCLIB_int_fast64_t offset, int whence, _PDCLIB_int_fast64_t *newPos ); void (*close)( _PDCLIB_fd_t self ); - /*! Behaves as read does, except for wide characters. Both length and + /*! Behaves as read does, except for wide characters. Both length and * *numCharsRead represent counts of characters, not bytes. * * This function is optional; if missing, PDCLib will buffer the character * data as bytes and perform translation directly into the user's buffers. - * It is useful if your backend can directly take wide characters (for + * It is useful if your backend can directly take wide characters (for * example, the Windows console) */ - _PDCLIB_bool (*wread)( _PDCLIB_fd_t self, _PDCLIB_wchar_t * buf, + _PDCLIB_bool (*wread)( _PDCLIB_fd_t self, _PDCLIB_wchar_t * buf, _PDCLIB_size_t length, _PDCLIB_size_t * numCharsRead ); /* Behaves as write does, except for wide characters. As with wread, both * length and *numCharsWritten are character counts. * - * This function is also optional; if missing, PDCLib will buffer the - * character data as bytes and do translation directly from the user's - * buffers. You only need to implement this if your backend can directly + * This function is also optional; if missing, PDCLib will buffer the + * character data as bytes and do translation directly from the user's + * buffers. You only need to implement this if your backend can directly * take wide characters (for example, the Windows console) */ - _PDCLIB_bool (*wwrite)( _PDCLIB_fd_t self, const _PDCLIB_wchar_t * buf, + _PDCLIB_bool (*wwrite)( _PDCLIB_fd_t self, const _PDCLIB_wchar_t * buf, _PDCLIB_size_t length, _PDCLIB_size_t * numCharsWritten ); }; @@ -195,7 +196,7 @@ static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n, int c; while ( stream->ungetidx > 0 && i != n ) { - c = (unsigned char) + c = (unsigned char) ( out[ i++ ] = stream->ungetbuf[ --(stream->ungetidx) ] ); if( c == stopchar ) return i; @@ -203,9 +204,9 @@ static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n, while ( i != n ) { - while ( stream->bufidx != stream->bufend && i != n) + while ( stream->bufidx != stream->bufend && i != n) { - c = (unsigned char) + c = (unsigned char) ( out[ i++ ] = stream->buffer[ stream->bufidx++ ] ); if( c == stopchar ) return i; @@ -223,10 +224,10 @@ static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n, return i; } -/* Unlocked functions - internal names +/* Unlocked functions - internal names * * We can't use the functions using their "normal" names internally because that - * would cause namespace leakage. Therefore, we use them by prefixed internal + * would cause namespace leakage. Therefore, we use them by prefixed internal * names */ void _PDCLIB_flockfile(struct _PDCLIB_file *file) _PDCLIB_nothrow; @@ -237,7 +238,7 @@ int _PDCLIB_getc_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; int _PDCLIB_getchar_unlocked(void) _PDCLIB_nothrow; int _PDCLIB_putc_unlocked(int c, struct _PDCLIB_file *stream) _PDCLIB_nothrow; int _PDCLIB_putchar_unlocked(int c) _PDCLIB_nothrow; -void _PDCLIB_clearerr_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; +void _PDCLIB_clearerr_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; int _PDCLIB_feof_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; int _PDCLIB_ferror_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; int _PDCLIB_fflush_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; -- 2.40.0