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