]> git.sur5r.net Git - cc65/blobdiff - libsrc/common/_scanf.c
Reintroduce a patch for a bug that has been lost between version 1.2 and 1.3
[cc65] / libsrc / common / _scanf.c
index 9543c1c5033a31c6555ab89362712318c527ce01..8dbe2cb77ee8ab733d41cf2e5b172472176c7fbe 100644 (file)
@@ -1,15 +1,18 @@
 /*
  * _scanf.c
  *
- * (C) Copyright 2001 Ullrich von Bassewitz (uz@cc65.org)
+ * (C) Copyright 2001-2002 Ullrich von Bassewitz (uz@cc65.org)
  *
- * This is the basic layer for all scanf type functions.
+ * This is the basic layer for all scanf type functions. It should get
+ * rewritten in assembler at some time in the future, so most of the code
+ * is not as elegant as it could be.
  */
 
 
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 #include <setjmp.h>
 #include <ctype.h>
 #include <limits.h>
@@ -49,15 +52,59 @@ 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 const unsigned char Bits[8] = {
+    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
+};
 
 
 
 /*****************************************************************************/
-/*                             Character sets                               */
+/*                             Character sets                               */
 /*****************************************************************************/
 
 
 
+static void AddCharToSet (unsigned char C)
+/* Set the given bit in the character set */
+{
+    asm ("ldy #%o", C);
+    asm ("lda (sp),y");
+    asm ("lsr a");
+    asm ("lsr a");
+    asm ("lsr a");
+    asm ("tax");
+    asm ("lda (sp),y");
+    asm ("and #$07");
+    asm ("tay");
+    asm ("lda %v,y", Bits);
+    asm ("ora %v,x", CharSet);
+    asm ("sta %v,x", CharSet);
+}
+
+
+
+static unsigned char IsCharInSet (unsigned char C)
+/* Check if the given char is part of the character set */
+{
+    asm ("ldy #%o", C);
+    asm ("lda (sp),y");
+    asm ("lsr a");
+    asm ("lsr a");
+    asm ("lsr a");
+    asm ("tax");
+    asm ("lda (sp),y");
+    asm ("and #$07");
+    asm ("tay");
+    asm ("lda %v,y", Bits);
+    asm ("and %v,x", CharSet);
+    asm ("ldx #$00");
+    return __AX__;
+}
+
+
+
 /*****************************************************************************/
 /*                                  Code                                    */
 /*****************************************************************************/
@@ -126,7 +173,6 @@ static void ReadInt (unsigned char Base)
     /* Read the value */
     IntVal = 0;
     while (isxdigit (C) && Width-- > 0) {
-       printf ("ReadInt: '%c'\n", C);
                IntVal = IntVal * Base + HexVal (C);
        ReadChar ();
     }
@@ -168,7 +214,9 @@ int _scanf (struct indesc* D_, 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 Base;                /* Integer base in %i */
+    unsigned char HaveWidth;   /* True if a width was given */
+    char          Start;        /* Start of range */
 
     /* Place copies of the arguments into global variables. This is not very
      * nice, but on a 6502 platform it gives better code, since the values
@@ -185,7 +233,6 @@ int _scanf (struct indesc* D_, const char* format, va_list ap_)
      * is reached.
      */
     Result = setjmp (JumpBuf);
-    printf ("Result = %u\n", Result);
     if (Result == RC_OK) {
 
 Again:
@@ -198,71 +245,71 @@ Again:
            /* Check for a conversion */
            if (F != '%' || *format == '%') {
 
-               /* %% or any char other than % */
-               if (F == '%') {
-                   ++format;
-               }
+               /* %% or any char other than % */
+               if (F == '%') {
+                   ++format;
+               }
 
-               /* Check for a match */
-               if (isspace (F)) {
+               /* 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.
-                    */
-                   SkipWhite ();
-                   continue;
+                   /* Special white space handling: Any whitespace matches
+                    * any amount of whitespace including none(!). So this
+                    * match will never fail.
+                    */
+                   SkipWhite ();
+                   continue;
 
-               } else if (F != C) {
+               } else if (F != C) {
 
-                   /* A mismatch. We will stop scanning the input and return
-                    * the number of conversions.
-                    */
-                   printf ("F = '%c', C = '%c' --> mismatch\n", F, C);
-                   return Conversions;
+                   /* A mismatch. We will stop scanning the input and return
+                    * the number of conversions.
+                    */
+                   return Conversions;
 
-               } else {
+               } else {
 
-                   /* A match. Read the next input character and start over */
-                   goto Again;
+                   /* A match. Read the next input character and start over */
+                   goto Again;
 
-               }
+               }
 
            } else {
 
-               /* A conversion. Skip the percent sign. */
-               F = *format++;
+               /* A conversion. Skip the percent sign. */
+               F = *format++;
 
                /* Initialize variables */
-               NoAssign    = 0;
-               IsShort     = 0;
-               IsLong      = 0;
-               Width       = UINT_MAX;
-
-               /* Check for flags. */
-               while (1) {
-                   if (isdigit (F)) {
-                       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++;
-                   }
-               }
+               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:
 
                /* Check for the actual conversion character */
-               printf ("F = '%c'\n", F);
                switch (F) {
 
                    case 'D':
@@ -288,8 +335,8 @@ FlagsDone:
                                case 'x':
                                case 'X':
                                    Base = 16;
-                                   ReadChar();
-                                   break;
+                                   ReadChar();
+                                   break;
                                default:
                                    Base = 8;
                            }
@@ -305,7 +352,7 @@ FlagsDone:
 
                    case 'o':
                        /* Unsigned octal integer */
-                       SkipWhite ();
+                       SkipWhite ();
                        ReadInt (8);
                        AssignInt ();
                        break;
@@ -330,41 +377,144 @@ FlagsDone:
                    case 'f':
                    case 'g':
                        /* Optionally signed float */
+                       longjmp (JumpBuf, RC_NOCONV);
                        break;
 
                    case 's':
                        /* Whitespace terminated string */
                        SkipWhite ();
-                       S = NoAssign? 0 : va_arg (ap, char*);
-                       while (C && !isspace (C) && Width--) {
-                           if (S) {
-                               *S++ = C;
+                       if (!NoAssign) {
+                           S = va_arg (ap, char*);
+                       }
+                               while (!isspace (C) && Width--) {
+                           if (!NoAssign) {
+                               *S++ = C;
                            }
                            ReadChar ();
                        }
-                       break;
+                       /* Terminate the string just read */
+                       if (!NoAssign) {
+                           *S = '\0';
+                       }
+                        ++Conversions;
+                       break;
 
                    case 'c':
-                       /* Fixed length string */
+                       /* Fixed length string, NOT zero terminated */
+                       if (!HaveWidth) {
+                           /* No width given, default is 1 */
+                           Width = 1;
+                       }
+                       if (!NoAssign) {
+                           S = va_arg (ap, char*);
+                            while (Width--) {
+                                *S++ = C;
+                                ReadChar ();
+                            }
+                       } else {
+                            /* Just skip as many chars as given */
+                            while (Width--) {
+                                ReadChar ();
+                            }
+                        }
+                       ++Conversions;
                        break;
 
                    case '[':
                        /* String using characters from a set */
+                        Invert = 0;
+                        /* Clear the set */
+                        memset (CharSet, 0, sizeof (CharSet));
+                        F = *format++;
+                        if (F == '^') {
+                            Invert = 1;
+                            F = *format++;
+                        }
+                        if (F == ']') {
+                            AddCharToSet (']');
+                            F = *format++;
+                        }
+                        /* Read the characters that are part of the set */
+                        while (F != ']' && F != '\0') {
+                            if (*format == '-') {
+                                /* A range. Get start and end, skip the '-' */
+                                Start = F;
+                                F = *++format;
+                                ++format;
+                                if (F == ']') {
+                                    /* '-' as last char means: include '-' */
+                                    AddCharToSet (Start);
+                                    AddCharToSet ('-');
+                                } else if (F != '\0') {
+                                    /* Include all chars in the range */
+                                    while (1) {
+                                        AddCharToSet (Start);
+                                        if (Start == F) {
+                                            break;
+                                        }
+                                        ++Start;
+                                    }
+                                    /* Get next char after range */
+                                    F = *format++;
+                                }
+                            } else {
+                                /* Just a character */
+                                AddCharToSet (F);
+                                /* Get next char */
+                                F = *format++;
+                            }
+                        }
+
+                        /* Invert the set if requested */
+                        if (Invert) {
+                            for (Start = 0; Start < sizeof (CharSet); ++Start) {
+                                CharSet[Start] ^= 0xFF;
+                            }
+                        }
+
+                        /* We have the set in CharSet. Read characters and
+                         * store them into a string while they are part of
+                         * the set.
+                         */
+                       if (!NoAssign) {
+                           S = va_arg (ap, char*);
+                            while (IsCharInSet (C) && Width--) {
+                                *S++ = C;
+                                ReadChar ();
+                            }
+                            *S = '\0';
+                        } else {
+                            while (IsCharInSet (C) && Width--) {
+                                ReadChar ();
+                            }
+                        }
+                        ++Conversions;
                        break;
 
                    case 'p':
-                       /* Pointer */
+                       /* Pointer, format is 0xABCD */
+                       SkipWhite ();
+                       if (C != '0') {
+                            longjmp (JumpBuf, RC_NOCONV);
+                        }
+                       ReadChar ();
+                        if (C != 'x' && C != 'X') {
+                            longjmp (JumpBuf, RC_NOCONV);
+                        }
+                        ReadChar ();
+                        ReadInt (16);
+                       AssignInt ();
                        break;
 
                    case 'n':
                        /* Store characters consumed so far */
                        IntVal = D->ccount;
-                       IsLong = 0;
                        AssignInt ();
                        break;
 
                    default:
                        /* Invalid conversion */
+                       longjmp (JumpBuf, RC_NOCONV);
                        break;
 
                }