]> git.sur5r.net Git - cc65/blobdiff - src/ld65/scanner.c
Merge remote-tracking branch 'upstream/master' into a5200
[cc65] / src / ld65 / scanner.c
index 7b0770016335d41fdc77c3ad0880ed7e07327ff1..0f6ea58de4240b2090abfc916f2dcb70aaf38ba4 100644 (file)
@@ -1,15 +1,15 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                scanner.c                                 */
+/*                                 scanner.c                                 */
 /*                                                                           */
-/*             Configuration file scanner for the ld65 linker               */
+/*              Configuration file scanner for the ld65 linker               */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2000 Ullrich von Bassewitz                                       */
-/*               Wacholderweg 14                                             */
-/*               D-70597 Stuttgart                                           */
-/* EMail:        uz@musoftware.de                                            */
+/* (C) 1998-2013, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 #include <ctype.h>
 
 /* common */
+#include "chartype.h"
+#include "strbuf.h"
 #include "xsprintf.h"
 
 /* ld65 */
 #include "global.h"
 #include "error.h"
 #include "scanner.h"
+#include "spool.h"
 
 
 
 /*****************************************************************************/
-/*                                          Data                                    */
+/*                                   Data                                    */
 /*****************************************************************************/
 
 
 
 /* Current token and attributes */
-cfgtok_t       CfgTok;
-char                   CfgSVal [CFG_MAX_IDENT_LEN+1];
+cfgtok_t        CfgTok;
+StrBuf          CfgSVal = STATIC_STRBUF_INITIALIZER;
 unsigned long   CfgIVal;
 
 /* Error location */
-unsigned               CfgErrorLine;
-unsigned               CfgErrorCol;
+FilePos                 CfgErrorPos;
 
-/* Input sources for the configuration */
-static const char*             CfgName         = 0;
-static const char*      CfgBuf                 = 0;
+/* Input source for the configuration */
+static const char*      CfgName         = 0;
 
 /* Other input stuff */
-static int                     C               = ' ';
-static unsigned                InputLine       = 1;
-static unsigned                InputCol        = 0;
-static FILE*                   InputFile       = 0;
+static int              C               = ' ';
+static FilePos          InputPos;
+static FILE*            InputFile       = 0;
 
 
 
 /*****************************************************************************/
-/*                             Error handling                               */
+/*                              Error handling                               */
 /*****************************************************************************/
 
 
 
-void CfgWarning (const char* Format, ...)
-/* Print a warning message adding file name and line number of the config file */
+void CfgWarning (const FilePos* Pos, const char* Format, ...)
+/* Print a warning message adding file name and line number of a given file */
 {
-    char Buf [512];
+    StrBuf Buf = STATIC_STRBUF_INITIALIZER;
     va_list ap;
 
     va_start (ap, Format);
-    xvsprintf (Buf, sizeof (Buf), Format, ap);
+    SB_VPrintf (&Buf, Format, ap);
     va_end (ap);
 
-    Warning ("%s(%u): %s", CfgName, CfgErrorLine, Buf);
+    Warning ("%s(%u): %s",
+             GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf));
+    SB_Done (&Buf);
 }
 
 
 
-void CfgError (const char* Format, ...)
-/* Print an error message adding file name and line number of the config file */
+void CfgError (const FilePos* Pos, const char* Format, ...)
+/* Print an error message adding file name and line number of a given file */
 {
-    char Buf [512];
+    StrBuf Buf = STATIC_STRBUF_INITIALIZER;
     va_list ap;
 
     va_start (ap, Format);
-    xvsprintf (Buf, sizeof (Buf), Format, ap);
+    SB_VPrintf (&Buf, Format, ap);
     va_end (ap);
 
-    Error ("%s(%u): %s", CfgName, CfgErrorLine, Buf);
+    Error ("%s(%u): %s",
+           GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf));
+    SB_Done (&Buf);
 }
 
 
 
 /*****************************************************************************/
-/*                                          Code                                    */
+/*                                   Code                                    */
 /*****************************************************************************/
 
 
@@ -121,28 +125,18 @@ void CfgError (const char* Format, ...)
 static void NextChar (void)
 /* Read the next character from the input file */
 {
-    if (CfgBuf) {
-       /* Read from buffer */
-       C = (unsigned char)(*CfgBuf);
-       if (C == 0) {
-           C = EOF;
-       } else {
-           ++CfgBuf;
-       }
-    } else {
-       /* Read from the file */
-       C = getc (InputFile);
-    }
+    /* Read from the file */
+    C = getc (InputFile);
 
     /* Count columns */
     if (C != EOF) {
-       ++InputCol;
+        ++InputPos.Col;
     }
 
     /* Count lines */
     if (C == '\n') {
-       ++InputLine;
-       InputCol = 0;
+        ++InputPos.Line;
+        InputPos.Col = 0;
     }
 }
 
@@ -152,170 +146,250 @@ static unsigned DigitVal (int C)
 /* Return the value for a numeric digit */
 {
     if (isdigit (C)) {
-       return C - '0';
+        return C - '0';
     } else {
-       return toupper (C) - 'A' + 10;
+        return toupper (C) - 'A' + 10;
     }
 }
 
 
 
-void CfgNextTok (void)
-/* Read the next token from the input stream */
+static void StrVal (void)
+/* Parse a string value and expand escape sequences */
 {
-    unsigned I;
+    /* Skip the starting double quotes */
+    NextChar ();
+
+    /* Read input chars */
+    SB_Clear (&CfgSVal);
+    while (C != '\"') {
+        switch (C) {
+
+            case EOF:
+            case '\n':
+                CfgError (&CfgErrorPos, "Unterminated string");
+                break;
+
+            case '%':
+                NextChar ();
+                switch (C) {
+
+                    case EOF:
+                    case '\n':
+                    case '\"':
+                        CfgError (&CfgErrorPos, "Unterminated '%%' escape sequence");
+                        break;
+
+                    case '%':
+                        SB_AppendChar (&CfgSVal, '%');
+                        NextChar ();
+                        break;
+
+                    case 'O':
+                        /* Replace by output file */
+                        if (OutputName) {
+                            SB_AppendStr (&CfgSVal, OutputName);
+                        }
+                        OutputNameUsed = 1;
+                        NextChar ();
+                        break;
+
+                    default:
+                        CfgWarning (&CfgErrorPos,
+                                    "Unkown escape sequence `%%%c'", C);
+                        SB_AppendChar (&CfgSVal, '%');
+                        SB_AppendChar (&CfgSVal, C);
+                        NextChar ();
+                        break;
+                }
+                break;
+
+            default:
+                SB_AppendChar (&CfgSVal, C);
+                NextChar ();
+        }
+    }
+
+    /* Skip the terminating double quotes */
+    NextChar ();
+
+    /* Terminate the string */
+    SB_Terminate (&CfgSVal);
+
+    /* We've read a string value */
+    CfgTok = CFGTOK_STRCON;
+}
+
 
 
+void CfgNextTok (void)
+/* Read the next token from the input stream */
+{
 Again:
     /* Skip whitespace */
     while (isspace (C)) {
-       NextChar ();
+        NextChar ();
     }
 
     /* Remember the current position */
-    CfgErrorLine = InputLine;
-    CfgErrorCol  = InputCol;
+    CfgErrorPos = InputPos;
 
     /* Identifier? */
-    if (C == '_' || isalpha (C)) {
-
-       /* Read the identifier */
-       I = 0;
-       while (C == '_' || isalnum (C)) {
-           if (I < CFG_MAX_IDENT_LEN) {
-               CfgSVal [I++] = C;
-           }
-           NextChar ();
-       }
-       CfgSVal [I] = '\0';
-       CfgTok = CFGTOK_IDENT;
-       return;
+    if (C == '_' || IsAlpha (C)) {
+
+        /* Read the identifier */
+        SB_Clear (&CfgSVal);
+        while (C == '_' || IsAlNum (C)) {
+            SB_AppendChar (&CfgSVal, C);
+            NextChar ();
+        }
+        SB_Terminate (&CfgSVal);
+        CfgTok = CFGTOK_IDENT;
+        return;
     }
 
     /* Hex number? */
     if (C == '$') {
-       NextChar ();
-       if (!isxdigit (C)) {
-           Error ("%s(%u): Hex digit expected", CfgName, InputLine);
-       }
-       CfgIVal = 0;
-       while (isxdigit (C)) {
-                   CfgIVal = CfgIVal * 16 + DigitVal (C);
-           NextChar ();
-       }
-       CfgTok = CFGTOK_INTCON;
-       return;
+        NextChar ();
+        if (!isxdigit (C)) {
+            CfgError (&CfgErrorPos, "Hex digit expected");
+        }
+        CfgIVal = 0;
+        while (isxdigit (C)) {
+            CfgIVal = CfgIVal * 16 + DigitVal (C);
+            NextChar ();
+        }
+        CfgTok = CFGTOK_INTCON;
+        return;
     }
 
     /* Decimal number? */
     if (isdigit (C)) {
-       CfgIVal = 0;
-       while (isdigit (C)) {
-                   CfgIVal = CfgIVal * 10 + DigitVal (C);
-           NextChar ();
-       }
-       CfgTok = CFGTOK_INTCON;
-       return;
+        CfgIVal = 0;
+        while (isdigit (C)) {
+            CfgIVal = CfgIVal * 10 + DigitVal (C);
+            NextChar ();
+        }
+        CfgTok = CFGTOK_INTCON;
+        return;
     }
 
     /* Other characters */
     switch (C) {
 
-       case '{':
-           NextChar ();
-           CfgTok = CFGTOK_LCURLY;
-           break;
-
-       case '}':
-           NextChar ();
-           CfgTok = CFGTOK_RCURLY;
-           break;
-
-       case ';':
-           NextChar ();
-           CfgTok = CFGTOK_SEMI;
-           break;
-
-       case '.':
-           NextChar ();
-           CfgTok = CFGTOK_DOT;
-           break;
-
-       case ',':
-           NextChar ();
-           CfgTok = CFGTOK_COMMA;
-           break;
-
-       case '=':
-           NextChar ();
-           CfgTok = CFGTOK_EQ;
-           break;
+        case '-':
+            NextChar ();
+            CfgTok = CFGTOK_MINUS;
+            break;
+
+        case '+':
+            NextChar ();
+            CfgTok = CFGTOK_PLUS;
+            break;
+
+        case '*':
+            NextChar ();
+            CfgTok = CFGTOK_MUL;
+            break;
+
+        case '/':
+            NextChar ();
+            CfgTok = CFGTOK_DIV;
+            break;
+
+        case '(':
+            NextChar ();
+            CfgTok = CFGTOK_LPAR;
+            break;
+
+        case ')':
+            NextChar ();
+            CfgTok = CFGTOK_RPAR;
+            break;
+
+        case '{':
+            NextChar ();
+            CfgTok = CFGTOK_LCURLY;
+            break;
+
+        case '}':
+            NextChar ();
+            CfgTok = CFGTOK_RCURLY;
+            break;
+
+        case ';':
+            NextChar ();
+            CfgTok = CFGTOK_SEMI;
+            break;
+
+        case '.':
+            NextChar ();
+            CfgTok = CFGTOK_DOT;
+            break;
+
+        case ',':
+            NextChar ();
+            CfgTok = CFGTOK_COMMA;
+            break;
+
+        case '=':
+            NextChar ();
+            CfgTok = CFGTOK_EQ;
+            break;
 
         case ':':
-           NextChar ();
-           CfgTok = CFGTOK_COLON;
-           break;
+            NextChar ();
+            CfgTok = CFGTOK_COLON;
+            break;
 
         case '\"':
-           NextChar ();
-           I = 0;
-           while (C != '\"') {
-               if (C == EOF || C == '\n') {
-                   Error ("%s(%u): Unterminated string", CfgName, InputLine);
-               }
-               if (I < CFG_MAX_IDENT_LEN) {
-                   CfgSVal [I++] = C;
-               }
-               NextChar ();
-           }
-                   NextChar ();
-           CfgSVal [I] = '\0';
-           CfgTok = CFGTOK_STRCON;
-           break;
+            StrVal ();
+            break;
 
         case '#':
-           /* Comment */
-           while (C != '\n' && C != EOF) {
-               NextChar ();
-           }
-           if (C != EOF) {
-               goto Again;
-           }
-           CfgTok = CFGTOK_EOF;
-           break;
+            /* Comment */
+            while (C != '\n' && C != EOF) {
+                NextChar ();
+            }
+            if (C != EOF) {
+                goto Again;
+            }
+            CfgTok = CFGTOK_EOF;
+            break;
 
         case '%':
-           NextChar ();
-           switch (C) {
-
-               case 'O':
-                   NextChar ();
-                   if (OutputName) {
-                       strncpy (CfgSVal, OutputName, CFG_MAX_IDENT_LEN);
-                       CfgSVal [CFG_MAX_IDENT_LEN] = '\0';
-                   } else {
-                       CfgSVal [0] = '\0';
-                   }
-                   CfgTok = CFGTOK_STRCON;
-                   break;
-
-               case 'S':
-                   NextChar ();
-                   CfgIVal = StartAddr;
-                   CfgTok = CFGTOK_INTCON;
-                   break;
-
-               default:
-                   CfgError ("Invalid format specification");
-           }
-           break;
+            NextChar ();
+            switch (C) {
+
+                case 'O':
+                    NextChar ();
+                    if (OutputName) {
+                        SB_CopyStr (&CfgSVal, OutputName);
+                    } else {
+                        SB_Clear (&CfgSVal);
+                    }
+                    SB_Terminate (&CfgSVal);
+                    OutputNameUsed = 1;
+                    CfgTok = CFGTOK_STRCON;
+                    break;
+
+                case 'S':
+                    NextChar ();
+                    CfgIVal = StartAddr;
+                    CfgTok = CFGTOK_INTCON;
+                    break;
+
+                default:
+                    CfgError (&CfgErrorPos, "Invalid format specification");
+            }
+            break;
 
         case EOF:
-           CfgTok = CFGTOK_EOF;
-           break;
+            CfgTok = CFGTOK_EOF;
+            break;
 
-       default:
-           Error ("%s(%u): Invalid character `%c'", CfgName, InputLine, C);
+        default:
+            CfgError (&CfgErrorPos, "Invalid character `%c'", C);
 
     }
 }
@@ -326,7 +400,7 @@ void CfgConsume (cfgtok_t T, const char* Msg)
 /* Skip a token, print an error message if not found */
 {
     if (CfgTok != T) {
-               CfgError (Msg);
+        CfgError (&CfgErrorPos, "%s", Msg);
     }
     CfgNextTok ();
 }
@@ -353,7 +427,7 @@ void CfgOptionalComma (void)
 /* Consume a comma if there is one */
 {
     if (CfgTok == CFGTOK_COMMA) {
-               CfgNextTok ();
+        CfgNextTok ();
     }
 }
 
@@ -363,7 +437,7 @@ void CfgOptionalAssign (void)
 /* Consume an equal sign if there is one */
 {
     if (CfgTok == CFGTOK_EQ) {
-               CfgNextTok ();
+        CfgNextTok ();
     }
 }
 
@@ -373,7 +447,7 @@ void CfgAssureInt (void)
 /* Make sure the next token is an integer */
 {
     if (CfgTok != CFGTOK_INTCON) {
-               CfgError ("Integer constant expected");
+        CfgError (&CfgErrorPos, "Integer constant expected");
     }
 }
 
@@ -383,7 +457,7 @@ void CfgAssureStr (void)
 /* Make sure the next token is a string constant */
 {
     if (CfgTok != CFGTOK_STRCON) {
-               CfgError ("String constant expected");
+        CfgError (&CfgErrorPos, "String constant expected");
     }
 }
 
@@ -393,7 +467,7 @@ void CfgAssureIdent (void)
 /* Make sure the next token is an identifier */
 {
     if (CfgTok != CFGTOK_IDENT) {
-               CfgError ("Identifier expected");
+        CfgError (&CfgErrorPos, "Identifier expected");
     }
 }
 
@@ -403,7 +477,7 @@ void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
 /* Check the range of CfgIVal */
 {
     if (CfgIVal < Lo || CfgIVal > Hi) {
-       CfgError ("Range error");
+        CfgError (&CfgErrorPos, "Range error");
     }
 }
 
@@ -417,25 +491,21 @@ void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
     /* We need an identifier */
     if (CfgTok == CFGTOK_IDENT) {
 
-       /* Make it upper case */
-       I = 0;
-       while (CfgSVal [I]) {
-           CfgSVal [I] = toupper (CfgSVal [I]);
-           ++I;
-       }
-
-               /* Linear search */
-       for (I = 0; I < Size; ++I) {
-           if (strcmp (CfgSVal, Table [I].Ident) == 0) {
-               CfgTok = Table [I].Tok;
-               return;
-           }
-       }
+        /* Make it upper case */
+        SB_ToUpper (&CfgSVal);
+
+        /* Linear search */
+        for (I = 0; I < Size; ++I) {
+            if (SB_CompareStr (&CfgSVal, Table[I].Ident) == 0) {
+                CfgTok = Table[I].Tok;
+                return;
+            }
+        }
 
     }
 
     /* Not found or no identifier */
-    Error ("%s(%u): %s expected", CfgName, InputLine, Name);
+    CfgError (&CfgErrorPos, "%s expected", Name);
 }
 
 
@@ -444,21 +514,21 @@ void CfgBoolToken (void)
 /* Map an identifier or integer to a boolean token */
 {
     static const IdentTok Booleans [] = {
-               {   "YES",      CFGTOK_TRUE     },
-       {   "NO",       CFGTOK_FALSE    },
+        {   "YES",      CFGTOK_TRUE     },
+        {   "NO",       CFGTOK_FALSE    },
         {   "TRUE",     CFGTOK_TRUE     },
         {   "FALSE",    CFGTOK_FALSE    },
     };
 
     /* If we have an identifier, map it to a boolean token */
     if (CfgTok == CFGTOK_IDENT) {
-       CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
+        CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
     } else {
-       /* We expected an integer here */
-       if (CfgTok != CFGTOK_INTCON) {
-           CfgError ("Boolean value expected");
-       }
-       CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
+        /* We expected an integer here */
+        if (CfgTok != CFGTOK_INTCON) {
+            CfgError (&CfgErrorPos, "Boolean value expected");
+        }
+        CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
     }
 }
 
@@ -472,26 +542,10 @@ void CfgSetName (const char* Name)
 
 
 
-const char* CfgGetName (void)
-/* Get the name of the config file */
-{
-    return CfgName? CfgName : "";
-}
-
-
-
-void CfgSetBuf (const char* Buf)
-/* Set a memory buffer for the config */
-{
-    CfgBuf = Buf;
-}
-
-
-
 int CfgAvail (void)
 /* Return true if we have a configuration available */
 {
-    return CfgName != 0 || CfgBuf != 0;
+    return CfgName != 0;
 }
 
 
@@ -499,23 +553,17 @@ int CfgAvail (void)
 void CfgOpenInput (void)
 /* Open the input file if we have one */
 {
-    /* If we have a config name given, open the file, otherwise we will read
-     * from a buffer.
-     */
-    if (!CfgBuf) {
-
-       /* Open the file */
-       InputFile = fopen (CfgName, "r");
-       if (InputFile == 0) {
-           Error ("Cannot open `%s': %s", CfgName, strerror (errno));
-       }
-
+    /* Open the file */
+    InputFile = fopen (CfgName, "r");
+    if (InputFile == 0) {
+        Error ("Cannot open `%s': %s", CfgName, strerror (errno));
     }
 
     /* Initialize variables */
     C         = ' ';
-    InputLine = 1;
-    InputCol  = 0;
+    InputPos.Line = 1;
+    InputPos.Col  = 0;
+    InputPos.Name = GetStringId (CfgName);
 
     /* Start the ball rolling ... */
     CfgNextTok ();
@@ -529,10 +577,6 @@ void CfgCloseInput (void)
     /* Close the input file if we had one */
     if (InputFile) {
         (void) fclose (InputFile);
-       InputFile = 0;
+        InputFile = 0;
     }
 }
-
-
-
-