]> git.sur5r.net Git - cc65/blob - src/cc65/pragma.c
Adjustments in response to latest comments
[cc65] / src / cc65 / pragma.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 pragma.c                                  */
4 /*                                                                           */
5 /*                  Pragma handling for the cc65 C compiler                  */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2011, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 /* common */
40 #include "chartype.h"
41 #include "segnames.h"
42 #include "tgttrans.h"
43
44 /* cc65 */
45 #include "codegen.h"
46 #include "error.h"
47 #include "expr.h"
48 #include "global.h"
49 #include "litpool.h"
50 #include "scanner.h"
51 #include "scanstrbuf.h"
52 #include "symtab.h"
53 #include "pragma.h"
54 #include "wrappedcall.h"
55
56
57
58 /*****************************************************************************/
59 /*                                   data                                    */
60 /*****************************************************************************/
61
62
63
64 /* Tokens for the #pragmas */
65 typedef enum {
66     PRAGMA_ILLEGAL = -1,
67     PRAGMA_ALIGN,
68     PRAGMA_ALLOW_EAGER_INLINE,
69     PRAGMA_BSS_NAME,
70     PRAGMA_BSSSEG,                                      /* obsolete */
71     PRAGMA_CHARMAP,
72     PRAGMA_CHECK_STACK,
73     PRAGMA_CHECKSTACK,                                  /* obsolete */
74     PRAGMA_CODE_NAME,
75     PRAGMA_CODESEG,                                     /* obsolete */
76     PRAGMA_CODESIZE,
77     PRAGMA_DATA_NAME,
78     PRAGMA_DATASEG,                                     /* obsolete */
79     PRAGMA_INLINE_STDFUNCS,
80     PRAGMA_LOCAL_STRINGS,
81     PRAGMA_OPTIMIZE,
82     PRAGMA_REGVARADDR,
83     PRAGMA_REGISTER_VARS,
84     PRAGMA_REGVARS,                                     /* obsolete */
85     PRAGMA_RODATA_NAME,
86     PRAGMA_RODATASEG,                                   /* obsolete */
87     PRAGMA_SIGNED_CHARS,
88     PRAGMA_SIGNEDCHARS,                                 /* obsolete */
89     PRAGMA_STATIC_LOCALS,
90     PRAGMA_STATICLOCALS,                                /* obsolete */
91     PRAGMA_WARN,
92     PRAGMA_WRAPPED_CALL,
93     PRAGMA_WRITABLE_STRINGS,
94     PRAGMA_ZPSYM,
95     PRAGMA_COUNT
96 } pragma_t;
97
98 /* Pragma table */
99 static const struct Pragma {
100     const char* Key;            /* Keyword */
101     pragma_t    Tok;            /* Token */
102 } Pragmas[PRAGMA_COUNT] = {
103     { "align",                  PRAGMA_ALIGN              },
104     { "allow-eager-inline",     PRAGMA_ALLOW_EAGER_INLINE },
105     { "bss-name",               PRAGMA_BSS_NAME           },
106     { "bssseg",                 PRAGMA_BSSSEG             },      /* obsolete */
107     { "charmap",                PRAGMA_CHARMAP            },
108     { "check-stack",            PRAGMA_CHECK_STACK        },
109     { "checkstack",             PRAGMA_CHECKSTACK         },      /* obsolete */
110     { "code-name",              PRAGMA_CODE_NAME          },
111     { "codeseg",                PRAGMA_CODESEG            },      /* obsolete */
112     { "codesize",               PRAGMA_CODESIZE           },
113     { "data-name",              PRAGMA_DATA_NAME          },
114     { "dataseg",                PRAGMA_DATASEG            },      /* obsolete */
115     { "inline-stdfuncs",        PRAGMA_INLINE_STDFUNCS    },
116     { "local-strings",          PRAGMA_LOCAL_STRINGS      },
117     { "optimize",               PRAGMA_OPTIMIZE           },
118     { "register-vars",          PRAGMA_REGISTER_VARS      },
119     { "regvaraddr",             PRAGMA_REGVARADDR         },
120     { "regvars",                PRAGMA_REGVARS            },      /* obsolete */
121     { "rodata-name",            PRAGMA_RODATA_NAME        },
122     { "rodataseg",              PRAGMA_RODATASEG          },      /* obsolete */
123     { "signed-chars",           PRAGMA_SIGNED_CHARS       },
124     { "signedchars",            PRAGMA_SIGNEDCHARS        },      /* obsolete */
125     { "static-locals",          PRAGMA_STATIC_LOCALS      },
126     { "staticlocals",           PRAGMA_STATICLOCALS       },      /* obsolete */
127     { "warn",                   PRAGMA_WARN               },
128     { "wrapped-call",           PRAGMA_WRAPPED_CALL       },
129     { "writable-strings",       PRAGMA_WRITABLE_STRINGS   },
130     { "zpsym",                  PRAGMA_ZPSYM              },
131 };
132
133 /* Result of ParsePushPop */
134 typedef enum {
135     PP_NONE,
136     PP_POP,
137     PP_PUSH,
138     PP_ERROR,
139 } PushPopResult;
140
141
142
143 /*****************************************************************************/
144 /*                             Helper functions                              */
145 /*****************************************************************************/
146
147
148
149 static void PragmaErrorSkip (void)
150 /* Called in case of an error, skips tokens until the closing paren or a
151 ** semicolon is reached.
152 */
153 {
154     static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
155     SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
156 }
157
158
159
160 static int CmpKey (const void* Key, const void* Elem)
161 /* Compare function for bsearch */
162 {
163     return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key);
164 }
165
166
167
168 static pragma_t FindPragma (const StrBuf* Key)
169 /* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is
170 ** not a valid pragma.
171 */
172 {
173     struct Pragma* P;
174     P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
175     return P? P->Tok : PRAGMA_ILLEGAL;
176 }
177
178
179
180 static int GetComma (StrBuf* B)
181 /* Expects and skips a comma in B. Prints an error and returns zero if no
182 ** comma is found. Return a value <> 0 otherwise.
183 */
184 {
185     SB_SkipWhite (B);
186     if (SB_Get (B) != ',') {
187         Error ("Comma expected");
188         return 0;
189     }
190     SB_SkipWhite (B);
191     return 1;
192 }
193
194
195
196 static int GetString (StrBuf* B, StrBuf* S)
197 /* Expects and skips a string in B. Prints an error and returns zero if no
198 ** string is found. Returns a value <> 0 otherwise.
199 */
200 {
201     if (!SB_GetString (B, S)) {
202         Error ("String literal expected");
203         return 0;
204     }
205     return 1;
206 }
207
208
209
210 static int GetNumber (StrBuf* B, long* Val)
211 /* Expects and skips a number in B. Prints an eror and returns zero if no
212 ** number is found. Returns a value <> 0 otherwise.
213 */
214 {
215     if (!SB_GetNumber (B, Val)) {
216         Error ("Constant integer expected");
217         return 0;
218     }
219     return 1;
220 }
221
222
223
224 static IntStack* GetWarning (StrBuf* B)
225 /* Get a warning name from the string buffer. Returns a pointer to the intstack
226 ** that holds the state of the warning, and NULL in case of errors. The
227 ** function will output error messages in case of problems.
228 */
229 {
230     IntStack* S = 0;
231     StrBuf W = AUTO_STRBUF_INITIALIZER;
232
233     /* The warning name is a symbol but the '-' char is allowed within */
234     if (SB_GetSym (B, &W, "-")) {
235
236         /* Map the warning name to an IntStack that contains its state */
237         S = FindWarning (SB_GetConstBuf (&W));
238
239         /* Handle errors */
240         if (S == 0) {
241             Error ("Pragma expects a warning name as first argument");
242         }
243     }
244
245     /* Deallocate the string */
246     SB_Done (&W);
247
248     /* Done */
249     return S;
250 }
251
252
253
254 static int HasStr (StrBuf* B, const char* E)
255 /* Checks if E follows in B. If so, skips it and returns true */
256 {
257     unsigned Len = strlen (E);
258     if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
259         if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
260             /* Found */
261             SB_SkipMultiple (B, Len);
262             return 1;
263         }
264     }
265     return 0;
266 }
267
268
269
270 static PushPopResult ParsePushPop (StrBuf* B)
271 /* Check for and parse the "push" and "pop" keywords. In case of "push", a
272 ** following comma is expected and skipped.
273 */
274 {
275     StrBuf Ident      = AUTO_STRBUF_INITIALIZER;
276     PushPopResult Res = PP_NONE;
277
278     /* Remember the current string index, so we can go back in case of errors */
279     unsigned Index = SB_GetIndex (B);
280
281     /* Try to read an identifier */
282     if (SB_GetSym (B, &Ident, 0)) {
283
284         /* Check if we have a first argument named "pop" */
285         if (SB_CompareStr (&Ident, "pop") == 0) {
286
287             Res = PP_POP;
288
289         /* Check if we have a first argument named "push" */
290         } else if (SB_CompareStr (&Ident, "push") == 0) {
291
292             Res = PP_PUSH;
293
294             /* Skip the following comma */
295             if (!GetComma (B)) {
296                 /* Error already flagged by GetComma */
297                 Res = PP_ERROR;
298             }
299
300         } else {
301
302             /* Unknown keyword, roll back */
303             SB_SetIndex (B, Index);
304         }
305     }
306
307     /* Free the string buffer and return the result */
308     SB_Done (&Ident);
309     return Res;
310 }
311
312
313
314 static void PopInt (IntStack* S)
315 /* Pops an integer from an IntStack. Prints an error if the stack is empty */
316 {
317     if (IS_GetCount (S) < 2) {
318         Error ("Cannot pop, stack is empty");
319     } else {
320         IS_Drop (S);
321     }
322 }
323
324
325
326 static void PushInt (IntStack* S, long Val)
327 /* Pushes an integer onto an IntStack. Prints an error if the stack is full */
328 {
329     if (IS_IsFull (S)) {
330         Error ("Cannot push: stack overflow");
331     } else {
332         IS_Push (S, Val);
333     }
334 }
335
336
337
338 static int BoolKeyword (StrBuf* Ident)
339 /* Check if the identifier in Ident is a keyword for a boolean value. Currently
340 ** accepted are true/false/on/off.
341 */
342 {
343     if (SB_CompareStr (Ident, "true") == 0) {
344         return 1;
345     }
346     if (SB_CompareStr (Ident, "on") == 0) {
347         return 1;
348     }
349     if (SB_CompareStr (Ident, "false") == 0) {
350         return 0;
351     }
352     if (SB_CompareStr (Ident, "off") == 0) {
353         return 0;
354     }
355
356     /* Error */
357     Error ("Pragma argument must be one of `on', `off', `true' or `false'");
358     return 0;
359 }
360
361
362
363 /*****************************************************************************/
364 /*                         Pragma handling functions                         */
365 /*****************************************************************************/
366
367
368
369 static void StringPragma (StrBuf* B, void (*Func) (const char*))
370 /* Handle a pragma that expects a string parameter */
371 {
372     StrBuf S = AUTO_STRBUF_INITIALIZER;
373
374     /* We expect a string here */
375     if (GetString (B, &S)) {
376         /* Call the given function with the string argument */
377         Func (SB_GetConstBuf (&S));
378     }
379
380     /* Call the string buf destructor */
381     SB_Done (&S);
382 }
383
384
385
386 static void SegNamePragma (StrBuf* B, segment_t Seg)
387 /* Handle a pragma that expects a segment name parameter */
388 {
389     StrBuf      S = AUTO_STRBUF_INITIALIZER;
390     const char* Name;
391
392     /* Check for the "push" or "pop" keywords */
393     int Push = 0;
394     switch (ParsePushPop (B)) {
395
396         case PP_NONE:
397             break;
398
399         case PP_PUSH:
400             Push = 1;
401             break;
402
403         case PP_POP:
404             /* Pop the old value and output it */
405             PopSegName (Seg);
406             g_segname (Seg);
407
408             /* Done */
409             goto ExitPoint;
410
411         case PP_ERROR:
412             /* Bail out */
413             goto ExitPoint;
414
415         default:
416             Internal ("Invalid result from ParsePushPop");
417
418     }
419
420     /* A string argument must follow */
421     if (!GetString (B, &S)) {
422         goto ExitPoint;
423     }
424
425     /* Get the string */
426     Name = SB_GetConstBuf (&S);
427
428     /* Check if the name is valid */
429     if (ValidSegName (Name)) {
430
431         /* Set the new name */
432         if (Push) {
433             PushSegName (Seg, Name);
434         } else {
435             SetSegName (Seg, Name);
436         }
437         g_segname (Seg);
438
439     } else {
440
441         /* Segment name is invalid */
442         Error ("Illegal segment name: `%s'", Name);
443
444     }
445
446 ExitPoint:
447     /* Call the string buf destructor */
448     SB_Done (&S);
449 }
450
451
452 static void WrappedCallPragma (StrBuf* B)
453 /* Handle the wrapped-call pragma */
454 {
455     StrBuf      S = AUTO_STRBUF_INITIALIZER;
456     const char *Name;
457     long Val;
458     SymEntry *Entry;
459
460     /* Check for the "push" or "pop" keywords */
461     switch (ParsePushPop (B)) {
462
463         case PP_NONE:
464             Error ("Push or pop required");
465             break;
466
467         case PP_PUSH:
468             break;
469
470         case PP_POP:
471             PopWrappedCall();
472
473             /* Done */
474             goto ExitPoint;
475
476         case PP_ERROR:
477             /* Bail out */
478             goto ExitPoint;
479
480         default:
481             Internal ("Invalid result from ParsePushPop");
482
483     }
484
485     /* A symbol argument must follow */
486     if (!SB_GetSym (B, &S, NULL)) {
487         goto ExitPoint;
488     }
489
490     /* Skip the following comma */
491     if (!GetComma (B)) {
492         /* Error already flagged by GetComma */
493         Error ("Value required for wrapped-call identifier");
494         goto ExitPoint;
495     }
496
497     if (!GetNumber (B, &Val)) {
498         Error ("Value required for wrapped-call identifier");
499         goto ExitPoint;
500     }
501
502     if (Val < 0 || Val > 255) {
503         Error ("Identifier must be between 0-255");
504         goto ExitPoint;
505     }
506
507     /* Get the string */
508     Name = SB_GetConstBuf (&S);
509     Entry = FindSym(Name);
510
511     /* Check if the name is valid */
512     if (Entry && Entry->Flags & SC_FUNC) {
513
514         PushWrappedCall(Entry, Val);
515         Entry->Flags |= SC_REF;
516
517     } else {
518
519         /* Segment name is invalid */
520         Error ("Wrapped-call target does not exist or is not a function");
521
522     }
523
524 ExitPoint:
525     /* Call the string buf destructor */
526     SB_Done (&S);
527 }
528
529
530
531 static void CharMapPragma (StrBuf* B)
532 /* Change the character map */
533 {
534     long Index, C;
535
536     /* Read the character index */
537     if (!GetNumber (B, &Index)) {
538         return;
539     }
540     if (Index < 0 || Index > 255) {
541         Error ("Character index out of range");
542         return;
543     }
544
545     /* Comma follows */
546     if (!GetComma (B)) {
547         return;
548     }
549
550     /* Read the character code */
551     if (!GetNumber (B, &C)) {
552         return;
553     }
554     if (C < 0 || C > 255) {
555         Error ("Character code out of range");
556         return;
557     }
558
559     /* Warn about remapping character code 0x00
560     ** (except when remapping it back to itself).
561     */
562     if (Index + C != 0 && IS_Get (&WarnRemapZero)) {
563         if (Index == 0) {
564             Warning ("Remapping from 0 is dangerous with string functions");
565         }
566         else if (C == 0) {
567             Warning ("Remapping to 0 can make string functions stop unexpectedly");
568         }
569     }
570
571     /* Remap the character */
572     TgtTranslateSet ((unsigned) Index, (unsigned char) C);
573 }
574
575
576
577 static void WarnPragma (StrBuf* B)
578 /* Enable/disable warnings */
579 {
580     long   Val;
581     int    Push;
582
583     /* A warning name must follow */
584     IntStack* S = GetWarning (B);
585     if (S == 0) {
586         return;
587     }
588
589     /* Comma follows */
590     if (!GetComma (B)) {
591         return;
592     }
593
594     /* Check for the "push" or "pop" keywords */
595     switch (ParsePushPop (B)) {
596
597         case PP_NONE:
598             Push = 0;
599             break;
600
601         case PP_PUSH:
602             Push = 1;
603             break;
604
605         case PP_POP:
606             /* Pop the old value and bail out */
607             PopInt (S);
608             return;
609
610         case PP_ERROR:
611             /* Bail out */
612             return;
613
614         default:
615             Internal ("Invalid result from ParsePushPop");
616     }
617
618     /* Boolean argument follows */
619     if (HasStr (B, "true") || HasStr (B, "on")) {
620         Val = 1;
621     } else if (HasStr (B, "false") || HasStr (B, "off")) {
622         Val = 0;
623     } else if (!SB_GetNumber (B, &Val)) {
624         Error ("Invalid pragma argument");
625         return;
626     }
627
628     /* Set/push the new value */
629     if (Push) {
630         PushInt (S, Val);
631     } else {
632         IS_Set (S, Val);
633     }
634 }
635
636
637
638 static void FlagPragma (StrBuf* B, IntStack* Stack)
639 /* Handle a pragma that expects a boolean paramater */
640 {
641     StrBuf Ident = AUTO_STRBUF_INITIALIZER;
642     long   Val;
643     int    Push;
644
645
646     /* Try to read an identifier */
647     int IsIdent = SB_GetSym (B, &Ident, 0);
648
649     /* Check if we have a first argument named "pop" */
650     if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
651         PopInt (Stack);
652         /* No other arguments allowed */
653         return;
654     }
655
656     /* Check if we have a first argument named "push" */
657     if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
658         Push = 1;
659         if (!GetComma (B)) {
660             goto ExitPoint;
661         }
662         IsIdent = SB_GetSym (B, &Ident, 0);
663     } else {
664         Push = 0;
665     }
666
667     /* Boolean argument follows */
668     if (IsIdent) {
669         Val = BoolKeyword (&Ident);
670     } else if (!GetNumber (B, &Val)) {
671         goto ExitPoint;
672     }
673
674     /* Set/push the new value */
675     if (Push) {
676         PushInt (Stack, Val);
677     } else {
678         IS_Set (Stack, Val);
679     }
680
681 ExitPoint:
682     /* Free the identifier */
683     SB_Done (&Ident);
684 }
685
686
687
688 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
689 /* Handle a pragma that expects an int paramater */
690 {
691     long  Val;
692     int   Push;
693
694     /* Check for the "push" or "pop" keywords */
695     switch (ParsePushPop (B)) {
696
697         case PP_NONE:
698             Push = 0;
699             break;
700
701         case PP_PUSH:
702             Push = 1;
703             break;
704
705         case PP_POP:
706             /* Pop the old value and bail out */
707             PopInt (Stack);
708             return;
709
710         case PP_ERROR:
711             /* Bail out */
712             return;
713
714         default:
715             Internal ("Invalid result from ParsePushPop");
716
717     }
718
719     /* Integer argument follows */
720     if (!GetNumber (B, &Val)) {
721         return;
722     }
723
724     /* Check the argument */
725     if (Val < Low || Val > High) {
726         Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
727         return;
728     }
729
730     /* Set/push the new value */
731     if (Push) {
732         PushInt (Stack, Val);
733     } else {
734         IS_Set (Stack, Val);
735     }
736 }
737
738
739
740 static void ParsePragma (void)
741 /* Parse the contents of the _Pragma statement */
742 {
743     pragma_t Pragma;
744     StrBuf   Ident = AUTO_STRBUF_INITIALIZER;
745
746     /* Create a string buffer from the string literal */
747     StrBuf B = AUTO_STRBUF_INITIALIZER;
748     SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
749
750     /* Skip the string token */
751     NextToken ();
752
753     /* Get the pragma name from the string */
754     SB_SkipWhite (&B);
755     if (!SB_GetSym (&B, &Ident, "-")) {
756         Error ("Invalid pragma");
757         goto ExitPoint;
758     }
759
760     /* Search for the name */
761     Pragma = FindPragma (&Ident);
762
763     /* Do we know this pragma? */
764     if (Pragma == PRAGMA_ILLEGAL) {
765         /* According to the ANSI standard, we're not allowed to generate errors
766         ** for unknown pragmas, but warn about them if enabled (the default).
767         */
768         if (IS_Get (&WarnUnknownPragma)) {
769             Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
770         }
771         goto ExitPoint;
772     }
773
774     /* Check for an open paren */
775     SB_SkipWhite (&B);
776     if (SB_Get (&B) != '(') {
777         Error ("'(' expected");
778         goto ExitPoint;
779     }
780
781     /* Skip white space before the argument */
782     SB_SkipWhite (&B);
783
784     /* Switch for the different pragmas */
785     switch (Pragma) {
786
787         case PRAGMA_ALIGN:
788             IntPragma (&B, &DataAlignment, 1, 4096);
789             break;
790
791         case PRAGMA_ALLOW_EAGER_INLINE:
792             FlagPragma (&B, &EagerlyInlineFuncs);
793             break;
794
795         case PRAGMA_BSSSEG:
796             Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
797             /* FALLTHROUGH */
798         case PRAGMA_BSS_NAME:
799             SegNamePragma (&B, SEG_BSS);
800             break;
801
802         case PRAGMA_CHARMAP:
803             CharMapPragma (&B);
804             break;
805
806         case PRAGMA_CHECKSTACK:
807             Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
808             /* FALLTHROUGH */
809         case PRAGMA_CHECK_STACK:
810             FlagPragma (&B, &CheckStack);
811             break;
812
813         case PRAGMA_CODESEG:
814             Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
815             /* FALLTHROUGH */
816         case PRAGMA_CODE_NAME:
817             SegNamePragma (&B, SEG_CODE);
818             break;
819
820         case PRAGMA_CODESIZE:
821             IntPragma (&B, &CodeSizeFactor, 10, 1000);
822             break;
823
824         case PRAGMA_DATASEG:
825             Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
826             /* FALLTHROUGH */
827         case PRAGMA_DATA_NAME:
828             SegNamePragma (&B, SEG_DATA);
829             break;
830
831         case PRAGMA_INLINE_STDFUNCS:
832             FlagPragma (&B, &InlineStdFuncs);
833             break;
834
835         case PRAGMA_LOCAL_STRINGS:
836             FlagPragma (&B, &LocalStrings);
837             break;
838
839         case PRAGMA_OPTIMIZE:
840             FlagPragma (&B, &Optimize);
841             break;
842
843         case PRAGMA_REGVARADDR:
844             FlagPragma (&B, &AllowRegVarAddr);
845             break;
846
847         case PRAGMA_REGVARS:
848             Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
849             /* FALLTHROUGH */
850         case PRAGMA_REGISTER_VARS:
851             FlagPragma (&B, &EnableRegVars);
852             break;
853
854         case PRAGMA_RODATASEG:
855             Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
856             /* FALLTHROUGH */
857         case PRAGMA_RODATA_NAME:
858             SegNamePragma (&B, SEG_RODATA);
859             break;
860
861         case PRAGMA_SIGNEDCHARS:
862             Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
863             /* FALLTHROUGH */
864         case PRAGMA_SIGNED_CHARS:
865             FlagPragma (&B, &SignedChars);
866             break;
867
868         case PRAGMA_STATICLOCALS:
869             Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
870             /* FALLTHROUGH */
871         case PRAGMA_STATIC_LOCALS:
872             FlagPragma (&B, &StaticLocals);
873             break;
874
875         case PRAGMA_WRAPPED_CALL:
876             WrappedCallPragma(&B);
877             break;
878
879         case PRAGMA_WARN:
880             WarnPragma (&B);
881             break;
882
883         case PRAGMA_WRITABLE_STRINGS:
884             FlagPragma (&B, &WritableStrings);
885             break;
886
887         case PRAGMA_ZPSYM:
888             StringPragma (&B, MakeZPSym);
889             break;
890
891         default:
892             Internal ("Invalid pragma");
893     }
894
895     /* Closing paren expected */
896     SB_SkipWhite (&B);
897     if (SB_Get (&B) != ')') {
898         Error ("')' expected");
899         goto ExitPoint;
900     }
901     SB_SkipWhite (&B);
902
903     /* Allow an optional semicolon to be compatible with the old syntax */
904     if (SB_Peek (&B) == ';') {
905         SB_Skip (&B);
906         SB_SkipWhite (&B);
907     }
908
909     /* Make sure nothing follows */
910     if (SB_Peek (&B) != '\0') {
911         Error ("Unexpected input following pragma directive");
912     }
913
914 ExitPoint:
915     /* Release the string buffers */
916     SB_Done (&B);
917     SB_Done (&Ident);
918 }
919
920
921
922 void DoPragma (void)
923 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
924 {
925     /* Skip the token itself */
926     NextToken ();
927
928     /* We expect an opening paren */
929     if (!ConsumeLParen ()) {
930         return;
931     }
932
933     /* String literal */
934     if (CurTok.Tok != TOK_SCONST) {
935
936         /* Print a diagnostic */
937         Error ("String literal expected");
938
939         /* Try some smart error recovery: Skip tokens until we reach the
940         ** enclosing paren, or a semicolon.
941         */
942         PragmaErrorSkip ();
943
944     } else {
945
946         /* Parse the _Pragma statement */
947         ParsePragma ();
948     }
949
950     /* Closing paren needed */
951     ConsumeRParen ();
952 }