]> pd.if.org Git - pdclib/blobdiff - functions/_PDCLIB/print.c
Clearing the Tyndur tests.
[pdclib] / functions / _PDCLIB / print.c
index 40ec2a2c7c88c62f08640107579edd667258ef16..6db55e85a27307ef8211091f2891148f69c0ed3f 100644 (file)
@@ -9,6 +9,9 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stddef.h>
+#include <string.h>
+
+#ifndef REGTEST
 
 /* Using an integer's bits as flags for both the conversion flags and length
    modifiers.
 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
           width flags) into a combined field.
 */
-#define E_minus    1<<0
-#define E_plus     1<<1
-#define E_alt      1<<2
-#define E_space    1<<3
-#define E_zero     1<<4
-#define E_done     1<<5
-#define E_char     1<<6
-#define E_short    1<<7
-#define E_long     1<<8
-#define E_llong    1<<9
-#define E_intmax   1<<10
-#define E_size     1<<11
-#define E_ptrdiff  1<<12
-#define E_intptr   1<<13
-#define E_ldouble  1<<14
-#define E_lower    1<<15
-#define E_unsigned 1<<16
+#define E_minus    (1<<0)
+#define E_plus     (1<<1)
+#define E_alt      (1<<2)
+#define E_space    (1<<3)
+#define E_zero     (1<<4)
+#define E_done     (1<<5)
+
+#define E_char     (1<<6)
+#define E_short    (1<<7)
+#define E_long     (1<<8)
+#define E_llong    (1<<9)
+#define E_intmax   (1<<10)
+#define E_size     (1<<11)
+#define E_ptrdiff  (1<<12)
+#define E_pointer  (1<<13)
+
+#define E_ldouble  (1<<14)
+
+#define E_lower    (1<<15)
+#define E_unsigned (1<<16)
 
 /* This macro delivers a given character to either a memory buffer or a stream,
    depending on the contents of 'status' (struct _PDCLIB_status_t).
@@ -56,10 +62,14 @@ do { \
 
 static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
 {
+    if ( status->prec < 0 )
+    {
+        status->prec = 1;
+    }
     /* At worst, we need two prefix characters (hex prefix). */
     char preface[3] = "\0";
     size_t preidx = 0;
-    if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
+    if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) && ( value != 0 ) )
     {
         /* Octal / hexadecimal prefix for "%#" conversions */
         preface[ preidx++ ] = '0';
@@ -86,18 +96,22 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
         }
     }
     {
-    size_t prec_pads = ( status->prec > status->current ) ? ( status->prec - status->current ) : 0;
+    /* At this point, status->current has the number of digits queued up.
+       Determine if we have a precision requirement to pad those.
+    */
+    size_t prec_pads = ( (_PDCLIB_size_t)status->prec > status->current ) ? ( (_PDCLIB_size_t)status->prec - status->current ) : 0;
     if ( ! ( status->flags & ( E_minus | E_zero ) ) )
     {
         /* Space padding is only done if no zero padding or left alignment
-           is requested. Leave space for any prefixes determined above.
+           is requested. Calculate the number of characters that WILL be
+           printed, including any prefixes determined above.
         */
         /* The number of characters to be printed, plus prefixes if any. */
         /* This line contained probably the most stupid, time-wasting bug
            I've ever perpetrated. Greetings to Samface, DevL, and all
            sceners at Breakpoint 2006.
         */
-        size_t characters = preidx + ( ( status->current > status->prec ) ? status->current : status->prec );
+        size_t characters = preidx + ( ( status->current > (_PDCLIB_size_t)status->prec ) ? status->current : (_PDCLIB_size_t)status->prec );
         if ( status->width > characters )
         {
             for ( size_t i = 0; i < status->width - characters; ++i )
@@ -114,6 +128,12 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
         PUT( preface[ preidx++ ] );
         ++(status->current);
     }
+    /* Do the precision padding if necessary. */
+    while ( prec_pads-- > 0 )
+    {
+        PUT( '0' );
+        ++(status->current);
+    }
     if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
     {
         /* If field is not left aligned, and zero padding is requested, do
@@ -125,11 +145,6 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
             ++(status->current);
         }
     }
-    /* Do the precision padding if necessary. */
-    for ( size_t i = 0; i < prec_pads; ++i )
-    {
-        PUT( '0' );
-    }
     }
 }
 
@@ -144,6 +159,12 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
 */
 static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
 {
+    /* Special case: zero value, zero precision -- no output (but padding) */
+    if ( status->current == 0 && value == 0 && status->prec == 0 )
+    {
+        intformat( value, status );
+        return;
+    }
     /* Registering the character being printed at the end of the function here
        already so it will be taken into account when the deepestmost recursion
        does the prefix / padding stuff.
@@ -184,6 +205,55 @@ static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
 }
 
 
+static void stringformat( const char * s, struct _PDCLIB_status_t * status )
+{
+    if ( status->flags & E_char )
+    {
+        status->prec = 1;
+    }
+    else
+    {
+        if ( status->prec < 0 )
+        {
+            status->prec = strlen( s );
+        }
+        else
+        {
+            for ( int i = 0; i < status->prec; ++i )
+            {
+                if ( s[i] == 0 )
+                {
+                    status->prec = i;
+                    break;
+                }
+            }
+        }
+    }
+    if ( ! ( status->flags & E_minus ) && ( status->width > (_PDCLIB_size_t)status->prec ) )
+    {
+        while ( status->current < ( status->width - status->prec ) )
+        {
+            PUT( ' ' );
+            ++(status->current);
+        }
+    }
+    while ( status->prec > 0 )
+    {
+        PUT( *(s++) );
+        --(status->prec);
+        ++(status->current);
+    }
+    if ( status->flags & E_minus )
+    {
+        while ( status->width > status->current )
+        {
+            PUT( ' ' );
+            ++(status->current);
+        }
+    }
+}
+
+
 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
 {
     const char * orig_spec = spec;
@@ -198,7 +268,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
     status->base  = 0;
     status->current  = 0;
     status->width = 0;
-    status->prec  = 0;
+    status->prec  = EOF;
 
     /* First come 0..n flags */
     do
@@ -273,6 +343,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
                EOF (negative), there is no need for testing for negative here.
             */
             status->prec = va_arg( status->arg, int );
+            ++spec;
         }
         else
         {
@@ -280,13 +351,13 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
             status->prec = (int)strtol( spec, &endptr, 10 );
             if ( spec == endptr )
             {
-                /* Decimal point but no number - bad conversion specifier. */
-                return orig_spec;
+                /* Decimal point but no number - equals zero */
+                status->prec = 0;
             }
             spec = endptr;
         }
         /* Having a precision cancels out any zero flag. */
-        status->flags ^= E_zero;
+        status->flags &= ~E_zero;
     }
 
     /* Optional length modifier
@@ -378,23 +449,21 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
         case 'A':
             break;
         case 'c':
-            /* TODO: Flags, wide chars. */
-            PUT( va_arg( status->arg, int ) );
-            return ++spec;
-        case 's':
-            /* TODO: Flags, wide chars. */
+            /* TODO: wide chars. */
             {
-                char * s = va_arg( status->arg, char * );
-                while ( *s != '\0' )
-                {
-                    PUT( *(s++) );
-                }
+                char c[1];
+                c[0] = (char)va_arg( status->arg, int );
+                status->flags |= E_char;
+                stringformat( c, status );
                 return ++spec;
             }
+        case 's':
+            /* TODO: wide chars. */
+            stringformat( va_arg( status->arg, char * ), status );
+            return ++spec;
         case 'p':
-            /* TODO: E_long -> E_intptr */
             status->base = 16;
-            status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
+            status->flags |= ( E_lower | E_unsigned | E_alt | E_pointer );
             break;
         case 'n':
            {
@@ -415,7 +484,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
         if ( status->flags & E_unsigned )
         {
             uintmax_t value;
-            switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
+            switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size | E_pointer ) )
             {
                 case E_char:
                     value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
@@ -435,30 +504,46 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
                 case E_size:
                     value = (uintmax_t)va_arg( status->arg, size_t );
                     break;
+                case E_pointer:
+                    value = (uintmax_t)(uintptr_t)va_arg( status->arg, void * );
+                    break;
+                default:
+                    puts( "UNSUPPORTED PRINTF FLAG COMBINATION" );
+                    return NULL;
             }
-            ++(status->current);
-            /* FIXME: The if clause means one-digit values do not get formatted */
-            /* Was introduced originally to get value to "safe" levels re. uintmax_t. */
-            if ( ( value / status->base ) != 0 )
-            {
-                int2base( (intmax_t)(value / status->base), status );
-            }
-            else
-            {
-                intformat( (intmax_t)value, status );
-            }
-            int digit = value % status->base;
-            if ( digit < 0 )
-            {
-                digit *= -1;
-            }
-            if ( status->flags & E_lower )
+            /* Special case: zero value, zero precision: No output, just padding */
+            if ( value == 0 && status->prec == 0 )
             {
-                PUT( _PDCLIB_digits[ digit ] );
+                int2base( 0, status );
             }
             else
             {
-                PUT( _PDCLIB_Xdigits[ digit ] );
+                /* To make the call to int2base (using intmax_t) safe for
+                   uintmax_t values > INTMAX_MAX, we basically to the first
+                   "recursion" level of int2base right here.
+                */
+                ++(status->current);
+                if ( ( value / status->base ) != 0 )
+                {
+                    int2base( (intmax_t)(value / status->base), status );
+                }
+                else
+                {
+                    intformat( (intmax_t)value, status );
+                }
+                int digit = value % status->base;
+                if ( digit < 0 )
+                {
+                    digit *= -1;
+                }
+                if ( status->flags & E_lower )
+                {
+                    PUT( _PDCLIB_digits[ digit ] );
+                }
+                else
+                {
+                    PUT( _PDCLIB_Xdigits[ digit ] );
+                }
             }
         }
         else
@@ -486,6 +571,9 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
                 case E_intmax:
                     int2base( va_arg( status->arg, intmax_t ), status );
                     break;
+                default:
+                    puts( "UNSUPPORTED PRINTF FLAG COMBINATION" );
+                    return NULL;
             }
         }
         if ( status->flags & E_minus )
@@ -504,11 +592,15 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
     return ++spec;
 }
 
+#endif
+
 #ifdef TEST
 #define _PDCLIB_FILEID "_PDCLIB/print.c"
 #define _PDCLIB_STRINGIO
 
-#include <_PDCLIB_test.h>
+#include "_PDCLIB_test.h"
+
+#ifndef REGTEST
 
 static int testprintf( char * buffer, const char * format, ... )
 {
@@ -521,7 +613,7 @@ static int testprintf( char * buffer, const char * format, ... )
     status.current = 0;
     status.s = buffer;
     status.width = 0;
-    status.prec = 0;
+    status.prec = EOF;
     status.stream = NULL;
     va_start( status.arg, format );
     memset( buffer, '\0', 100 );
@@ -534,12 +626,16 @@ static int testprintf( char * buffer, const char * format, ... )
     return status.i;
 }
 
+#endif
+
 #define TEST_CONVERSION_ONLY
 
 int main( void )
 {
+#ifndef REGTEST
     char target[100];
 #include "printf_testcases.h"
+#endif
     return TEST_RESULTS;
 }