]> git.sur5r.net Git - cc65/blobdiff - src/cc65/preproc.c
Fixed initializers for standard behaviour (hopefully ok now)
[cc65] / src / cc65 / preproc.c
index 321b3b1756082756b40c2e65e69088bcf1deeb70..d835c075b50d1ffb7bfaecd736a878fdbd1d3e6d 100644 (file)
@@ -5,10 +5,15 @@
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
-#include <ctype.h>
 
-#include "../common/xmalloc.h"
+/* common */
+#include "chartype.h"
+#include "check.h"
+#include "inline.h"
+#include "print.h"
+#include "xmalloc.h"
 
+/* cc65 */
 #include "codegen.h"
 #include "error.h"
 #include "expr.h"
@@ -16,6 +21,7 @@
 #include "ident.h"
 #include "incpath.h"
 #include "input.h"
+#include "lineinfo.h"
 #include "macrotab.h"
 #include "scanner.h"
 #include "util.h"
@@ -35,55 +41,166 @@ static int Pass1 (const char* From, char* To);
 
 
 /*****************************************************************************/
-/*                                  data                                    */
+/*                                  Data                                    */
 /*****************************************************************************/
 
 
 
-/* Set when the pp calls expr() recursively */
+/* Set when the preprocessor calls expr() recursively */
 unsigned char Preprocessing = 0;
 
 /* Management data for #if */
-#define N_IFDEF                16
-static int i_ifdef = -1;
-static char s_ifdef[N_IFDEF];
+#define MAX_IFS         64
+#define IFCOND_NONE     0x00U
+#define IFCOND_SKIP     0x01U
+#define IFCOND_ELSE     0x02U
+#define IFCOND_NEEDTERM 0x04U
+static unsigned char IfStack[MAX_IFS];
+static int           IfIndex = -1;
 
 /* Buffer for macro expansion */
 static char mlinebuf [LINESIZE];
 static char* mline = mlinebuf;
 static char* mptr;
 
-/* Flag: Expand macros in this line */
-static int ExpandMacros = 1;
+
+
+/*****************************************************************************/
+/*                  Low level preprocessor token handling                   */
+/*****************************************************************************/
+
+
+
+/* Types of preprocessor tokens */
+typedef enum {
+    PP_DEFINE,
+    PP_ELIF,
+    PP_ELSE,
+    PP_ENDIF,
+    PP_ERROR,
+    PP_IF,
+    PP_IFDEF,
+    PP_IFNDEF,
+    PP_INCLUDE,
+    PP_LINE,
+    PP_PRAGMA,
+    PP_UNDEF,
+    PP_ILLEGAL
+} pptoken_t;
+
+
+
+/* Preprocessor keyword to token mapping table */
+static const struct PPToken {
+    const char*        Key;            /* Keyword */
+    pptoken_t          Tok;            /* Token */
+} PPTokens[] = {
+    {          "define",       PP_DEFINE       },
+    {   "elif",         PP_ELIF         },
+    {          "else",         PP_ELSE         },
+    {          "endif",        PP_ENDIF        },
+    {          "error",        PP_ERROR        },
+    {          "if",           PP_IF           },
+    {          "ifdef",        PP_IFDEF        },
+    {          "ifndef",       PP_IFNDEF       },
+    {          "include",      PP_INCLUDE      },
+    {   "line",                PP_LINE         },
+    {          "pragma",       PP_PRAGMA       },
+    {          "undef",        PP_UNDEF        },
+};
+
+/* Number of preprocessor tokens */
+#define PPTOKEN_COUNT          (sizeof(PPTokens) / sizeof(PPTokens[0]))
+
+
+
+static int CmpToken (const void* Key, const void* Elem)
+/* Compare function for bsearch */
+{
+    return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
+}
+
+
+
+static pptoken_t FindPPToken (const char* Ident)
+/* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
+ * is not a valid preprocessor token.
+ */
+{
+    struct PPToken* P;
+    P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
+    return P? P->Tok : PP_ILLEGAL;
+}
 
 
 
 /*****************************************************************************/
-/*                                  code                                    */
+/*                                  Code                                    */
 /*****************************************************************************/
 
 
 
-static int keepch (char c)
+#ifdef HAVE_INLINE
+INLINE void KeepChar (char c)
 /* Put character c into translation buffer. */
 {
-    return (*mptr++ = c);
+    *mptr++ = c;
 }
+#else
+#define KeepChar(c)     *mptr++ = (c)
+#endif
 
 
 
-static void keepstr (const char* S)
+static void KeepStr (const char* S)
 /* Put string str into translation buffer. */
 {
+    unsigned Len = strlen (S);
+    memcpy (mptr, S, Len);
+    mptr += Len;
+}
+
+
+
+static void Stringize (const char* S)
+/* Stringize the given string: Add double quotes at start and end and preceed
+ * each occurance of " and \ by a backslash.
+ */
+{
+    KeepChar ('\"');
+    /* Replace any characters inside the string may not be part of a string
+     * unescaped.
+     */
     while (*S) {
-       keepch (*S++);
+        switch (*S) {
+            case '\"':
+            case '\\':
+                KeepChar ('\\');
+            /* FALLTHROUGH */
+            default:
+                KeepChar (*S);
+                break;
+        }
+        ++S;
     }
+    KeepChar ('\"');
+}
+
+
+
+static void SwapLineBuffers (void)
+/* Swap both line buffers */
+{
+    /* Swap mline and line */
+    char* p = line;
+    line = mline;
+    mline = p;
 }
 
 
 
-static void Comment (void)
-/* Remove a C comment from line. */
+static void OldStyleComment (void)
+/* Remove an old style C comment from line. */
 {
     /* Remember the current line number, so we can output better error
      * messages if the comment is not terminated in the current file.
@@ -98,12 +215,13 @@ static void Comment (void)
     while (CurC != '*' || NextC != '/') {
        if (CurC == '\0') {
            if (NextLine () == 0) {
-               PPError (ERR_EOF_IN_COMMENT, StartingLine);
+               PPError ("End-of-file reached in comment starting at line %u",
+                        StartingLine);
                return;
            }
        } else {
            if (CurC == '/' && NextC == '*') {
-               PPWarning (WARN_NESTED_COMMENT);
+               PPWarning ("`/*' found inside a comment");
            }
            NextChar ();
        }
@@ -116,6 +234,24 @@ static void Comment (void)
 
 
 
+static void NewStyleComment (void)
+/* Remove a new style C comment from line. */
+{
+    /* Beware: Because line continuation chars are handled when reading
+     * lines, we may only skip til the end of the source line, which
+     * may not be the same as the end of the input line. The end of the
+     * source line is denoted by a lf (\n) character.
+     */
+    do {
+       NextChar ();
+    } while (CurC != '\n' && CurC != '\0');
+    if (CurC == '\n') {
+       NextChar ();
+    }
+}
+
+
+
 static void SkipBlank (void)
 /* Skip blanks and tabs in the input stream. */
 {
@@ -139,7 +275,7 @@ static char* CopyQuotedString (char* Target)
     /* Copy the characters inside the string */
     while (CurC != '\0' && CurC != Quote) {
                /* Keep an escaped char */
-       if (CurC == '\\') {
+       if (CurC == '\\') {
            *Target++ = CurC;
            NextChar ();
        }
@@ -161,18 +297,20 @@ static char* CopyQuotedString (char* Target)
 
 
 /*****************************************************************************/
-/*                               Macro stuff                                */
+/*                               Macro stuff                                */
 /*****************************************************************************/
 
 
 
 static int MacName (char* Ident)
-/* Get macro symbol name.  If error, print message and clear line. */
+/* Get a macro symbol name into Ident.  If we have an error, print a
+ * diagnostic message and clear the line.
+ */
 {
     if (IsSym (Ident) == 0) {
-       PPError (ERR_IDENT_EXPECTED);
+       PPError ("Identifier expected");
        ClearLine ();
-       return 0;
+       return 0;
     } else {
        return 1;
     }
@@ -181,7 +319,7 @@ static int MacName (char* Ident)
 
 
 static void ExpandMacroArgs (Macro* M)
-/* Preprocessor pass 2.  Perform macro substitution. */
+/* Expand the arguments of a macro */
 {
     ident      Ident;
     const char* Replacement;
@@ -193,34 +331,34 @@ static void ExpandMacroArgs (Macro* M)
 
     /* Copy the macro replacement checking for parameters to replace */
     while (CurC != '\0') {
-       /* If the next token is an identifier, check for a macro arg */
+       /* If the next token is an identifier, check for a macro arg */
        if (IsIdent (CurC)) {
            SymName (Ident);
-           Replacement = FindMacroArg (M, Ident);
-           if (Replacement) {
-               /* Macro arg, keep the replacement */
-               keepstr (Replacement);
+           Replacement = FindMacroArg (M, Ident);
+           if (Replacement) {
+               /* Macro arg, keep the replacement */
+               KeepStr (Replacement);
            } else {
-               /* No macro argument, keep the original identifier */
-               keepstr (Ident);
+               /* No macro argument, keep the original identifier */
+               KeepStr (Ident);
            }
        } else if (CurC == '#' && IsIdent (NextC)) {
                    NextChar ();
            SymName (Ident);
-           Replacement = FindMacroArg (M, Ident);
+           Replacement = FindMacroArg (M, Ident);
                    if (Replacement) {
-               keepch ('\"');
-               keepstr (Replacement);
-               keepch ('\"');
+                /* Make a valid string from Replacement */
+                Stringize (Replacement);
            } else {
-               keepch ('#');
-               keepstr (Ident);
+                /* No replacement - keep the input */
+               KeepChar ('#');
+               KeepStr (Ident);
            }
-       } else if (IsQuoteChar (CurC)) {
+       } else if (IsQuote (CurC)) {
            mptr = CopyQuotedString (mptr);
        } else {
-           *mptr++ = CurC;
-           NextChar ();
+           KeepChar (CurC);
+           NextChar ();
        }
     }
 
@@ -233,7 +371,7 @@ static void ExpandMacroArgs (Macro* M)
 static int MacroCall (Macro* M)
 /* Process a function like macro */
 {
-    unsigned   ArgCount;       /* Macro argument count */
+    int         ArgCount;              /* Macro argument count */
     unsigned   ParCount;       /* Number of open parenthesis */
     char       Buf[LINESIZE];  /* Argument buffer */
     const char* ArgStart;
@@ -242,7 +380,7 @@ static int MacroCall (Macro* M)
     /* Expect an argument list */
     SkipBlank ();
     if (CurC != '(') {
-       PPError (ERR_ILLEGAL_MACRO_CALL);
+       PPError ("Illegal macro call");
        return 0;
     }
 
@@ -258,68 +396,74 @@ static int MacroCall (Macro* M)
     B       = Buf;
     while (1) {
                if (CurC == '(') {
-           /* Nested parenthesis */
+           /* Nested parenthesis */
            *B++ = CurC;
-           NextChar ();
+           NextChar ();
            ++ParCount;
-       } else if (IsQuoteChar (CurC)) {
+       } else if (IsQuote (CurC)) {
            B = CopyQuotedString (B);
        } else if (CurC == ',' || CurC == ')') {
            if (ParCount == 0) {
-               /* End of actual argument */
+               /* End of actual argument */
                *B++ = '\0';
-               while (IsBlank(*ArgStart)) {
-                   ++ArgStart;
-               }
+               while (IsBlank(*ArgStart)) {
+                   ++ArgStart;
+               }
                if (ArgCount < M->ArgCount) {
                    M->ActualArgs[ArgCount++] = ArgStart;
                        } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
-                   /* Be sure not to count the single empty argument for a
-                    * macro that does not have arguments.
-                    */
+                   /* Be sure not to count the single empty argument for a
+                    * macro that does not have arguments.
+                    */
                    ++ArgCount;
-               }
+               }
 
-               /* Check for end of macro param list */
-               if (CurC == ')') {
-                   NextChar ();
-                   break;
-               }
+               /* Check for end of macro param list */
+               if (CurC == ')') {
+                   NextChar ();
+                   break;
+               }
 
                        /* Start the next param */
                ArgStart = B;
-               NextChar ();
+               NextChar ();
            } else {
-               /* Comma or right paren inside nested parenthesis */
+               /* Comma or right paren inside nested parenthesis */
                if (CurC == ')') {
                    --ParCount;
                }
                *B++ = CurC;
-               NextChar ();
+               NextChar ();
            }
        } else if (IsBlank (CurC)) {
-           /* Squeeze runs of blanks */
+           /* Squeeze runs of blanks */
            *B++ = ' ';
            SkipBlank ();
+       } else if (CurC == '/' && NextC == '*') {
+           *B++ = ' ';
+           OldStyleComment ();
+       } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
+           *B++ = ' ';
+           NewStyleComment ();
        } else if (CurC == '\0') {
-           /* End of line inside macro argument list - read next line */
+           /* End of line inside macro argument list - read next line */
            if (NextLine () == 0) {
                return 0;
            }
        } else {
-           /* Just copy the character */
+           /* Just copy the character */
            *B++ = CurC;
-           NextChar ();
+           NextChar ();
        }
     }
 
     /* Compare formal argument count with actual */
     if (M->ArgCount != ArgCount) {
-       PPError (ERR_MACRO_ARGCOUNT);
-       /* Be sure to make enough empty arguments available */
-       while (ArgCount < M->ArgCount) {
-           M->ActualArgs [ArgCount++] = "";
-       }
+       PPError ("Macro argument count mismatch");
+       /* Be sure to make enough empty arguments available */
+       while (ArgCount < M->ArgCount) {
+           M->ActualArgs [ArgCount++] = "";
+       }
     }
 
     /* Preprocess the line, replacing macro parameters */
@@ -336,25 +480,26 @@ static void ExpandMacro (Macro* M)
 {
     /* Check if this is a function like macro */
     if (M->ArgCount >= 0) {
-       /* Function like macro */
+       /* Function like macro */
                if (MacroCall (M) == 0) {
            ClearLine ();
        }
     } else {
-       /* Just copy the replacement text */
-       keepstr (M->Replacement);
+       /* Just copy the replacement text */
+       KeepStr (M->Replacement);
     }
 }
 
 
 
-static void addmac (void)
-/* Add a macro to the macro table. */
+static void DefineMacro (void)
+/* Handle a macro definition. */
 {
     char*      saveptr;
     ident      Ident;
     char       Buf[LINESIZE];
     Macro*     M;
+    Macro*     Existing;
 
     /* Read the macro name */
     SkipBlank ();
@@ -362,6 +507,9 @@ static void addmac (void)
        return;
     }
 
+    /* Get an existing macro definition with this name */
+    Existing = FindMacro (Ident);
+
     /* Create a new macro definition */
     M = NewMacro (Ident);
 
@@ -372,9 +520,9 @@ static void addmac (void)
        NextChar ();
 
                /* Set the marker that this is a function like macro */
-       M->ArgCount = 0;
+       M->ArgCount = 0;
 
-       /* Read the formal parameter list */
+       /* Read the formal parameter list */
        while (1) {
            SkipBlank ();
            if (CurC == ')')
@@ -382,16 +530,16 @@ static void addmac (void)
            if (MacName (Ident) == 0) {
                return;
            }
-           AddMacroArg (M, Ident);
+           AddMacroArg (M, Ident);
            SkipBlank ();
            if (CurC != ',')
                break;
            NextChar ();
        }
 
-       /* Check for a right paren and eat it if we find one */
+       /* Check for a right paren and eat it if we find one */
        if (CurC != ')') {
-                   PPError (ERR_RPAREN_EXPECTED);
+                   PPError ("`)' expected");
            ClearLine ();
            return;
        }
@@ -411,12 +559,21 @@ static void addmac (void)
 
     /* Create a copy of the replacement */
     M->Replacement = xstrdup (Buf);
+
+    /* If we have an existing macro, check if the redefinition is identical.
+     * Print a diagnostic if not.
+     */
+    if (Existing) {
+       if (MacroCmp (M, Existing) != 0) {
+           PPError ("Macro redefinition is not identical");
+       }
+    }
 }
 
 
 
 /*****************************************************************************/
-
+/*                              Preprocessing                               */
 /*****************************************************************************/
 
 
@@ -438,7 +595,7 @@ static int Pass1 (const char* From, char* To)
     done = 1;
     while (CurC != '\0') {
        if (IsBlank (CurC)) {
-           keepch (' ');
+           KeepChar (' ');
            SkipBlank ();
                } else if (IsIdent (CurC)) {
            SymName (Ident);
@@ -452,16 +609,16 @@ static int Pass1 (const char* From, char* To)
                    SkipBlank();
                }
                if (!IsIdent (CurC)) {
-                   PPError (ERR_IDENT_EXPECTED);
-                   *mptr++ = '0';
+                   PPError ("Identifier expected");
+                   KeepChar ('0');
                } else {
                    SymName (Ident);
-                   *mptr++ = IsMacro (Ident)? '1' : '0';
+                   KeepChar (IsMacro (Ident)? '1' : '0');
                    if (HaveParen) {
                        SkipBlank();
                        if (CurC != ')') {
-                           PPError (ERR_RPAREN_EXPECTED);
-                       } else {
+                           PPError ("`)' expected");
+                       } else {
                            NextChar ();
                        }
                    }
@@ -470,32 +627,22 @@ static int Pass1 (const char* From, char* To)
                if (MaybeMacro (Ident[0])) {
                    done = 0;
                }
-               keepstr (Ident);
+               KeepStr (Ident);
            }
-       } else if (IsQuoteChar (CurC)) {
+       } else if (IsQuote (CurC)) {
            mptr = CopyQuotedString (mptr);
        } else if (CurC == '/' && NextC == '*') {
-           keepch (' ');
-           Comment ();
+           KeepChar (' ');
+           OldStyleComment ();
        } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
-           keepch (' ');
-           /* Beware: Because line continuation chars are handled when reading
-            * lines, we may only skip til the end of the source line, which
-            * may not be the same as the end of the input line. The end of the
-            * source line is denoted by a lf (\n) character.
-            */
-           do {
-               NextChar ();
-           } while (CurC != '\n' && CurC != '\0');
-           if (CurC == '\n') {
-               NextChar ();
-           }
+           KeepChar (' ');
+           NewStyleComment ();
        } else {
-           *mptr++ = CurC;
+           KeepChar (CurC);
            NextChar ();
        }
     }
-    keepch ('\0');
+    KeepChar ('\0');
     return done;
 }
 
@@ -517,7 +664,7 @@ static int Pass2 (const char* From, char* To)
     /* Loop substituting macros */
     no_chg = 1;
     while (CurC != '\0') {
-       /* If we have an identifier, check if it's a macro */
+       /* If we have an identifier, check if it's a macro */
        if (IsIdent (CurC)) {
            SymName (Ident);
            M = FindMacro (Ident);
@@ -525,12 +672,12 @@ static int Pass2 (const char* From, char* To)
                ExpandMacro (M);
                no_chg = 0;
            } else {
-               keepstr (Ident);
+               KeepStr (Ident);
            }
-       } else if (IsQuoteChar(CurC)) {
+       } else if (IsQuote (CurC)) {
            mptr = CopyQuotedString (mptr);
        } else {
-           *mptr++ = CurC;
+           KeepChar (CurC);
            NextChar ();
        }
     }
@@ -539,28 +686,28 @@ static int Pass2 (const char* From, char* To)
 
 
 
-static void xlateline (void)
+static void PreprocessLine (void)
 /* Translate one line. */
 {
-    int cnt;
-    int Done;
+    unsigned I;
 
-    Done = Pass1 (line, mline);
-    if (ExpandMacros == 0) {
-       Done = 1;
-       ExpandMacros = 1;       /* Reset to default */
-    }
-    cnt = 5;
-    do {
+    /* Trim whitespace and remove comments. The function returns false if no
+     * identifiers were found that may be macros. If this is the case, no
+     * macro substitution is performed.
+     */
+    int Done = Pass1 (line, mline);
+
+    /* Repeatedly expand macros in the line */
+    for (I = 0; I < 5; ++I) {
        /* Swap mline and line */
-               char* p = line;
-       line = mline;
-       mline = p;
-       if (Done)
-           break;
-       Done = Pass2 (line, mline);
-       keepch ('\0');
-    } while (--cnt);
+        SwapLineBuffers ();
+       if (Done) {
+           break;
+        }
+        /* Perform macro expansion */
+       Done = Pass2 (line, mline);
+       KeepChar ('\0');
+    }
 
     /* Reinitialize line parsing */
     InitLine (line);
@@ -568,8 +715,8 @@ static void xlateline (void)
 
 
 
-static void doundef (void)
-/* Process #undef directive */
+static void DoUndef (void)
+/* Process the #undef directive */
 {
     ident Ident;
 
@@ -581,42 +728,58 @@ static void doundef (void)
 
 
 
-static int setmflag (int skip, int flag, int cond)
-/* setmflag( skip, flag, cond ) */
+static int PushIf (int Skip, int Invert, int Cond)
+/* Push a new if level onto the if stack */
 {
-    if (skip) {
-       s_ifdef[++i_ifdef] = 3;
-       return (1);
+    /* Check for an overflow of the if stack */
+    if (IfIndex >= MAX_IFS-1) {
+       PPError ("Too many nested #if clauses");
+       return 1;
+    }
+
+    /* Push the #if condition */
+    ++IfIndex;
+    if (Skip) {
+       IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
+       return 1;
     } else {
-       s_ifdef[++i_ifdef] = 6;
-       return (flag ^ cond);
+       IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
+       return (Invert ^ Cond);
     }
 }
 
 
 
-static int doiff (int skip)
+static int DoIf (int Skip)
 /* Process #if directive */
 {
-    struct expent lval;
+    ExprDesc lval;
     char* S;
 
     /* We're about to abuse the compiler expression parser to evaluate the
      * #if expression. Save the current tokens to come back here later.
+     * NOTE: Yes, this is a hack, but it saves a complete separate expression
+     * evaluation for the preprocessor.
      */
     Token sv1 = CurTok;
     Token sv2 = NextTok;
 
-    /* Remove the #if from the line and add two semicolons as sentinels */
+    /* Make sure the line infos for the tokens won't get removed */
+    if (sv1.LI) {
+       UseLineInfo (sv1.LI);
+    }
+    if (sv2.LI) {
+       UseLineInfo (sv2.LI);
+    }
+
+    /* Remove the #if from the line */
     SkipBlank ();
     S = line;
     while (CurC != '\0') {
-       *S++ = CurC;
-       NextChar ();
+       *S++ = CurC;
+       NextChar ();
     }
-    *S++ = ';';
-    *S++ = ';';
-    *S   = '\0';
+    *S = '\0';
 
     /* Start over parsing from line */
     InitLine (line);
@@ -625,14 +788,20 @@ static int doiff (int skip)
     Preprocessing = 1;
 
     /* Expand macros in this line */
-    xlateline ();
+    PreprocessLine ();
+
+    /* Add two semicolons as sentinels to the line, so the following
+     * expression evaluation will eat these two tokens but nothing from
+     * the following line.
+     */
+    strcat (line, ";;");
 
     /* Prime the token pump (remove old tokens from the stream) */
     NextToken ();
     NextToken ();
 
     /* Call the expression parser */
-    constexpr (&lval);
+    ConstExpr (&lval);
 
     /* End preprocessing mode */
     Preprocessing = 0;
@@ -642,12 +811,12 @@ static int doiff (int skip)
     NextTok = sv2;
 
     /* Set the #if condition according to the expression result */
-    return (setmflag (skip, 1, lval.e_const != 0));
+    return PushIf (Skip, 1, lval.ConstVal != 0);
 }
 
 
 
-static int doifdef (int skip, int flag)
+static int DoIfDef (int skip, int flag)
 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
 {
     ident Ident;
@@ -656,13 +825,13 @@ static int doifdef (int skip, int flag)
     if (MacName (Ident) == 0) {
                return 0;
     } else {
-       return setmflag (skip, flag, IsMacro(Ident));
+       return PushIf (skip, flag, IsMacro(Ident));
     }
 }
 
 
 
-static void doinclude (void)
+static void DoInclude (void)
 /* Open an include file. */
 {
     char       RTerm;
@@ -688,7 +857,7 @@ static void doinclude (void)
                    break;
 
                default:
-                   PPError (ERR_INCLUDE_LTERM_EXPECTED);
+                   PPError ("`\"' or `<' expected");
                    goto Done;
     }
     NextChar ();
@@ -698,7 +867,7 @@ static void doinclude (void)
      */
     mptr = mline;
     while (CurC != '\0' && CurC != RTerm) {
-       *mptr++ = CurC;
+       KeepChar (CurC);
        NextChar ();
     }
     *mptr = '\0';
@@ -706,7 +875,7 @@ static void doinclude (void)
     /* Check if we got a terminator */
     if (CurC != RTerm) {
                /* No terminator found */
-               PPError (ERR_INCLUDE_RTERM_EXPECTED);
+               PPError ("Missing terminator or file name too long");
                goto Done;
     }
 
@@ -722,63 +891,42 @@ Done:
 
 
 
-static void doerror (void)
+static void DoError (void)
 /* Print an error */
 {
     SkipBlank ();
     if (CurC == '\0') {
-       PPError (ERR_INVALID_USER_ERROR);
+       PPError ("Invalid #error directive");
     } else {
-        PPError (ERR_USER_ERROR, lptr);
+        PPError ("#error: %s", lptr);
     }
 
-    /* clear rest of line */
+    /* Clear the rest of line */
     ClearLine ();
 }
 
 
 
-/* C preprocessor. */
-
-/* stuff used to bum the keyword dispatching stuff */
-enum {
-    PP_DEFINE,
-    PP_ELSE,
-    PP_ENDIF,
-    PP_ERROR,
-    PP_IF,
-    PP_IFDEF,
-    PP_IFNDEF,
-    PP_INCLUDE,
-    PP_LINE,
-    PP_PRAGMA,
-    PP_UNDEF,
-    PP_ILLEGAL
-};
-
-static const struct tok_elt pre_toks[] = {
-    {          "define",       PP_DEFINE       },
-    {          "else",         PP_ELSE         },
-    {          "endif",        PP_ENDIF        },
-    {          "error",        PP_ERROR        },
-    {          "if",           PP_IF           },
-    {          "ifdef",        PP_IFDEF        },
-    {          "ifndef",       PP_IFNDEF       },
-    {          "include",      PP_INCLUDE      },
-    {   "line",                PP_LINE         },
-    {          "pragma",       PP_PRAGMA       },
-    {          "undef",        PP_UNDEF        },
-    {          0,              PP_ILLEGAL      }
-};
+static void DoPragma (void)
+/* Handle a #pragma line by converting the #pragma preprocessor directive into
+ * the _Pragma() compiler operator.
+ */
+{
+    /* Skip blanks following the #pragma directive */
+    SkipBlank ();
 
+    /* Copy the remainder of the line into mline removing comments and ws */
+    Pass1 (lptr, mline);
 
+    /* Convert the directive into the operator */
+    mptr = line;
+    KeepStr ("_Pragma (");
+    Stringize (mline);
+    KeepChar (')');
+    *mptr = '\0';
 
-static int searchtok (const char *sym, const struct tok_elt *toks)
-/* Search a token in a table */
-{
-    while (toks->toknam && strcmp (toks->toknam, sym))
-       ++toks;
-    return (toks->toknbr);
+    /* Initialize reading from line */
+    InitLine (line);
 }
 
 
@@ -786,8 +934,8 @@ static int searchtok (const char *sym, const struct tok_elt *toks)
 void Preprocess (void)
 /* Preprocess a line */
 {
-    int        Skip;
-    ident      Directive;
+    int        Skip;
+    ident      Directive;
 
     /* Skip white space at the beginning of the line */
     SkipBlank ();
@@ -805,103 +953,136 @@ void Preprocess (void)
                        continue;
                    }
                    if (!IsSym (Directive)) {
-                       PPError (ERR_CPP_DIRECTIVE_EXPECTED);
+                       PPError ("Preprocessor directive expected");
                        ClearLine ();
                    } else {
-                       switch (searchtok (Directive, pre_toks)) {
+                       switch (FindPPToken (Directive)) {
 
                            case PP_DEFINE:
                                if (!Skip) {
-                                   addmac ();
+                                   DefineMacro ();
                                }
                                break;
 
+                   case PP_ELIF:
+                       if (IfIndex >= 0) {
+                           if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
+
+                               /* Handle as #else/#if combination */
+                               if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
+                                   Skip = !Skip;
+                               }
+                               IfStack[IfIndex] |= IFCOND_ELSE;
+                               Skip = DoIf (Skip);
+
+                               /* #elif doesn't need a terminator */
+                               IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
+                           } else {
+                               PPError ("Duplicate #else/#elif");
+                           }
+                       } else {
+                           PPError ("Unexpected #elif");
+                       }
+                       break;
+
                            case PP_ELSE:
-                               if (s_ifdef[i_ifdef] & 2) {
-                                   if (s_ifdef[i_ifdef] & 4) {
-                                       Skip = !Skip;
-                                   }
-                                   s_ifdef[i_ifdef] ^= 2;
+                               if (IfIndex >= 0) {
+                           if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
+                               if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
+                                   Skip = !Skip;
+                               }
+                               IfStack[IfIndex] |= IFCOND_ELSE;
+                           } else {
+                               PPError ("Duplicate #else");
+                           }
                                } else {
-                                   PPError (ERR_UNEXPECTED_CPP_ELSE);
+                                   PPError ("Unexpected `#else'");
                                }
                                break;
 
                            case PP_ENDIF:
-                               if (i_ifdef >= 0) {
-                                   Skip = s_ifdef[i_ifdef--] & 1;
+                               if (IfIndex >= 0) {
+                           /* Remove any clauses on top of stack that do not
+                            * need a terminating #endif.
+                            */
+                           while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
+                               --IfIndex;
+                           }
+
+                           /* Stack may not be empty here or something is wrong */
+                           CHECK (IfIndex >= 0);
+
+                           /* Remove the clause that needs a terminator */
+                           Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
                                } else {
-                                   PPError (ERR_UNEXPECTED_CPP_ENDIF);
+                                   PPError ("Unexpected `#endif'");
                                }
                                break;
 
                            case PP_ERROR:
                                if (!Skip) {
-                                   doerror ();
+                                   DoError ();
                        }
                        break;
 
                            case PP_IF:
-                       Skip = doiff (Skip);
+                       Skip = DoIf (Skip);
                        break;
 
                            case PP_IFDEF:
-                       Skip = doifdef (Skip, 1);
+                       Skip = DoIfDef (Skip, 1);
                        break;
 
                            case PP_IFNDEF:
-                       Skip = doifdef (Skip, 0);
+                       Skip = DoIfDef (Skip, 0);
                        break;
 
                            case PP_INCLUDE:
                        if (!Skip) {
-                           doinclude ();
+                           DoInclude ();
                        }
                        break;
 
                            case PP_LINE:
-                       /* Not allowed in strict ANSI mode */
-                       if (ANSI) {
-                           PPError (ERR_CPP_DIRECTIVE_EXPECTED);
-                           ClearLine ();
-                       }
-                       break;
+                       /* Not allowed in strict ANSI mode */
+                       if (!Skip && ANSI) {
+                           PPError ("Preprocessor directive expected");
+                           ClearLine ();
+                       }
+                       break;
 
                            case PP_PRAGMA:
-                       if (!Skip) {
-                           /* Don't expand macros in this line */
-                           ExpandMacros = 0;
-                           /* #pragma is handled on the scanner level */
-                           goto Done;
-                       }
+                       if (!Skip) {
+                            DoPragma ();
+                            goto Done;
+                       }
                        break;
 
                            case PP_UNDEF:
                        if (!Skip) {
-                           doundef ();
+                           DoUndef ();
                        }
                        break;
 
                    default:
-                       PPError (ERR_CPP_DIRECTIVE_EXPECTED);
+                       PPError ("Preprocessor directive expected");
                        ClearLine ();
                }
-           }
+           }
 
        }
        if (NextLine () == 0) {
-           if (i_ifdef >= 0) {
-               PPError (ERR_CPP_ENDIF_EXPECTED);
+           if (IfIndex >= 0) {
+               PPError ("`#endif' expected");
            }
            return;
        }
-       SkipBlank ();
+       SkipBlank ();
     }
 
+    PreprocessLine ();
+
 Done:
-    xlateline ();
-    if (Verbose > 1) {
-       printf ("line: %s\n", line);
-    }
+    Print (stdout, 2, "line: %s\n", line);
 }