]> pd.if.org Git - pdclib/blobdiff - functions/_PDCLIB/scan.c
Rough draft implementation.
[pdclib] / functions / _PDCLIB / scan.c
index 1502f62ba5cc6c489fcc2e94b046cfbefea6987e..c9826109cd56e9fe6af0fa0cd3e5b86564e4a74c 100644 (file)
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
 
 /* Using an integer's bits as flags for both the conversion flags and length
    modifiers.
 #define E_unsigned   1<<16
 
 
-static bool MATCH( const char x, struct _PDCLIB_status_t * status )
+#define ASSIGN( case_cond, type ) \
+    case case_cond: \
+        *( va_arg( status->arg, type * ) ) = (type)( value * sign ); \
+        break
+
+
+static int GET( struct _PDCLIB_status_t * status )
 {
+    ++(status->i);
+    ++(status->this);
     if ( status->stream != NULL )
     {
-        /* Matching against stream */
-        if ( status->stream->buffer[ status->stream->bufidx ] == x )
-        {
-            getc( status->stream );
-            ++(status->i);
-            return true;
-        }
-        else
-        {
-            return false;
-        }
+        return getc( status->stream );
     }
     else
     {
-        /* Matching against string */
-        if ( *(status->s) == x )
-        {
-            ++(status->s);
-            ++(status->i);
-            return true;
-        }
-        else
-        {
-            return false;
-        }
+        return *((status->s)++);
     }
 }
 
 
+static void UNGET( int c, struct _PDCLIB_status_t * status )
+{
+    if ( status->stream != NULL )
+    {
+        ungetc( c, status->stream ); /* TODO: Error? */
+    }
+    else
+    {
+        *(--(status->s)) = c;
+    }
+    --(status->i);
+    --(status->this);
+}
+
+
 const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
 {
+    /* generic input character */
+    int rc;
     const char * orig_spec = spec;
     if ( *(++spec) == '%' )
     {
         /* %% -> match single '%' */
-        MATCH( *spec, status );
-        return ++spec;
+        rc = GET( status );
+        switch ( rc )
+        {
+            case EOF:
+                /* matching failure */
+                return NULL;
+            case '%':
+                return ++spec;
+            default:
+                UNGET( rc, status );
+                break;
+        }
     }
     /* Initializing status structure */
     status->flags = 0;
-    status->base = 0;
+    status->base = -1;
     status->this = 0;
     status->width = 0;
     status->prec = 0;
@@ -144,13 +163,17 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
     }
 
     /* Conversion specifier */
+
+    /* whether valid input had been parsed */
+    bool value_parsed = false;
+
     switch ( *spec )
     {
         case 'd':
             status->base = 10;
             break;
         case 'i':
-            /* status->base = 0; */
+            status->base = 0;
             break;
         case 'o':
             status->base = 8;
@@ -174,13 +197,56 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
         case 'A':
             break;
         case 'c':
-            /* TODO */
-            break;
+        {
+            char * c = va_arg( status->arg, char * );
+            if ( status->width == SIZE_MAX )
+            {
+                status->width = 1;
+            }
+            while ( ( status->this < status->width ) &&
+                    ( ( rc = GET( status ) ) != EOF ) )
+            {
+                *(c++) = rc;
+                value_parsed = true;
+            }
+            return value_parsed ? spec : NULL;
+        }
         case 's':
-            /* TODO */
-            break;
+        {
+            char * c = va_arg( status->arg, char * );
+            while ( ( status->this < status->width ) && 
+                    ( ( rc = GET( status ) ) != EOF ) )
+            {
+                if ( isspace( rc ) )
+                {
+                    if ( value_parsed )
+                    {
+                        *c = '\0';
+                        return spec;
+                    }
+                    else
+                    {
+                        --(status->this);
+                    }
+                }
+                else
+                {
+                    value_parsed = true;
+                    *(c++) = rc;
+                }
+            }
+            /* width or input exhausted */
+            if ( value_parsed )
+            {
+                *c = '\0';
+                return spec;
+            }
+            else
+            {
+                return NULL;
+            }
+        }
         case 'p':
-            /* TODO */
             status->base = 16;
             status->flags |= E_unsigned;
             break;
@@ -194,10 +260,120 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
             /* No conversion specifier. Bad conversion. */
             return orig_spec;
     }
-    /* TODO: Actual conversions */
+
+    if ( status->base != -1 )
+    {
+        /* integer conversion */
+        uintmax_t value = 0;
+        bool prefix_parsed = false;
+        int sign = 0;
+        while ( ( status->this < status->width ) &&
+                ( ( rc = GET( status ) ) != EOF ) )
+        {
+            if ( ! sign )
+            {
+                switch ( rc )
+                {
+                    case '-':
+                        sign = -1;
+                        break;
+                    case '+':
+                        sign = 1;
+                        break;
+                    default:
+                        sign = 1;
+                        UNGET( rc, status );
+                        break;
+                }
+            }
+            else if ( ! prefix_parsed )
+            {
+                prefix_parsed = true;
+                if ( rc != '0' )
+                {
+                    if ( status->base == 0 )
+                    {
+                        status->base = 10;
+                    }
+                    UNGET( rc, status );
+                }
+                else
+                {
+                    if ( ( status->this < status->width ) &&
+                         ( ( rc = GET( status ) ) != EOF ) )
+                    {
+                        if ( tolower( rc ) == 'x' )
+                        {
+                            if ( ( status->base == 0 ) ||
+                                 ( status->base == 16 ) )
+                            {
+                                status->base = 16;
+                            }
+                            else
+                            {
+                                UNGET( rc, status );
+                                value_parsed = true;
+                            }
+                        }
+                        else
+                        {
+                            UNGET( rc, status );
+                            if ( status->base == 0 )
+                            {
+                                status->base = 8;
+                            }
+                            value_parsed = true;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
+                if ( digitptr == NULL )
+                {
+                    /* end of input item */
+                    break;
+                }
+                value *= status->base;
+                value += digitptr - _PDCLIB_digits;
+                value_parsed = true;
+            }
+        }
+        /* width exceeded, EOF, read error, non-matching character */
+        if ( ! value_parsed )
+        {
+            /* matching error */
+            return NULL;
+        }
+        switch ( status->flags & ( E_char | E_short | E_long | E_llong |
+                                   E_intmax | E_size | E_ptrdiff |
+                                   E_unsigned ) )
+        {
+            ASSIGN( E_char, char );
+            ASSIGN( E_char | E_unsigned, unsigned char );
+            ASSIGN( E_short, short );
+            ASSIGN( E_short | E_unsigned, unsigned short );
+            ASSIGN( 0, int );
+            ASSIGN( E_unsigned, unsigned int );
+            ASSIGN( E_long, long );
+            ASSIGN( E_long | E_unsigned, unsigned long );
+            ASSIGN( E_llong, long long );
+            ASSIGN( E_llong | E_unsigned, unsigned long long );
+            ASSIGN( E_intmax, intmax_t );
+            ASSIGN( E_intmax | E_unsigned, uintmax_t );
+            ASSIGN( E_size, size_t );
+            /* ASSIGN( E_size | E_unsigned, unsigned size_t ); */
+            ASSIGN( E_ptrdiff, ptrdiff_t );
+            /* ASSIGN( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */
+        }
+        return spec;
+    }
+    /* TODO: Floats. */
     return NULL;
 }
 
+
 #ifdef TEST
 #include <_PDCLIB_test.h>