]> git.sur5r.net Git - cc65/blobdiff - src/cc65/pragma.c
remove TABs
[cc65] / src / cc65 / pragma.c
index ba3815d965980be603936767a470520c2504db33..b05ef6122bb4fce4efc24bd4f649999e395f16cc 100644 (file)
@@ -1,15 +1,15 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                pragma.c                                  */
+/*                                 pragma.c                                  */
 /*                                                                           */
-/*                 Pragma handling for the cc65 C compiler                  */
+/*                  Pragma handling for the cc65 C compiler                  */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2001 Ullrich von Bassewitz                                       */
-/*               Wacholderweg 14                                             */
-/*               D-70597 Stuttgart                                           */
-/* EMail:        uz@cc65.org                                                 */
+/* (C) 1998-2011, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 #include <stdlib.h>
 #include <string.h>
 
+/* common */
+#include "chartype.h"
+#include "segnames.h"
+#include "tgttrans.h"
+
 /* cc65 */
 #include "codegen.h"
 #include "error.h"
 #include "global.h"
 #include "litpool.h"
 #include "scanner.h"
-#include "segments.h"
+#include "scanstrbuf.h"
 #include "symtab.h"
 #include "pragma.h"
+#include "wrappedcall.h"
 
 
 
 /*****************************************************************************/
-/*                                  data                                    */
+/*                                   data                                    */
 /*****************************************************************************/
 
 
 
 /* Tokens for the #pragmas */
 typedef enum {
-    PR_BSSSEG,
-    PR_CHECKSTACK,
-    PR_CODESEG,
-    PR_DATASEG,
-    PR_REGVARADDR,
-    PR_RODATASEG,
-    PR_SIGNEDCHARS,
-    PR_STATICLOCALS,
-    PR_ZPSYM,
-    PR_ILLEGAL
+    PRAGMA_ILLEGAL = -1,
+    PRAGMA_ALIGN,
+    PRAGMA_ALLOW_EAGER_INLINE,
+    PRAGMA_BSS_NAME,
+    PRAGMA_BSSSEG,                                      /* obsolete */
+    PRAGMA_CHARMAP,
+    PRAGMA_CHECK_STACK,
+    PRAGMA_CHECKSTACK,                                  /* obsolete */
+    PRAGMA_CODE_NAME,
+    PRAGMA_CODESEG,                                     /* obsolete */
+    PRAGMA_CODESIZE,
+    PRAGMA_DATA_NAME,
+    PRAGMA_DATASEG,                                     /* obsolete */
+    PRAGMA_INLINE_STDFUNCS,
+    PRAGMA_LOCAL_STRINGS,
+    PRAGMA_MESSAGE,
+    PRAGMA_OPTIMIZE,
+    PRAGMA_REGISTER_VARS,
+    PRAGMA_REGVARADDR,
+    PRAGMA_REGVARS,                                     /* obsolete */
+    PRAGMA_RODATA_NAME,
+    PRAGMA_RODATASEG,                                   /* obsolete */
+    PRAGMA_SIGNED_CHARS,
+    PRAGMA_SIGNEDCHARS,                                 /* obsolete */
+    PRAGMA_STATIC_LOCALS,
+    PRAGMA_STATICLOCALS,                                /* obsolete */
+    PRAGMA_WARN,
+    PRAGMA_WRAPPED_CALL,
+    PRAGMA_WRITABLE_STRINGS,
+    PRAGMA_ZPSYM,
+    PRAGMA_COUNT
 } pragma_t;
 
 /* Pragma table */
 static const struct Pragma {
-    const char*        Key;            /* Keyword */
-    pragma_t   Tok;            /* Token */
-} Pragmas[] = {
-    {  "bssseg",       PR_BSSSEG       },
-    {  "checkstack",   PR_CHECKSTACK   },
-    {   "codeseg",     PR_CODESEG      },
-    {   "dataseg",     PR_DATASEG      },
-    {   "regvaraddr",  PR_REGVARADDR   },
-    {   "rodataseg",   PR_RODATASEG    },
-    {  "signedchars",  PR_SIGNEDCHARS  },
-    {  "staticlocals", PR_STATICLOCALS },
-    {   "zpsym",               PR_ZPSYM        },
+    const char* Key;            /* Keyword */
+    pragma_t    Tok;            /* Token */
+} Pragmas[PRAGMA_COUNT] = {
+    { "align",                  PRAGMA_ALIGN              },
+    { "allow-eager-inline",     PRAGMA_ALLOW_EAGER_INLINE },
+    { "bss-name",               PRAGMA_BSS_NAME           },
+    { "bssseg",                 PRAGMA_BSSSEG             },      /* obsolete */
+    { "charmap",                PRAGMA_CHARMAP            },
+    { "check-stack",            PRAGMA_CHECK_STACK        },
+    { "checkstack",             PRAGMA_CHECKSTACK         },      /* obsolete */
+    { "code-name",              PRAGMA_CODE_NAME          },
+    { "codeseg",                PRAGMA_CODESEG            },      /* obsolete */
+    { "codesize",               PRAGMA_CODESIZE           },
+    { "data-name",              PRAGMA_DATA_NAME          },
+    { "dataseg",                PRAGMA_DATASEG            },      /* obsolete */
+    { "inline-stdfuncs",        PRAGMA_INLINE_STDFUNCS    },
+    { "local-strings",          PRAGMA_LOCAL_STRINGS      },
+    { "message",                PRAGMA_MESSAGE            },
+    { "optimize",               PRAGMA_OPTIMIZE           },
+    { "register-vars",          PRAGMA_REGISTER_VARS      },
+    { "regvaraddr",             PRAGMA_REGVARADDR         },
+    { "regvars",                PRAGMA_REGVARS            },      /* obsolete */
+    { "rodata-name",            PRAGMA_RODATA_NAME        },
+    { "rodataseg",              PRAGMA_RODATASEG          },      /* obsolete */
+    { "signed-chars",           PRAGMA_SIGNED_CHARS       },
+    { "signedchars",            PRAGMA_SIGNEDCHARS        },      /* obsolete */
+    { "static-locals",          PRAGMA_STATIC_LOCALS      },
+    { "staticlocals",           PRAGMA_STATICLOCALS       },      /* obsolete */
+    { "warn",                   PRAGMA_WARN               },
+    { "wrapped-call",           PRAGMA_WRAPPED_CALL       },
+    { "writable-strings",       PRAGMA_WRITABLE_STRINGS   },
+    { "zpsym",                  PRAGMA_ZPSYM              },
 };
 
-/* Number of pragmas */
-#define PRAGMA_COUNT   (sizeof(Pragmas) / sizeof(Pragmas[0]))
+/* Result of ParsePushPop */
+typedef enum {
+    PP_NONE,
+    PP_POP,
+    PP_PUSH,
+    PP_ERROR,
+} PushPopResult;
 
 
 
 /*****************************************************************************/
-/*                                  Code                                    */
+/*                             Helper functions                              */
 /*****************************************************************************/
 
 
 
+static void PragmaErrorSkip (void)
+/* Called in case of an error, skips tokens until the closing paren or a
+** semicolon is reached.
+*/
+{
+    static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
+    SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
+}
+
+
+
 static int CmpKey (const void* Key, const void* Elem)
 /* Compare function for bsearch */
 {
@@ -104,162 +167,812 @@ static int CmpKey (const void* Key, const void* Elem)
 
 
 
-static pragma_t FindPragma (const char* Key)
-/* Find a pragma and return the token. Return PR_ILLEGAL if the keyword is
- * not a valid pragma.
- */
+static pragma_t FindPragma (const StrBuf* Key)
+/* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is
+** not a valid pragma.
+*/
 {
     struct Pragma* P;
-    P = bsearch (Key, Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
-    return P? P->Tok : PR_ILLEGAL;
+    P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
+    return P? P->Tok : PRAGMA_ILLEGAL;
 }
 
 
 
-static void StringPragma (void (*Func) (const char*))
-/* Handle a pragma that expects a string parameter */
+static int GetComma (StrBuf* B)
+/* Expects and skips a comma in B. Prints an error and returns zero if no
+** comma is found. Return a value <> 0 otherwise.
+*/
+{
+    SB_SkipWhite (B);
+    if (SB_Get (B) != ',') {
+        Error ("Comma expected");
+        return 0;
+    }
+    SB_SkipWhite (B);
+    return 1;
+}
+
+
+
+static int GetString (StrBuf* B, StrBuf* S)
+/* Expects and skips a string in B. Prints an error and returns zero if no
+** string is found. Returns a value <> 0 otherwise.
+*/
+{
+    if (!SB_GetString (B, S)) {
+        Error ("String literal expected");
+        return 0;
+    }
+    return 1;
+}
+
+
+
+static int GetNumber (StrBuf* B, long* Val)
+/* Expects and skips a number in B. Prints an eror and returns zero if no
+** number is found. Returns a value <> 0 otherwise.
+*/
+{
+    if (!SB_GetNumber (B, Val)) {
+        Error ("Constant integer expected");
+        return 0;
+    }
+    return 1;
+}
+
+
+
+static IntStack* GetWarning (StrBuf* B)
+/* Get a warning name from the string buffer. Returns a pointer to the intstack
+** that holds the state of the warning, and NULL in case of errors. The
+** function will output error messages in case of problems.
+*/
+{
+    IntStack* S = 0;
+    StrBuf W = AUTO_STRBUF_INITIALIZER;
+
+    /* The warning name is a symbol but the '-' char is allowed within */
+    if (SB_GetSym (B, &W, "-")) {
+
+        /* Map the warning name to an IntStack that contains its state */
+        S = FindWarning (SB_GetConstBuf (&W));
+
+        /* Handle errors */
+        if (S == 0) {
+            Error ("Pragma expects a warning name as first argument");
+        }
+    }
+
+    /* Deallocate the string */
+    SB_Done (&W);
+
+    /* Done */
+    return S;
+}
+
+
+
+static int HasStr (StrBuf* B, const char* E)
+/* Checks if E follows in B. If so, skips it and returns true */
 {
-    if (curtok != TOK_SCONST) {
-       Error ("String literal expected");
+    unsigned Len = strlen (E);
+    if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
+        if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
+            /* Found */
+            SB_SkipMultiple (B, Len);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+
+static PushPopResult ParsePushPop (StrBuf* B)
+/* Check for and parse the "push" and "pop" keywords. In case of "push", a
+** following comma is expected and skipped.
+*/
+{
+    StrBuf Ident      = AUTO_STRBUF_INITIALIZER;
+    PushPopResult Res = PP_NONE;
+
+    /* Remember the current string index, so we can go back in case of errors */
+    unsigned Index = SB_GetIndex (B);
+
+    /* Try to read an identifier */
+    if (SB_GetSym (B, &Ident, 0)) {
+
+        /* Check if we have a first argument named "pop" */
+        if (SB_CompareStr (&Ident, "pop") == 0) {
+
+            Res = PP_POP;
+
+        /* Check if we have a first argument named "push" */
+        } else if (SB_CompareStr (&Ident, "push") == 0) {
+
+            Res = PP_PUSH;
+
+            /* Skip the following comma */
+            if (!GetComma (B)) {
+                /* Error already flagged by GetComma */
+                Res = PP_ERROR;
+            }
+
+        } else {
+
+            /* Unknown keyword, roll back */
+            SB_SetIndex (B, Index);
+        }
+    }
+
+    /* Free the string buffer and return the result */
+    SB_Done (&Ident);
+    return Res;
+}
+
+
+
+static void PopInt (IntStack* S)
+/* Pops an integer from an IntStack. Prints an error if the stack is empty */
+{
+    if (IS_GetCount (S) < 2) {
+        Error ("Cannot pop, stack is empty");
     } else {
-       /* Get the string */
-       const char* Name = GetLiteral (curval);
+        IS_Drop (S);
+    }
+}
+
 
-               /* Call the given function with the string argument */
-       Func (Name);
 
-       /* Reset the string pointer, removing the string from the pool */
-       ResetLiteralPoolOffs (curval);
+static void PushInt (IntStack* S, long Val)
+/* Pushes an integer onto an IntStack. Prints an error if the stack is full */
+{
+    if (IS_IsFull (S)) {
+        Error ("Cannot push: stack overflow");
+    } else {
+        IS_Push (S, Val);
     }
+}
 
-    /* Skip the string (or error) token */
-    NextToken ();
+
+
+static int BoolKeyword (StrBuf* Ident)
+/* Check if the identifier in Ident is a keyword for a boolean value. Currently
+** accepted are true/false/on/off.
+*/
+{
+    if (SB_CompareStr (Ident, "true") == 0) {
+        return 1;
+    }
+    if (SB_CompareStr (Ident, "on") == 0) {
+        return 1;
+    }
+    if (SB_CompareStr (Ident, "false") == 0) {
+        return 0;
+    }
+    if (SB_CompareStr (Ident, "off") == 0) {
+        return 0;
+    }
+
+    /* Error */
+    Error ("Pragma argument must be one of 'on', 'off', 'true' or 'false'");
+    return 0;
 }
 
 
 
-static void SegNamePragma (segment_t Seg)
+/*****************************************************************************/
+/*                         Pragma handling functions                         */
+/*****************************************************************************/
+
+
+
+static void StringPragma (StrBuf* B, void (*Func) (const char*))
+/* Handle a pragma that expects a string parameter */
+{
+    StrBuf S = AUTO_STRBUF_INITIALIZER;
+
+    /* We expect a string here */
+    if (GetString (B, &S)) {
+        /* Call the given function with the string argument */
+        Func (SB_GetConstBuf (&S));
+    }
+
+    /* Call the string buf destructor */
+    SB_Done (&S);
+}
+
+
+
+static void SegNamePragma (StrBuf* B, segment_t Seg)
 /* Handle a pragma that expects a segment name parameter */
 {
-    if (curtok != TOK_SCONST) {
-       Error ("String literal expected");
+    const char* Name;
+    StrBuf S = AUTO_STRBUF_INITIALIZER;
+    int Push = 0;
+
+    /* Check for the "push" or "pop" keywords */
+    switch (ParsePushPop (B)) {
+
+        case PP_NONE:
+            break;
+
+        case PP_PUSH:
+            Push = 1;
+            break;
+
+        case PP_POP:
+            /* Pop the old value and output it */
+            PopSegName (Seg);
+
+            /* BSS variables are output at the end of the compilation.  Don't
+            ** bother to change their segment, now.
+            */
+            if (Seg != SEG_BSS) {
+                g_segname (Seg);
+            }
+
+            /* Done */
+            goto ExitPoint;
+
+        case PP_ERROR:
+            /* Bail out */
+            goto ExitPoint;
+
+        default:
+            Internal ("Invalid result from ParsePushPop");
+
+    }
+
+    /* A string argument must follow */
+    if (!GetString (B, &S)) {
+        goto ExitPoint;
+    }
+
+    /* Get the string */
+    Name = SB_GetConstBuf (&S);
+
+    /* Check if the name is valid */
+    if (ValidSegName (Name)) {
+
+        /* Set the new name */
+        if (Push) {
+            PushSegName (Seg, Name);
+        } else {
+            SetSegName (Seg, Name);
+        }
+
+        /* BSS variables are output at the end of the compilation.  Don't
+        ** bother to change their segment, now.
+        */
+        if (Seg != SEG_BSS) {
+            g_segname (Seg);
+        }
+
+    } else {
+
+        /* Segment name is invalid */
+        Error ("Illegal segment name: '%s'", Name);
+
+    }
+
+ExitPoint:
+    /* Call the string buf destructor */
+    SB_Done (&S);
+}
+
+
+static void WrappedCallPragma (StrBuf* B)
+/* Handle the wrapped-call pragma */
+{
+    StrBuf      S = AUTO_STRBUF_INITIALIZER;
+    const char *Name;
+    long Val;
+    SymEntry *Entry;
+
+    /* Check for the "push" or "pop" keywords */
+    switch (ParsePushPop (B)) {
+
+        case PP_NONE:
+            Error ("Push or pop required");
+            break;
+
+        case PP_PUSH:
+            break;
+
+        case PP_POP:
+            PopWrappedCall();
+
+            /* Done */
+            goto ExitPoint;
+
+        case PP_ERROR:
+            /* Bail out */
+            goto ExitPoint;
+
+        default:
+            Internal ("Invalid result from ParsePushPop");
+
+    }
+
+    /* A symbol argument must follow */
+    if (!SB_GetSym (B, &S, NULL)) {
+        goto ExitPoint;
+    }
+
+    /* Skip the following comma */
+    if (!GetComma (B)) {
+        /* Error already flagged by GetComma */
+        Error ("Value required for wrapped-call identifier");
+        goto ExitPoint;
+    }
+
+    if (!GetNumber (B, &Val)) {
+        Error ("Value required for wrapped-call identifier");
+        goto ExitPoint;
+    }
+
+    if (Val < 0 || Val > 255) {
+        Error ("Identifier must be between 0-255");
+        goto ExitPoint;
+    }
+
+    /* Get the string */
+    Name = SB_GetConstBuf (&S);
+    Entry = FindSym(Name);
+
+    /* Check if the name is valid */
+    if (Entry && Entry->Flags & SC_FUNC) {
+
+        PushWrappedCall(Entry, (unsigned char) Val);
+        Entry->Flags |= SC_REF;
+        Entry->V.F.Func->Flags |= FD_CALL_WRAPPER;
+
     } else {
-       /* Get the segment name */
-       const char* Name = GetLiteral (curval);
 
-       /* Check if the name is valid */
-       if (ValidSegName (Name)) {
+        /* Segment name is invalid */
+        Error ("Wrapped-call target does not exist or is not a function");
+
+    }
+
+ExitPoint:
+    /* Call the string buf destructor */
+    SB_Done (&S);
+}
 
-                   /* Set the new name */
-           g_segname (Seg, Name);
 
-       } else {
 
-           /* Segment name is invalid */
-           Error ("Illegal segment name: `%s'", Name);
+static void CharMapPragma (StrBuf* B)
+/* Change the character map */
+{
+    long Index, C;
 
-       }
+    /* Read the character index */
+    if (!GetNumber (B, &Index)) {
+        return;
+    }
+    if (Index < 0 || Index > 255) {
+        Error ("Character index out of range");
+        return;
+    }
 
-       /* Reset the string pointer, removing the string from the pool */
-       ResetLiteralPoolOffs (curval);
+    /* Comma follows */
+    if (!GetComma (B)) {
+        return;
     }
 
-    /* Skip the string (or error) token */
-    NextToken ();
+    /* Read the character code */
+    if (!GetNumber (B, &C)) {
+        return;
+    }
+    if (C < 0 || C > 255) {
+        Error ("Character code out of range");
+        return;
+    }
+
+    /* Warn about remapping character code 0x00
+    ** (except when remapping it back to itself).
+    */
+    if (Index + C != 0 && IS_Get (&WarnRemapZero)) {
+        if (Index == 0) {
+            Warning ("Remapping from 0 is dangerous with string functions");
+        }
+        else if (C == 0) {
+            Warning ("Remapping to 0 can make string functions stop unexpectedly");
+        }
+    }
+
+    /* Remap the character */
+    TgtTranslateSet ((unsigned) Index, (unsigned char) C);
 }
 
 
 
-static void FlagPragma (unsigned char* Flag)
+static void WarnPragma (StrBuf* B)
+/* Enable/disable warnings */
+{
+    long   Val;
+    int    Push;
+
+    /* A warning name must follow */
+    IntStack* S = GetWarning (B);
+    if (S == 0) {
+        return;
+    }
+
+    /* Comma follows */
+    if (!GetComma (B)) {
+        return;
+    }
+
+    /* Check for the "push" or "pop" keywords */
+    switch (ParsePushPop (B)) {
+
+        case PP_NONE:
+            Push = 0;
+            break;
+
+        case PP_PUSH:
+            Push = 1;
+            break;
+
+        case PP_POP:
+            /* Pop the old value and bail out */
+            PopInt (S);
+            return;
+
+        case PP_ERROR:
+            /* Bail out */
+            return;
+
+        default:
+            Internal ("Invalid result from ParsePushPop");
+    }
+
+    /* Boolean argument follows */
+    if (HasStr (B, "true") || HasStr (B, "on")) {
+        Val = 1;
+    } else if (HasStr (B, "false") || HasStr (B, "off")) {
+        Val = 0;
+    } else if (!SB_GetNumber (B, &Val)) {
+        Error ("Invalid pragma argument");
+        return;
+    }
+
+    /* Set/push the new value */
+    if (Push) {
+        PushInt (S, Val);
+    } else {
+        IS_Set (S, Val);
+    }
+}
+
+
+
+static void FlagPragma (StrBuf* B, IntStack* Stack)
 /* Handle a pragma that expects a boolean paramater */
 {
-    /* Read a constant expression */
-    struct expent val;
-    constexpr (&val);
+    StrBuf Ident = AUTO_STRBUF_INITIALIZER;
+    long   Val;
+    int    Push;
+
+
+    /* Try to read an identifier */
+    int IsIdent = SB_GetSym (B, &Ident, 0);
+
+    /* Check if we have a first argument named "pop" */
+    if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
+        PopInt (Stack);
+        /* No other arguments allowed */
+        return;
+    }
 
-    /* Store the value into the flag parameter */
-    *Flag = (val.e_const != 0);
+    /* Check if we have a first argument named "push" */
+    if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
+        Push = 1;
+        if (!GetComma (B)) {
+            goto ExitPoint;
+        }
+        IsIdent = SB_GetSym (B, &Ident, 0);
+    } else {
+        Push = 0;
+    }
+
+    /* Boolean argument follows */
+    if (IsIdent) {
+        Val = BoolKeyword (&Ident);
+    } else if (!GetNumber (B, &Val)) {
+        goto ExitPoint;
+    }
+
+    /* Set/push the new value */
+    if (Push) {
+        PushInt (Stack, Val);
+    } else {
+        IS_Set (Stack, Val);
+    }
+
+ExitPoint:
+    /* Free the identifier */
+    SB_Done (&Ident);
 }
 
 
 
-void DoPragma (void)
-/* Handle pragmas */
+static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
+/* Handle a pragma that expects an int paramater */
+{
+    long  Val;
+    int   Push;
+
+    /* Check for the "push" or "pop" keywords */
+    switch (ParsePushPop (B)) {
+
+        case PP_NONE:
+            Push = 0;
+            break;
+
+        case PP_PUSH:
+            Push = 1;
+            break;
+
+        case PP_POP:
+            /* Pop the old value and bail out */
+            PopInt (Stack);
+            return;
+
+        case PP_ERROR:
+            /* Bail out */
+            return;
+
+        default:
+            Internal ("Invalid result from ParsePushPop");
+
+    }
+
+    /* Integer argument follows */
+    if (!GetNumber (B, &Val)) {
+        return;
+    }
+
+    /* Check the argument */
+    if (Val < Low || Val > High) {
+        Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
+        return;
+    }
+
+    /* Set/push the new value */
+    if (Push) {
+        PushInt (Stack, Val);
+    } else {
+        IS_Set (Stack, Val);
+    }
+}
+
+
+
+static void MakeMessage (const char* Message)
+{
+    fprintf (stderr, "%s(%u): Note: %s\n", GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Message);
+}
+
+
+
+static void ParsePragma (void)
+/* Parse the contents of the _Pragma statement */
 {
     pragma_t Pragma;
+    StrBuf   Ident = AUTO_STRBUF_INITIALIZER;
 
-    /* Skip the token itself */
+    /* Create a string buffer from the string literal */
+    StrBuf B = AUTO_STRBUF_INITIALIZER;
+    SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
+
+    /* Skip the string token */
     NextToken ();
 
-    /* Identifier must follow */
-    if (curtok != TOK_IDENT) {
-       Error ("Identifier expected");
-       return;
+    /* Get the pragma name from the string */
+    SB_SkipWhite (&B);
+    if (!SB_GetSym (&B, &Ident, "-")) {
+        Error ("Invalid pragma");
+        goto ExitPoint;
     }
 
-    /* Search for the name, then skip the identifier */
-    Pragma = FindPragma (CurTok.Ident);
-    NextToken ();
+    /* Search for the name */
+    Pragma = FindPragma (&Ident);
 
     /* Do we know this pragma? */
-    if (Pragma == PR_ILLEGAL) {
-               /* According to the ANSI standard, we're not allowed to generate errors
-                * for unknown pragmas, however, we're allowed to warn - and we will
-                * do so. Otherwise one typo may give you hours of bug hunting...
-                */
-               Warning ("Unknown #pragma `%s'", CurTok.Ident);
-               return;
+    if (Pragma == PRAGMA_ILLEGAL) {
+        /* According to the ANSI standard, we're not allowed to generate errors
+        ** for unknown pragmas, but warn about them if enabled (the default).
+        */
+        if (IS_Get (&WarnUnknownPragma)) {
+            Warning ("Unknown pragma '%s'", SB_GetConstBuf (&Ident));
+        }
+        goto ExitPoint;
     }
 
     /* Check for an open paren */
-    ConsumeLParen ();
+    SB_SkipWhite (&B);
+    if (SB_Get (&B) != '(') {
+        Error ("'(' expected");
+        goto ExitPoint;
+    }
+
+    /* Skip white space before the argument */
+    SB_SkipWhite (&B);
 
     /* Switch for the different pragmas */
     switch (Pragma) {
 
-       case PR_BSSSEG:
-           SegNamePragma (SEG_BSS);
-           break;
+        case PRAGMA_ALIGN:
+            IntPragma (&B, &DataAlignment, 1, 4096);
+            break;
+
+        case PRAGMA_ALLOW_EAGER_INLINE:
+            FlagPragma (&B, &EagerlyInlineFuncs);
+            break;
+
+        case PRAGMA_BSSSEG:
+            Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
+            /* FALLTHROUGH */
+        case PRAGMA_BSS_NAME:
+            SegNamePragma (&B, SEG_BSS);
+            break;
+
+        case PRAGMA_CHARMAP:
+            CharMapPragma (&B);
+            break;
+
+        case PRAGMA_CHECKSTACK:
+            Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
+            /* FALLTHROUGH */
+        case PRAGMA_CHECK_STACK:
+            FlagPragma (&B, &CheckStack);
+            break;
+
+        case PRAGMA_CODESEG:
+            Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
+            /* FALLTHROUGH */
+        case PRAGMA_CODE_NAME:
+            SegNamePragma (&B, SEG_CODE);
+            break;
+
+        case PRAGMA_CODESIZE:
+            IntPragma (&B, &CodeSizeFactor, 10, 1000);
+            break;
+
+        case PRAGMA_DATASEG:
+            Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
+            /* FALLTHROUGH */
+        case PRAGMA_DATA_NAME:
+            SegNamePragma (&B, SEG_DATA);
+            break;
+
+        case PRAGMA_INLINE_STDFUNCS:
+            FlagPragma (&B, &InlineStdFuncs);
+            break;
+
+        case PRAGMA_LOCAL_STRINGS:
+            FlagPragma (&B, &LocalStrings);
+            break;
+
+        case PRAGMA_MESSAGE:
+            StringPragma (&B, MakeMessage);
+            break;
+
+        case PRAGMA_OPTIMIZE:
+            FlagPragma (&B, &Optimize);
+            break;
+
+        case PRAGMA_REGVARADDR:
+            FlagPragma (&B, &AllowRegVarAddr);
+            break;
+
+        case PRAGMA_REGVARS:
+            Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
+            /* FALLTHROUGH */
+        case PRAGMA_REGISTER_VARS:
+            FlagPragma (&B, &EnableRegVars);
+            break;
+
+        case PRAGMA_RODATASEG:
+            Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
+            /* FALLTHROUGH */
+        case PRAGMA_RODATA_NAME:
+            SegNamePragma (&B, SEG_RODATA);
+            break;
+
+        case PRAGMA_SIGNEDCHARS:
+            Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
+            /* FALLTHROUGH */
+        case PRAGMA_SIGNED_CHARS:
+            FlagPragma (&B, &SignedChars);
+            break;
+
+        case PRAGMA_STATICLOCALS:
+            Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
+            /* FALLTHROUGH */
+        case PRAGMA_STATIC_LOCALS:
+            FlagPragma (&B, &StaticLocals);
+            break;
+
+        case PRAGMA_WRAPPED_CALL:
+            WrappedCallPragma(&B);
+            break;
+
+        case PRAGMA_WARN:
+            WarnPragma (&B);
+            break;
+
+        case PRAGMA_WRITABLE_STRINGS:
+            FlagPragma (&B, &WritableStrings);
+            break;
+
+        case PRAGMA_ZPSYM:
+            StringPragma (&B, MakeZPSym);
+            break;
+
+        default:
+            Internal ("Invalid pragma");
+    }
+
+    /* Closing paren expected */
+    SB_SkipWhite (&B);
+    if (SB_Get (&B) != ')') {
+        Error ("')' expected");
+        goto ExitPoint;
+    }
+    SB_SkipWhite (&B);
+
+    /* Allow an optional semicolon to be compatible with the old syntax */
+    if (SB_Peek (&B) == ';') {
+        SB_Skip (&B);
+        SB_SkipWhite (&B);
+    }
+
+    /* Make sure nothing follows */
+    if (SB_Peek (&B) != '\0') {
+        Error ("Unexpected input following pragma directive");
+    }
 
-       case PR_CHECKSTACK:
-           FlagPragma (&CheckStack);
-           break;
+ExitPoint:
+    /* Release the string buffers */
+    SB_Done (&B);
+    SB_Done (&Ident);
+}
 
-       case PR_CODESEG:
-           SegNamePragma (SEG_CODE);
-           break;
 
-       case PR_DATASEG:
-           SegNamePragma (SEG_DATA);
-           break;
 
-       case PR_REGVARADDR:
-           FlagPragma (&AllowRegVarAddr);
-           break;
+void DoPragma (void)
+/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
+{
+    /* Skip the token itself */
+    NextToken ();
+
+    /* We expect an opening paren */
+    if (!ConsumeLParen ()) {
+        return;
+    }
 
-       case PR_RODATASEG:
-           SegNamePragma (SEG_RODATA);
-           break;
+    /* String literal */
+    if (CurTok.Tok != TOK_SCONST) {
 
-       case PR_SIGNEDCHARS:
-           FlagPragma (&SignedChars);
-           break;
+        /* Print a diagnostic */
+        Error ("String literal expected");
 
-       case PR_STATICLOCALS:
-           FlagPragma (&StaticLocals);
-           break;
+        /* Try some smart error recovery: Skip tokens until we reach the
+        ** enclosing paren, or a semicolon.
+        */
+        PragmaErrorSkip ();
 
-       case PR_ZPSYM:
-           StringPragma (MakeZPSym);
-           break;
+    } else {
 
-       default:
-                   Internal ("Invalid pragma");
+        /* Parse the _Pragma statement */
+        ParsePragma ();
     }
 
     /* Closing paren needed */
     ConsumeRParen ();
 }
-
-
-