Add _cbprintf/_vcbprintf (callback based printf formatters)
authorOwen Shepherd <owen.shepherd@e43.eu>
Tue, 7 Oct 2014 22:19:34 +0000 (23:19 +0100)
committerOwen Shepherd <owen.shepherd@e43.eu>
Tue, 7 Oct 2014 22:19:34 +0000 (23:19 +0100)
functions/stdio/_PDCLIB_print.c
functions/stdio/_PDCLIB_scan.c
functions/stdio/_cbprintf.c [new file with mode: 0644]
functions/stdio/_vcbprintf.c [new file with mode: 0644]
functions/stdio/vfprintf.c
functions/stdio/vscanf.c
functions/stdio/vsnprintf.c
includes/stdio.h
internals/_PDCLIB_int.h
internals/_PDCLIB_io.h

index 21177af13a2f49ad9e8e1d2025bd72544de50546..03623948d45ae8856548a07f0b4137196d9af3c3 100644 (file)
 #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;
index 615d82e0f32ee659d5c9756ac90781b62f9cc660..6a31ff0692dbb8cce0743f0cf624869cd74e7599 100644 (file)
@@ -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 (file)
index 0000000..c0192fe
--- /dev/null
@@ -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 <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#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 <stddef.h>
+
+#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 (file)
index 0000000..dbb2171
--- /dev/null
@@ -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 <stdio.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#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 <stdint.h>
+#include <stddef.h>
+#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
+
index 7dd7f2dfe1e6b4855c99b2217e063fce91ebf1da..0aa66485e7ec96b86a5bde2c340a5e214b5de6b6 100644 (file)
 #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 );
index 3e4ada482a1e8074aeef459312485b4cfe87c476..148c828fa52898de7a86ea0a8d78a1a40a83e4a3 100644 (file)
@@ -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 );
index cda7ab6579d4f45ef9d47580fb5ef540e070d094..6cc233d97f4bed54e2cea64b20ac092734f2c163 100644 (file)
 
 #ifndef REGTEST
 #include <_PDCLIB_io.h>
+#include <string.h>
 
-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
index b10891ffe9b23a7191effab0432ba4bb9fccb5f9..e6e178e0056c5a0fe3907cc3c45364f779226ebf 100644 (file)
@@ -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;
index 134123a1862fa650a1bccb0be8bb8186c8f638fd..c200419e6133a0235fd92448066744cdf83db6d6 100644 (file)
@@ -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                               */
 };
 
index 65b658901ca1293f1cf51d41102aa7b2db74e93d..78e747c0edcc1df81a8184ca7a74372f4ef90a5a 100644 (file)
@@ -53,9 +53,10 @@ union _PDCLIB_fd
    be that of the current printf() function, of which the members n, s, stream\r
    and arg will be preserved; i will be updated; and all others will be trashed\r
    by the function.\r
-   Returns a pointer to the first character not parsed as conversion specifier.\r
+   Returns the number of characters parsed as a conversion specifier (0 if none\r
+   parsed); returns -1 if the underlying I/O callback returns failure.\r
 */\r
-const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status );\r
+int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status );\r
 \r
 /* The worker for all scanf() type of functions. The pointer spec should point\r
    to the introducing '%' of a conversion specifier. The status structure is to\r
@@ -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. */\r
 unsigned int _PDCLIB_filemode( const char * mode );\r
 \r
-/* Sanity checking and preparing of read buffer, should be called first thing \r
+/* Sanity checking and preparing of read buffer, should be called first thing\r
    by any stdio read-data function.\r
    Returns 0 on success, EOF on error.\r
    On error, EOF / error flags and errno are set appropriately.\r
@@ -103,7 +104,7 @@ int _PDCLIB_fillbuffer( _PDCLIB_file_t * stream );
 /* Repositions within a file. Returns new offset on success,\r
    -1 / errno on error.\r
 */\r
-_PDCLIB_int_fast64_t _PDCLIB_seek( _PDCLIB_file_t * stream, \r
+_PDCLIB_int_fast64_t _PDCLIB_seek( _PDCLIB_file_t * stream,\r
                                   _PDCLIB_int_fast64_t offset, int whence );\r
 \r
 /* File backend I/O operations\r
@@ -122,49 +123,49 @@ struct _PDCLIB_fileops
      *  On error, returns false and sets errno appropriately. *numBytesRead is\r
      *  ignored in this situation.\r
      */\r
-    _PDCLIB_bool (*read)( _PDCLIB_fd_t self, \r
-                          void * buf, \r
-                          _PDCLIB_size_t length, \r
+    _PDCLIB_bool (*read)( _PDCLIB_fd_t self,\r
+                          void * buf,\r
+                          _PDCLIB_size_t length,\r
                           _PDCLIB_size_t * numBytesRead );\r
 \r
     /*! Write length bytes to the file from buf; returning the number of bytes\r
      *  actually written in *numBytesWritten\r
      *\r
      *  Returns true if bytes were written successfully. On error, returns false\r
-     *  and setss errno appropriately (as with read, *numBytesWritten is \r
+     *  and setss errno appropriately (as with read, *numBytesWritten is\r
      *  ignored)\r
      */\r
-    _PDCLIB_bool (*write)( _PDCLIB_fd_t self, const void * buf, \r
+    _PDCLIB_bool (*write)( _PDCLIB_fd_t self, const void * buf,\r
                    _PDCLIB_size_t length, _PDCLIB_size_t * numBytesWritten );\r
 \r
     /* Seek to the file offset specified by offset, from location whence, which\r
      * may be one of the standard constants SEEK_SET/SEEK_CUR/SEEK_END\r
      */\r
-    _PDCLIB_bool (*seek)( _PDCLIB_fd_t self, _PDCLIB_int_fast64_t offset, \r
+    _PDCLIB_bool (*seek)( _PDCLIB_fd_t self, _PDCLIB_int_fast64_t offset,\r
                           int whence, _PDCLIB_int_fast64_t *newPos );\r
 \r
     void (*close)( _PDCLIB_fd_t self );\r
 \r
-    /*! Behaves as read does, except for wide characters. Both length and \r
+    /*! Behaves as read does, except for wide characters. Both length and\r
      *  *numCharsRead represent counts of characters, not bytes.\r
      *\r
      *  This function is optional; if missing, PDCLib will buffer the character\r
      *  data as bytes and perform translation directly into the user's buffers.\r
-     *  It is useful if your backend can directly take wide characters (for \r
+     *  It is useful if your backend can directly take wide characters (for\r
      *  example, the Windows console)\r
      */\r
-    _PDCLIB_bool (*wread)( _PDCLIB_fd_t self, _PDCLIB_wchar_t * buf, \r
+    _PDCLIB_bool (*wread)( _PDCLIB_fd_t self, _PDCLIB_wchar_t * buf,\r
                      _PDCLIB_size_t length, _PDCLIB_size_t * numCharsRead );\r
 \r
     /* Behaves as write does, except for wide characters. As with wread, both\r
      * length and *numCharsWritten are character counts.\r
      *\r
-     * This function is also optional; if missing, PDCLib will buffer the \r
-     * character data as bytes and do translation directly from the user's \r
-     * buffers. You only need to implement this if your backend can directly \r
+     * This function is also optional; if missing, PDCLib will buffer the\r
+     * character data as bytes and do translation directly from the user's\r
+     * buffers. You only need to implement this if your backend can directly\r
      * take wide characters (for example, the Windows console)\r
      */\r
-    _PDCLIB_bool (*wwrite)( _PDCLIB_fd_t self, const _PDCLIB_wchar_t * buf, \r
+    _PDCLIB_bool (*wwrite)( _PDCLIB_fd_t self, const _PDCLIB_wchar_t * buf,\r
                      _PDCLIB_size_t length, _PDCLIB_size_t * numCharsWritten );\r
 };\r
 \r
@@ -195,7 +196,7 @@ static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n,
     int c;\r
     while ( stream->ungetidx > 0 && i != n )\r
     {\r
-        c = (unsigned char) \r
+        c = (unsigned char)\r
                 ( out[ i++ ] = stream->ungetbuf[ --(stream->ungetidx) ] );\r
         if( c == stopchar )\r
             return i;\r
@@ -203,9 +204,9 @@ static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n,
 \r
     while ( i != n )\r
     {\r
-        while ( stream->bufidx != stream->bufend && i != n) \r
+        while ( stream->bufidx != stream->bufend && i != n)\r
         {\r
-            c = (unsigned char) \r
+            c = (unsigned char)\r
                 ( out[ i++ ] = stream->buffer[ stream->bufidx++ ] );\r
             if( c == stopchar )\r
                 return i;\r
@@ -223,10 +224,10 @@ static inline _PDCLIB_size_t _PDCLIB_getchars( char * out, _PDCLIB_size_t n,
     return i;\r
 }\r
 \r
-/* Unlocked functions - internal names \r
+/* Unlocked functions - internal names\r
  *\r
  * We can't use the functions using their "normal" names internally because that\r
- * would cause namespace leakage. Therefore, we use them by prefixed internal \r
+ * would cause namespace leakage. Therefore, we use them by prefixed internal\r
  * names\r
  */\r
 void _PDCLIB_flockfile(struct _PDCLIB_file *file) _PDCLIB_nothrow;\r
@@ -237,7 +238,7 @@ int _PDCLIB_getc_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;
 int _PDCLIB_getchar_unlocked(void) _PDCLIB_nothrow;\r
 int _PDCLIB_putc_unlocked(int c, struct _PDCLIB_file *stream) _PDCLIB_nothrow;\r
 int _PDCLIB_putchar_unlocked(int c) _PDCLIB_nothrow;\r
-void _PDCLIB_clearerr_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow; \r
+void _PDCLIB_clearerr_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;\r
 int _PDCLIB_feof_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;\r
 int _PDCLIB_ferror_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;\r
 int _PDCLIB_fflush_unlocked(struct _PDCLIB_file *stream) _PDCLIB_nothrow;\r