]> pd.if.org Git - pdclib/blobdiff - functions/_PDCLIB/scan.c
Intermediate code.
[pdclib] / functions / _PDCLIB / scan.c
index d12e8844a06e9eebc712cc68f522a5afbdc026e0..523b0c81449c5846f50868f09118fcb445c69dba 100644 (file)
 */
 
 #include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+/* Using an integer's bits as flags for both the conversion flags and length
+   modifiers.
+*/
+#define E_suppressed 1<<0
+#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_unsigned   1<<16
+
+
+#define MATCH_FAIL -1
+#define MATCH_ERROR -2
+
+static int MATCH( int c, struct _PDCLIB_status_t * status )
+{
+    if ( status->stream != NULL )
+    {
+        if ( ! _PDCLIB_prepread( status->stream ) )
+        {
+            return MATCH_ERROR;
+        }
+        if ( tolower( status->stream->buffer[ status->stream->bufidx ] ) == c )
+        {
+            /* recycling parameter */
+            c = getc( status->stream );
+        }
+        else
+        {
+            return MATCH_FAIL;
+        }
+    }
+    else
+    {
+        if ( tolower( *(status->s) ) == c )
+        {
+            /* recycling parameter */
+            c = *((status->s)++); /* TODO: \0 */
+        }
+        else
+        {
+            return MATCH_FAIL;
+        }
+    }
+    ++(status->i);
+    ++(status->this);
+    return c;
+}
+
+
+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 )
 {
-    return ++spec;
+    const char * orig_spec = spec;
+    if ( *(++spec) == '%' )
+    {
+        /* %% -> match single '%' */
+        MATCH( *spec, status );
+        return ++spec;
+    }
+    /* Initializing status structure */
+    status->flags = 0;
+    status->base = -1;
+    status->this = 0;
+    status->width = 0;
+    status->prec = 0;
+
+    /* '*' suppresses assigning parsed value to variable */
+    if ( *spec == '*' )
+    {
+        status->flags |= E_suppressed;
+        ++spec;
+    }
+
+    /* If a width is given, strtol() will return its value. If not given,
+       strtol() will return zero. In both cases, endptr will point to the
+       rest of the conversion specifier - just what we need.
+    */
+    status->width = (int)strtol( spec, (char**)&spec, 10 );
+
+    /* Optional length modifier
+       We step one character ahead in any case, and step back only if we find
+       there has been no length modifier (or step ahead another character if it
+       has been "hh" or "ll").
+    */
+    switch ( *(spec++) )
+    {
+        case 'h':
+            if ( *spec == 'h' )
+            {
+                /* hh -> char */
+                status->flags |= E_char;
+                ++spec;
+            }
+            else
+            {
+                /* h -> short */
+                status->flags |= E_short;
+            }
+            break;
+        case 'l':
+            if ( *spec == 'l' )
+            {
+                /* ll -> long long */
+                status->flags |= E_llong;
+                ++spec;
+            }
+            else
+            {
+                /* l -> long */
+                status->flags |= E_long;
+            }
+            break;
+        case 'j':
+            /* j -> intmax_t, which might or might not be long long */
+            status->flags |= E_intmax;
+            break;
+        case 'z':
+            /* z -> size_t, which might or might not be unsigned int */
+            status->flags |= E_size;
+            break;
+        case 't':
+            /* t -> ptrdiff_t, which might or might not be long */
+            status->flags |= E_ptrdiff;
+            break;
+        case 'L':
+            /* L -> long double */
+            status->flags |= E_ldouble;
+            break;
+        default:
+            --spec;
+            break;
+    }
+
+    /* Conversion specifier */
+    switch ( *spec )
+    {
+        case 'd':
+            status->base = 10;
+            break;
+        case 'i':
+            status->base = 0;
+            break;
+        case 'o':
+            status->base = 8;
+            status->flags |= E_unsigned;
+            break;
+        case 'u':
+            status->base = 10;
+            status->flags |= E_unsigned;
+            break;
+        case 'x':
+            status->base = 16;
+            status->flags |= E_unsigned;
+            break;
+        case 'f':
+        case 'F':
+        case 'e':
+        case 'E':
+        case 'g':
+        case 'G':
+        case 'a':
+        case 'A':
+            break;
+        case 'c':
+            /* TODO */
+            break;
+        case 's':
+            /* TODO */
+            break;
+        case 'p':
+            /* TODO */
+            status->base = 16;
+            status->flags |= E_unsigned;
+            break;
+        case 'n':
+        {
+            int * val = va_arg( status->arg, int * );
+            *val = status->i;
+            return ++spec;
+        }
+        default:
+            /* No conversion specifier. Bad conversion. */
+            return orig_spec;
+    }
+    bool zero = false;
+    if ( status->base != -1 )
+    {
+        bool value = false;
+        int rc;
+        if ( ( rc = MATCH( '0', status ) ) >= 0 )
+        {
+            if ( ( rc = MATCH( 'x', status ) ) >= 0 )
+            {
+                if ( ( status->base == 0 ) || ( status->base == 16 ) )
+                {
+                    status->base = 16;
+                }
+                else
+                {
+                    UNGET( rc, status );
+                    value = true;
+                }
+            }
+            else if ( rc == MATCH_FAIL )
+            {
+                if ( status->base == 0 )
+                {
+                    status->base = 8;
+                }
+                else
+                {
+                    value = true;
+                }
+            }
+            else
+            {
+                /* TODO: MATCH_ERROR */
+            }
+        }
+        else if ( rc == MATCH_FAIL )
+        {
+            if ( status->base == 0 )
+            {
+                status->base = 10;
+            }
+        }
+        else
+        {
+            /* TODO: MATCH_ERROR */
+        }
+        /* TODO: Integer conversion */
+    }
+    else
+    {
+        /* TODO: Float conversions? */
+    }
+    return NULL;
 }
 
 #ifdef TEST