]> git.sur5r.net Git - cc65/commitdiff
Rewrote _scanf. It does need some tests and improvements, but it's a more
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Fri, 26 Nov 2004 22:16:54 +0000 (22:16 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Fri, 26 Nov 2004 22:16:54 +0000 (22:16 +0000)
standard version than before, and it does support the necessary functionality
to support scanf functions for files.
Added vfscanf, fscanf and vfscanf.

git-svn-id: svn://svn.cc65.org/cc65/trunk@3301 b7a2c559-68d2-44c3-8de9-860c34a00d81

libsrc/common/.cvsignore
libsrc/common/Makefile
libsrc/common/_scanf.c
libsrc/common/_scanf.h
libsrc/common/fscanf.c [new file with mode: 0644]
libsrc/common/scanf.c [new file with mode: 0644]
libsrc/common/vfscanf.c [new file with mode: 0644]
libsrc/common/vsscanf.c

index adb36788890c96c1296d36ef403fd2c07a203e04..25e50ed2a16678b613a98118882f3a3d99cb3e8c 100644 (file)
@@ -14,6 +14,7 @@ fgets.s
 fputc.s
 fputs.s
 freopen.s
+fscanf.s
 fseek.s
 fsetpos.s
 ftell.s
@@ -28,6 +29,7 @@ puts.s
 qsort.s
 realloc.s
 rewind.s
+scanf.s
 sleep.s
 sscanf.s
 strftime.s
@@ -35,4 +37,7 @@ strtok.s
 strxfrm.s
 system.s
 timezone.s
+vfscanf.s
 vsscanf.s
+
+
index 7f3babe3363fe7ba69903dafd7a2ad8a7a15a15f..c72f9974290fedb355ad11780124078714278074 100644 (file)
@@ -44,6 +44,7 @@ C_OBJS =      _afailed.o      \
                fputc.o         \
                fputs.o         \
                freopen.o       \
+                fscanf.o        \
                fseek.o         \
                fsetpos.o       \
                ftell.o         \
@@ -58,6 +59,7 @@ C_OBJS =      _afailed.o      \
                qsort.o         \
                realloc.o       \
                rewind.o        \
+                scanf.o         \
                sleep.o         \
                sscanf.o        \
                 strftime.o      \
@@ -65,6 +67,7 @@ C_OBJS =      _afailed.o      \
                strtok.o        \
                 system.o        \
                 timezone.o      \
+                vfscanf.o       \
                 vsscanf.o
 
 
index 7351a5d930077dfa30dc9416699412ab925a742c..53d65694e03ade1fecff15378457ba8c10530230 100644 (file)
 
 
 
-static struct indesc*  D;              /* Copy of function argument */
-static va_list         ap;             /* Copy of function argument */
-static jmp_buf                 JumpBuf;        /* Label that is used in case of EOF */
-static char                    C;              /* Character from input */
-static unsigned                Width;          /* Maximum field width */
-static long                    IntVal;         /* Converted int value */
-static unsigned                Conversions;    /* Number of conversions */
+static struct scanfdata*  D;           /* Copy of function argument */
+static va_list           ap;           /* Copy of function argument */
+static jmp_buf                   JumpBuf;      /* Label that is used in case of EOF */
+static int                C;                   /* Character from input */
+static unsigned                  Width;        /* Maximum field width */
+static long                      IntVal;       /* Converted int value */
+static unsigned                  Conversions;  /* Number of conversions */
+static unsigned char      IntBytes;     /* Number of bytes-1 for int conversions */
 
 /* Flags */
-static unsigned char   Positive;       /* Flag for positive value */
-static unsigned char   NoAssign;       /* Supppress assigment */
-static unsigned char   IsShort;        /* Short type */
-static unsigned char   IsLong;         /* Long type */
-static unsigned char    Invert;         /* Do we need to invert the charset? */
-static unsigned char    CharSet[32];    /* 32 * 8 bits = 256 bits */
+static unsigned char             Positive;     /* Flag for positive value */
+static unsigned char             NoAssign;     /* Supppress assigment */
+static unsigned char      Invert;       /* Do we need to invert the charset? */
+static unsigned char      CharSet[32];  /* 32 * 8 bits = 256 bits */
 static const unsigned char Bits[8] = {
     0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
 };
@@ -120,7 +119,7 @@ static void InvertCharSet (void)
 
 
 /*****************************************************************************/
-/*                                  Code                                    */
+/*                                  Code                                    */
 /*****************************************************************************/
 
 
@@ -128,8 +127,21 @@ static void InvertCharSet (void)
 static void ReadChar (void)
 /* Get an input character, count characters */
 {
-    C = D->fin (D);
-    ++D->ccount;
+    C = D->get (D->data);
+    if (C != EOF) {
+        ++D->ccount;
+    }
+}
+
+
+
+static void ReadCharWithCheck (void)
+/* Get an input char, use longjmp in case of EOF */
+{
+    ReadChar ();
+    if (C == EOF) {
+       longjmp (JumpBuf, RC_EOF);
+    }
 }
 
 
@@ -171,29 +183,8 @@ static unsigned char HexVal (char C)
     if (isdigit (C)) {
        return C - '0';
     } else {
-       return C - toupper (C) + ('A' + 10);
-    }
-}
-
-
-
-static void ReadInt (unsigned char Base)
-/* Read an integer and store it into IntVal */
-{
-    /* Value must start with a digit */
-    if (!isdigit (C)) {
-       longjmp (JumpBuf, RC_NOCONV);
-    }
-
-    /* Read the value */
-    IntVal = 0;
-    while (isxdigit (C) && Width-- > 0) {
-               IntVal = IntVal * Base + HexVal (C);
-       ReadChar ();
+       return toupper (C) - ('A' - 10);
     }
-
-    /* One more conversion */
-    ++Conversions;
 }
 
 
@@ -216,10 +207,7 @@ static void AssignInt (void)
         asm ("stx ptr1+1");
 
         /* Get the number of bytes-1 to copy */
-        asm ("ldy #3");
-        asm ("lda %v", IsLong);
-        asm ("bne L1");
-        asm ("ldy #1");
+        asm ("ldy %v", IntBytes);
 
         /* Assign the integer value */
         asm ("L1: lda %v,y", IntVal);
@@ -232,7 +220,80 @@ static void AssignInt (void)
 
 
 
-int _scanf (struct indesc* D_, register const char* format, va_list ap_)
+static unsigned char ReadInt (unsigned char Base)
+/* Read an integer and store it into IntVal. Returns the number of chars
+ * converted. Does NOT bump Conversions.
+ */
+{
+    unsigned char Val;
+    unsigned char CharCount = 0;
+
+    /* Read the integer value */
+    IntVal = 0;
+    while (isxdigit (C) && Width-- > 0 && (Val = HexVal (C)) < Base) {
+        ++CharCount;
+               IntVal = IntVal * Base + Val;
+       ReadChar ();
+    }
+
+    /* If we didn't convert anything, it's an error */
+    if (CharCount == 0) {
+       longjmp (JumpBuf, RC_NOCONV);
+    }
+
+    /* Return the number of characters converted */
+    return CharCount;
+}
+
+
+
+static void ScanInt (unsigned char Base)
+/* Scan an integer including white space, sign and optional base spec,
+ * and store it into IntVal.
+ */
+{
+    /* Skip whitespace */
+    SkipWhite ();
+
+    /* Read an optional sign */
+    ReadSign ();
+
+    /* If Base is unknown (zero), figure it out */
+    if (Base == 0) {
+        if (C == '0') {
+            ReadChar ();
+            switch (C) {
+                case 'x':
+                case 'X':
+                    Base = 16;
+                    ReadChar ();
+                    break;
+                default:
+                    Base = 8;
+            }
+        } else {
+            Base = 10;
+        }
+    }
+
+    /* Read the integer value */
+    ReadInt (Base);
+
+    /* Apply the sign */
+    if (!Positive) {
+        IntVal = -IntVal;
+    }
+
+    /* Assign the value to the next argument unless suppressed */
+    AssignInt ();
+
+    /* One more conversion */
+    ++Conversions;
+}
+
+
+
+int _scanf (struct scanfdata* D_, register const char* format, va_list ap_)
 /* This is the routine used to do the actual work. It is called from several
  * types of wrappers to implement the actual ISO xxscanf functions.
  */
@@ -240,7 +301,6 @@ int _scanf (struct indesc* D_, register const char* format, va_list ap_)
     char         F;            /* Character from format string */
     unsigned char Result;      /* setjmp result */
     char*        S;
-    unsigned char Base;                /* Integer base in %i */
     unsigned char HaveWidth;   /* True if a width was given */
     char          Start;        /* Start of range */
 
@@ -263,13 +323,13 @@ int _scanf (struct indesc* D_, register const char* format, va_list ap_)
 
 Again:
                /* Get the next input character */
-       ReadChar ();
+       ReadChar ();
 
        /* Walk over the format string */
        while (F = *format++) {
 
            /* Check for a conversion */
-           if (F != '%' || *format == '%') {
+                   if (F != '%' || *format == '%') {
 
                /* %% or any char other than % */
                if (F == '%') {
@@ -279,25 +339,25 @@ Again:
                /* Check for a match */
                if (isspace (F)) {
 
-                   /* Special white space handling: Any whitespace matches
-                    * any amount of whitespace including none(!). So this
-                    * match will never fail.
+                   /* Special white space handling: Any whitespace in the
+                     * format string matches any amount of whitespace including
+                     * none(!). So this match will never fail.
                     */
                    SkipWhite ();
                    continue;
 
-               } else if (F != C) {
+               } else if (F == C) {
+
+                   /* A match. Read the next input character and start over */
+                   goto Again;
+
+               } else {
 
                    /* A mismatch. We will stop scanning the input and return
                     * the number of conversions.
                     */
                    return Conversions;
 
-               } else {
-
-                   /* A match. Read the next input character and start over */
-                   goto Again;
-
                }
 
            } else {
@@ -305,97 +365,82 @@ Again:
                /* A conversion. Skip the percent sign. */
                F = *format++;
 
-               /* Initialize variables */
-               NoAssign    = 0;
-               IsShort     = 0;
-               IsLong      = 0;
-               Width       = UINT_MAX;
-               HaveWidth   = 0;
-
-               /* Check for flags. */
-               while (1) {
-                   if (isdigit (F)) {
-                       HaveWidth = 1;
-                       Width     = 0;
-                       do {
-                           /* ### Non portable ### */
-                           Width = Width * 10 + (F & 0x0F);
-                           F = *format++;
-                       } while (isdigit (F));
-                   } else {
-                       switch (F) {
-                           case '*':   NoAssign = 1;   break;
-                           case 'h':   IsShort = 1;    break;
-                           case 'l':
-                           case 'L':   IsLong = 1;     break;
-                           default:    goto FlagsDone;
-                       }
-                       F = *format++;
-                   }
-               }
-FlagsDone:
+               /* 1. Assignment suppression */
+                if (F == '*') {
+                    F = *format++;
+                    NoAssign = 1;
+                } else {
+                    NoAssign = 0;
+                }
+
+                /* 2. Maximum field width */
+                       Width     = UINT_MAX;
+               HaveWidth = 0;
+                if (isdigit (F)) {
+                    HaveWidth = 1;
+                    Width     = 0;
+                    do {
+                        /* ### Non portable ### */
+                        Width = Width * 10 + (F & 0x0F);
+                        F = *format++;
+                    } while (isdigit (F));
+                }
+
+                /* 3. Length modifier */
+                IntBytes = sizeof(int) - 1;
+                switch (F) {
+                    case 'h':
+                        if (*format == 'h') {
+                            IntBytes = sizeof(char) - 1;
+                            ++format;
+                        }
+                        F = *format++;
+                        break;
+
+                    case 'l':
+                        if (*format == 'l') {
+                            /* Treat long long as long */
+                            ++format;
+                        }
+                        /* FALLTHROUGH */
+                    case 'j':   /* intmax_t */
+                        IntBytes = sizeof(long) - 1;
+                        F = *format++;
+                        break;
+
+                    case 'z':   /* size_t */
+                    case 't':   /* ptrdiff_t */
+                    case 'L':   /* long double - ignore this one */
+                        F = *format++;
+                        break;
+                }
 
-               /* Check for the actual conversion character */
+                /* 4. Conversion specifier */
                switch (F) {
 
-                   case 'D':
-                       IsLong = 1;
-                   case 'd':
-                       /* Optionally signed decimal integer */
-                       SkipWhite ();
-                       ReadSign ();
-                       ReadInt (10);
-                       if (!Positive) {
-                           IntVal = -IntVal;
-                       }
-                       AssignInt ();
-                       break;
+                    /* 'd' and 'u' conversions are actually the same, since the
+                     * standard says that evene the 'u' modifier allows an
+                     * optionally signed integer.
+                     */
+                   case 'd':   /* Optionally signed decimal integer */
+                    case 'u':
+                       ScanInt (10);
+                       break;
 
                    case 'i':
                        /* Optionally signed integer with a base */
-                       SkipWhite ();
-                       ReadSign ();
-                       if (C == '0') {
-                           ReadChar ();
-                           switch (C) {
-                               case 'x':
-                               case 'X':
-                                   Base = 16;
-                                   ReadChar();
-                                   break;
-                               default:
-                                   Base = 8;
-                           }
-                       } else {
-                           Base = 10;
-                       }
-                       ReadInt (Base);
-                       if (!Positive) {
-                           IntVal = -IntVal;
-                       }
-                       AssignInt ();
+                       ScanInt (0);
                        break;
 
                    case 'o':
-                       /* Unsigned octal integer */
-                       SkipWhite ();
-                       ReadInt (8);
-                       AssignInt ();
+                       /* Optionally signed octal integer */
+                       ScanInt (8);
                        break;
 
-                   case 'u':
-                       /* Unsigned decimal integer */
-                       SkipWhite ();
-                       ReadInt (10);
-                       AssignInt ();
-                       break;
-
                    case 'x':
                    case 'X':
-                       /* Unsigned hexadecimal integer */
-                       SkipWhite ();
-                       ReadInt (16);
-                       AssignInt ();
+                       /* Optionally signed hexadecimal integer */
+                       ScanInt (16);
                        break;
 
                    case 'E':
@@ -408,7 +453,7 @@ FlagsDone:
 
                    case 's':
                        /* Whitespace terminated string */
-                       SkipWhite ();
+                       SkipWhite ();
                        if (!NoAssign) {
                            S = va_arg (ap, char*);
                        }
@@ -416,7 +461,7 @@ FlagsDone:
                            if (!NoAssign) {
                                *S++ = C;
                            }
-                           ReadChar ();
+                           ReadChar ();
                        }
                        /* Terminate the string just read */
                        if (!NoAssign) {
@@ -435,12 +480,12 @@ FlagsDone:
                            S = va_arg (ap, char*);
                             while (Width--) {
                                 *S++ = C;
-                                ReadChar ();
+                                ReadCharWithCheck ();
                             }
                        } else {
                             /* Just skip as many chars as given */
                             while (Width--) {
-                                ReadChar ();
+                                ReadCharWithCheck ();
                             }
                         }
                        ++Conversions;
@@ -516,7 +561,7 @@ FlagsDone:
                        break;
 
                    case 'p':
-                       /* Pointer, format is 0xABCD */
+                       /* Pointer, format is 0xABCD */
                        SkipWhite ();
                        if (C != '0') {
                             longjmp (JumpBuf, RC_NOCONV);
@@ -526,8 +571,11 @@ FlagsDone:
                             longjmp (JumpBuf, RC_NOCONV);
                         }
                         ReadChar ();
-                        ReadInt (16);
+                        if (ReadInt (16) != 4) { /* 4 chars expected */
+                            longjmp (JumpBuf, RC_NOCONV);
+                        }
                        AssignInt ();
+                        ++Conversions;
                        break;
 
                    case 'n':
@@ -542,21 +590,24 @@ FlagsDone:
                        break;
 
                }
-
-               /* Skip the format char */
-               goto Again;
-
            }
+        }
 
-       }
+        /* Push back the last unused character, provided it is not EOF */
+        if (C != EOF) {
+            D->unget (C, D->data);
+        }
 
-    } else if (Result == RC_EOF) {
+    } else {
 
-       /* Jump via JumpBuf means EOF on input */
-       if (D->ccount == 0) {
+       /* Jump via JumpBuf means an error. If this happens at EOF with no
+         * conversions, it is considered an error, otherwise the number
+         * of conversions is returned (the default behaviour).
+         */
+               if (C == EOF && D->ccount == 0) {
            /* Special case: error */
-           return -1;
-       }
+           Conversions = EOF;
+       }
 
     }
 
index a385e4ae44f35d00ca84ef2326fe8215916255c5..e477c37daafba9266b0918477201baeea74cf957 100644 (file)
 
 
 
-/* Forward */
-struct indesc;
-
 /* Type of the function that is called to input data. The function will
  * return EOF if no more data is available.
  */
-typedef char (*infunc) (struct indesc* desc);
+typedef int __fastcall__ (*getfunc) (void* data);
+typedef int __fastcall__ (*ungetfunc) (int c, void* data);
 
 
 
@@ -26,21 +24,19 @@ typedef char (*infunc) (struct indesc* desc);
  * Beware: The low level functions will access the structure on the assembly
  * level, so check this when altering the structure.
  */
-struct indesc {
-    infunc         fin;        /* Pointer to input routine */
-    unsigned       ccount;     /* Number of chars read */
+struct scanfdata {
+    getfunc                get;        /* Pointer to input routine */
+    ungetfunc       unget;      /* Pointer to pushback routine */
+    unsigned       ccount;     /* Number of chars read */
 
     /* Fields used outside of _scanf */
-    char*                  buf;        /* Pointer to input buffer */
-    unsigned       size;       /* Size of input buffer */
-    unsigned       fill;       /* Fill mark of input buffer */
-    unsigned       ridx;       /* Read index of input buffer */
+    void*           data;       /* Caller data */
 };
 
 
 
 /* Internal scanning routine */
-int _scanf (struct indesc* d, const char* format, va_list ap);
+int _scanf (struct scanfdata* d, const char* format, va_list ap);
 
 
 
diff --git a/libsrc/common/fscanf.c b/libsrc/common/fscanf.c
new file mode 100644 (file)
index 0000000..dae7109
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * fscanf.c
+ *
+ * Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
+ *
+ */
+
+
+
+#include <stdio.h>
+
+
+
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
+int fscanf (FILE* f, const char* format, ...)
+/* Standard C function */
+{
+    va_list ap;
+
+    /* Setup for variable arguments */
+    va_start (ap, format);
+
+    /* Call vfscanf(). Since we know that va_end won't do anything, we will
+     * save the call and return the value directly.
+     */
+    return vfscanf (f, format, ap);
+}
+
+
+
+
diff --git a/libsrc/common/scanf.c b/libsrc/common/scanf.c
new file mode 100644 (file)
index 0000000..d92174a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * scanf.c
+ *
+ * Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
+ *
+ */
+
+
+
+#include <stdio.h>
+
+
+
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
+int scanf (const char* format, ...)
+/* Standard C function */
+{
+    va_list ap;
+
+    /* Setup for variable arguments */
+    va_start (ap, format);
+
+    /* Call vfscanf(). Since we know that va_end won't do anything, we will
+     * save the call and return the value directly.
+     */
+    return vfscanf (stdin, format, ap);
+}
+
+
+
+
diff --git a/libsrc/common/vfscanf.c b/libsrc/common/vfscanf.c
new file mode 100644 (file)
index 0000000..0f62113
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * vfscanf.c
+ *
+ * Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
+ *
+ */
+
+
+
+#include <stdio.h>
+#include "_scanf.h"
+
+
+
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
+int __fastcall__ vfscanf (FILE* f, const char* format, va_list ap)
+/* Standard C function */
+{
+    struct scanfdata d;
+
+    /* Initialize the data struct. We do only need the given file as user data,
+     * since the get and ungetc are crafted so they match the standard fgetc
+     * and ungetc functions.
+     */
+    d.get    = (getfunc) fgetc,
+    d.unget  = (ungetfunc) ungetc,
+    d.data   = f;
+
+    /* Call the internal function and return the result */
+    return _scanf (&d, format, ap);
+}
+
+
+
index 95fcf5e9bc53bf106aab2444b4890cde7ef9cfb1..68d748c2f103171a114c74e472e12fff5f70bfbe 100644 (file)
@@ -9,7 +9,20 @@
 
 #include <stdio.h>
 #include "_scanf.h"
-                
+
+
+
+/*****************************************************************************/
+/*                                          Data                                    */
+/*****************************************************************************/
+
+
+
+struct sscanfdata {
+    const char* str;            /* Pointer to input string */
+    unsigned    index;          /* Read index */
+};
+
 
 
 /*****************************************************************************/
 
 
 
-static char get (struct indesc* d)
+static int __fastcall__ get (struct sscanfdata* d)
 /* Read a character from the input string and return it */
 {
     char C;
-    if (C = d->buf[d->ridx]) {
-       /* Increment index only if end not reached */
-       ++d->ridx;
+    if (C = d->str[d->index]) {
+       /* Increment index only if end not reached */
+               ++d->index;
+        return C;
+    } else {
+        return EOF;
     }
-    return C;
+}
+
+
+
+static int __fastcall__ unget (int c, struct sscanfdata* d)
+/* Push back a character onto the input stream */
+{
+    /* We do assume here that the _scanf routine will not push back anything
+     * not read, so we can ignore c safely and won't check the index.
+     */
+    --d->index;
+    return c;
 }
 
 
@@ -34,17 +61,20 @@ static char get (struct indesc* d)
 int __fastcall__ vsscanf (const char* str, const char* format, va_list ap)
 /* Standard C function */
 {
-    struct indesc id;
+    struct sscanfdata sd;
+    struct scanfdata d;
 
-    /* Initialize the indesc struct. We leave all fields uninitialized that we
-     * don't need
+    /* Initialize the data structs. The sscanfdata struct will be passed back
+     * to the get and unget functions by _scanf.
      */
-    id.fin  = (infunc) get;
-    id.buf  = (char*) str;
-    id.ridx = 0;
+    d.get    = (getfunc) get;
+    d.unget  = (ungetfunc) unget,
+    d.data   = &sd;
+    sd.str   = str;
+    sd.index = 0;
 
     /* Call the internal function and return the result */
-    return _scanf (&id, format, ap);
+    return _scanf (&d, format, ap);
 }