]> git.sur5r.net Git - cc65/blob - src/cc65/pragma.c
Stopped cc65 from putting redundant .segment directives into its Assembly output.
[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     const char* Name;
390     StrBuf S = AUTO_STRBUF_INITIALIZER;
391     int Push = 0;
392
393     /* Check for the "push" or "pop" keywords */
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
407             /* BSS variables are output at the end of the compilation.  Don't
408             ** bother to change their segment, now.
409             */
410             if (Seg != SEG_BSS) {
411                 g_segname (Seg);
412             }
413
414             /* Done */
415             goto ExitPoint;
416
417         case PP_ERROR:
418             /* Bail out */
419             goto ExitPoint;
420
421         default:
422             Internal ("Invalid result from ParsePushPop");
423
424     }
425
426     /* A string argument must follow */
427     if (!GetString (B, &S)) {
428         goto ExitPoint;
429     }
430
431     /* Get the string */
432     Name = SB_GetConstBuf (&S);
433
434     /* Check if the name is valid */
435     if (ValidSegName (Name)) {
436
437         /* Set the new name */
438         if (Push) {
439             PushSegName (Seg, Name);
440         } else {
441             SetSegName (Seg, Name);
442         }
443
444         /* BSS variables are output at the end of the compilation.  Don't
445         ** bother to change their segment, now.
446         */
447         if (Seg != SEG_BSS) {
448             g_segname (Seg);
449         }
450
451     } else {
452
453         /* Segment name is invalid */
454         Error ("Illegal segment name: `%s'", Name);
455
456     }
457
458 ExitPoint:
459     /* Call the string buf destructor */
460     SB_Done (&S);
461 }
462
463
464 static void WrappedCallPragma (StrBuf* B)
465 /* Handle the wrapped-call pragma */
466 {
467     StrBuf      S = AUTO_STRBUF_INITIALIZER;
468     const char *Name;
469     long Val;
470     SymEntry *Entry;
471
472     /* Check for the "push" or "pop" keywords */
473     switch (ParsePushPop (B)) {
474
475         case PP_NONE:
476             Error ("Push or pop required");
477             break;
478
479         case PP_PUSH:
480             break;
481
482         case PP_POP:
483             PopWrappedCall();
484
485             /* Done */
486             goto ExitPoint;
487
488         case PP_ERROR:
489             /* Bail out */
490             goto ExitPoint;
491
492         default:
493             Internal ("Invalid result from ParsePushPop");
494
495     }
496
497     /* A symbol argument must follow */
498     if (!SB_GetSym (B, &S, NULL)) {
499         goto ExitPoint;
500     }
501
502     /* Skip the following comma */
503     if (!GetComma (B)) {
504         /* Error already flagged by GetComma */
505         Error ("Value required for wrapped-call identifier");
506         goto ExitPoint;
507     }
508
509     if (!GetNumber (B, &Val)) {
510         Error ("Value required for wrapped-call identifier");
511         goto ExitPoint;
512     }
513
514     if (Val < 0 || Val > 255) {
515         Error ("Identifier must be between 0-255");
516         goto ExitPoint;
517     }
518
519     /* Get the string */
520     Name = SB_GetConstBuf (&S);
521     Entry = FindSym(Name);
522
523     /* Check if the name is valid */
524     if (Entry && Entry->Flags & SC_FUNC) {
525
526         PushWrappedCall(Entry, Val);
527         Entry->Flags |= SC_REF;
528         Entry->V.F.Func->Flags |= FD_CALL_WRAPPER;
529
530     } else {
531
532         /* Segment name is invalid */
533         Error ("Wrapped-call target does not exist or is not a function");
534
535     }
536
537 ExitPoint:
538     /* Call the string buf destructor */
539     SB_Done (&S);
540 }
541
542
543
544 static void CharMapPragma (StrBuf* B)
545 /* Change the character map */
546 {
547     long Index, C;
548
549     /* Read the character index */
550     if (!GetNumber (B, &Index)) {
551         return;
552     }
553     if (Index < 0 || Index > 255) {
554         Error ("Character index out of range");
555         return;
556     }
557
558     /* Comma follows */
559     if (!GetComma (B)) {
560         return;
561     }
562
563     /* Read the character code */
564     if (!GetNumber (B, &C)) {
565         return;
566     }
567     if (C < 0 || C > 255) {
568         Error ("Character code out of range");
569         return;
570     }
571
572     /* Warn about remapping character code 0x00
573     ** (except when remapping it back to itself).
574     */
575     if (Index + C != 0 && IS_Get (&WarnRemapZero)) {
576         if (Index == 0) {
577             Warning ("Remapping from 0 is dangerous with string functions");
578         }
579         else if (C == 0) {
580             Warning ("Remapping to 0 can make string functions stop unexpectedly");
581         }
582     }
583
584     /* Remap the character */
585     TgtTranslateSet ((unsigned) Index, (unsigned char) C);
586 }
587
588
589
590 static void WarnPragma (StrBuf* B)
591 /* Enable/disable warnings */
592 {
593     long   Val;
594     int    Push;
595
596     /* A warning name must follow */
597     IntStack* S = GetWarning (B);
598     if (S == 0) {
599         return;
600     }
601
602     /* Comma follows */
603     if (!GetComma (B)) {
604         return;
605     }
606
607     /* Check for the "push" or "pop" keywords */
608     switch (ParsePushPop (B)) {
609
610         case PP_NONE:
611             Push = 0;
612             break;
613
614         case PP_PUSH:
615             Push = 1;
616             break;
617
618         case PP_POP:
619             /* Pop the old value and bail out */
620             PopInt (S);
621             return;
622
623         case PP_ERROR:
624             /* Bail out */
625             return;
626
627         default:
628             Internal ("Invalid result from ParsePushPop");
629     }
630
631     /* Boolean argument follows */
632     if (HasStr (B, "true") || HasStr (B, "on")) {
633         Val = 1;
634     } else if (HasStr (B, "false") || HasStr (B, "off")) {
635         Val = 0;
636     } else if (!SB_GetNumber (B, &Val)) {
637         Error ("Invalid pragma argument");
638         return;
639     }
640
641     /* Set/push the new value */
642     if (Push) {
643         PushInt (S, Val);
644     } else {
645         IS_Set (S, Val);
646     }
647 }
648
649
650
651 static void FlagPragma (StrBuf* B, IntStack* Stack)
652 /* Handle a pragma that expects a boolean paramater */
653 {
654     StrBuf Ident = AUTO_STRBUF_INITIALIZER;
655     long   Val;
656     int    Push;
657
658
659     /* Try to read an identifier */
660     int IsIdent = SB_GetSym (B, &Ident, 0);
661
662     /* Check if we have a first argument named "pop" */
663     if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
664         PopInt (Stack);
665         /* No other arguments allowed */
666         return;
667     }
668
669     /* Check if we have a first argument named "push" */
670     if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
671         Push = 1;
672         if (!GetComma (B)) {
673             goto ExitPoint;
674         }
675         IsIdent = SB_GetSym (B, &Ident, 0);
676     } else {
677         Push = 0;
678     }
679
680     /* Boolean argument follows */
681     if (IsIdent) {
682         Val = BoolKeyword (&Ident);
683     } else if (!GetNumber (B, &Val)) {
684         goto ExitPoint;
685     }
686
687     /* Set/push the new value */
688     if (Push) {
689         PushInt (Stack, Val);
690     } else {
691         IS_Set (Stack, Val);
692     }
693
694 ExitPoint:
695     /* Free the identifier */
696     SB_Done (&Ident);
697 }
698
699
700
701 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
702 /* Handle a pragma that expects an int paramater */
703 {
704     long  Val;
705     int   Push;
706
707     /* Check for the "push" or "pop" keywords */
708     switch (ParsePushPop (B)) {
709
710         case PP_NONE:
711             Push = 0;
712             break;
713
714         case PP_PUSH:
715             Push = 1;
716             break;
717
718         case PP_POP:
719             /* Pop the old value and bail out */
720             PopInt (Stack);
721             return;
722
723         case PP_ERROR:
724             /* Bail out */
725             return;
726
727         default:
728             Internal ("Invalid result from ParsePushPop");
729
730     }
731
732     /* Integer argument follows */
733     if (!GetNumber (B, &Val)) {
734         return;
735     }
736
737     /* Check the argument */
738     if (Val < Low || Val > High) {
739         Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
740         return;
741     }
742
743     /* Set/push the new value */
744     if (Push) {
745         PushInt (Stack, Val);
746     } else {
747         IS_Set (Stack, Val);
748     }
749 }
750
751
752
753 static void ParsePragma (void)
754 /* Parse the contents of the _Pragma statement */
755 {
756     pragma_t Pragma;
757     StrBuf   Ident = AUTO_STRBUF_INITIALIZER;
758
759     /* Create a string buffer from the string literal */
760     StrBuf B = AUTO_STRBUF_INITIALIZER;
761     SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
762
763     /* Skip the string token */
764     NextToken ();
765
766     /* Get the pragma name from the string */
767     SB_SkipWhite (&B);
768     if (!SB_GetSym (&B, &Ident, "-")) {
769         Error ("Invalid pragma");
770         goto ExitPoint;
771     }
772
773     /* Search for the name */
774     Pragma = FindPragma (&Ident);
775
776     /* Do we know this pragma? */
777     if (Pragma == PRAGMA_ILLEGAL) {
778         /* According to the ANSI standard, we're not allowed to generate errors
779         ** for unknown pragmas, but warn about them if enabled (the default).
780         */
781         if (IS_Get (&WarnUnknownPragma)) {
782             Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
783         }
784         goto ExitPoint;
785     }
786
787     /* Check for an open paren */
788     SB_SkipWhite (&B);
789     if (SB_Get (&B) != '(') {
790         Error ("'(' expected");
791         goto ExitPoint;
792     }
793
794     /* Skip white space before the argument */
795     SB_SkipWhite (&B);
796
797     /* Switch for the different pragmas */
798     switch (Pragma) {
799
800         case PRAGMA_ALIGN:
801             IntPragma (&B, &DataAlignment, 1, 4096);
802             break;
803
804         case PRAGMA_ALLOW_EAGER_INLINE:
805             FlagPragma (&B, &EagerlyInlineFuncs);
806             break;
807
808         case PRAGMA_BSSSEG:
809             Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
810             /* FALLTHROUGH */
811         case PRAGMA_BSS_NAME:
812             SegNamePragma (&B, SEG_BSS);
813             break;
814
815         case PRAGMA_CHARMAP:
816             CharMapPragma (&B);
817             break;
818
819         case PRAGMA_CHECKSTACK:
820             Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
821             /* FALLTHROUGH */
822         case PRAGMA_CHECK_STACK:
823             FlagPragma (&B, &CheckStack);
824             break;
825
826         case PRAGMA_CODESEG:
827             Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
828             /* FALLTHROUGH */
829         case PRAGMA_CODE_NAME:
830             SegNamePragma (&B, SEG_CODE);
831             break;
832
833         case PRAGMA_CODESIZE:
834             IntPragma (&B, &CodeSizeFactor, 10, 1000);
835             break;
836
837         case PRAGMA_DATASEG:
838             Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
839             /* FALLTHROUGH */
840         case PRAGMA_DATA_NAME:
841             SegNamePragma (&B, SEG_DATA);
842             break;
843
844         case PRAGMA_INLINE_STDFUNCS:
845             FlagPragma (&B, &InlineStdFuncs);
846             break;
847
848         case PRAGMA_LOCAL_STRINGS:
849             FlagPragma (&B, &LocalStrings);
850             break;
851
852         case PRAGMA_OPTIMIZE:
853             FlagPragma (&B, &Optimize);
854             break;
855
856         case PRAGMA_REGVARADDR:
857             FlagPragma (&B, &AllowRegVarAddr);
858             break;
859
860         case PRAGMA_REGVARS:
861             Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
862             /* FALLTHROUGH */
863         case PRAGMA_REGISTER_VARS:
864             FlagPragma (&B, &EnableRegVars);
865             break;
866
867         case PRAGMA_RODATASEG:
868             Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
869             /* FALLTHROUGH */
870         case PRAGMA_RODATA_NAME:
871             SegNamePragma (&B, SEG_RODATA);
872             break;
873
874         case PRAGMA_SIGNEDCHARS:
875             Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
876             /* FALLTHROUGH */
877         case PRAGMA_SIGNED_CHARS:
878             FlagPragma (&B, &SignedChars);
879             break;
880
881         case PRAGMA_STATICLOCALS:
882             Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
883             /* FALLTHROUGH */
884         case PRAGMA_STATIC_LOCALS:
885             FlagPragma (&B, &StaticLocals);
886             break;
887
888         case PRAGMA_WRAPPED_CALL:
889             WrappedCallPragma(&B);
890             break;
891
892         case PRAGMA_WARN:
893             WarnPragma (&B);
894             break;
895
896         case PRAGMA_WRITABLE_STRINGS:
897             FlagPragma (&B, &WritableStrings);
898             break;
899
900         case PRAGMA_ZPSYM:
901             StringPragma (&B, MakeZPSym);
902             break;
903
904         default:
905             Internal ("Invalid pragma");
906     }
907
908     /* Closing paren expected */
909     SB_SkipWhite (&B);
910     if (SB_Get (&B) != ')') {
911         Error ("')' expected");
912         goto ExitPoint;
913     }
914     SB_SkipWhite (&B);
915
916     /* Allow an optional semicolon to be compatible with the old syntax */
917     if (SB_Peek (&B) == ';') {
918         SB_Skip (&B);
919         SB_SkipWhite (&B);
920     }
921
922     /* Make sure nothing follows */
923     if (SB_Peek (&B) != '\0') {
924         Error ("Unexpected input following pragma directive");
925     }
926
927 ExitPoint:
928     /* Release the string buffers */
929     SB_Done (&B);
930     SB_Done (&Ident);
931 }
932
933
934
935 void DoPragma (void)
936 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
937 {
938     /* Skip the token itself */
939     NextToken ();
940
941     /* We expect an opening paren */
942     if (!ConsumeLParen ()) {
943         return;
944     }
945
946     /* String literal */
947     if (CurTok.Tok != TOK_SCONST) {
948
949         /* Print a diagnostic */
950         Error ("String literal expected");
951
952         /* Try some smart error recovery: Skip tokens until we reach the
953         ** enclosing paren, or a semicolon.
954         */
955         PragmaErrorSkip ();
956
957     } else {
958
959         /* Parse the _Pragma statement */
960         ParsePragma ();
961     }
962
963     /* Closing paren needed */
964     ConsumeRParen ();
965 }