]> git.sur5r.net Git - cc65/blob - src/cc65/preproc.c
f1135acb23df277611e23f5ea6650d4ccb472e1c
[cc65] / src / cc65 / preproc.c
1
2 /* C pre-processor functions */
3
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <errno.h>
8
9 /* common */
10 #include "chartype.h"
11 #include "check.h"
12 #include "print.h"
13 #include "xmalloc.h"
14
15 /* cc65 */
16 #include "codegen.h"
17 #include "error.h"
18 #include "expr.h"
19 #include "global.h"
20 #include "ident.h"
21 #include "incpath.h"
22 #include "input.h"
23 #include "lineinfo.h"
24 #include "macrotab.h"
25 #include "scanner.h"
26 #include "util.h"
27 #include "preproc.h"
28
29
30
31 /*****************************************************************************/
32 /*                                 Forwards                                  */
33 /*****************************************************************************/
34
35
36
37 static int Pass1 (const char* From, char* To);
38 /* Preprocessor pass 1. Remove whitespace and comments. */
39
40
41
42 /*****************************************************************************/
43 /*                                   Data                                    */
44 /*****************************************************************************/
45
46
47
48 /* Set when the preprocessor calls expr() recursively */
49 unsigned char Preprocessing = 0;
50
51 /* Management data for #if */
52 #define MAX_IFS         64
53 #define IFCOND_NONE     0x00U
54 #define IFCOND_SKIP     0x01U
55 #define IFCOND_ELSE     0x02U
56 #define IFCOND_NEEDTERM 0x04U
57 static unsigned char IfStack[MAX_IFS];
58 static int           IfIndex = -1;
59
60 /* Buffer for macro expansion */
61 static char mlinebuf [LINESIZE];
62 static char* mline = mlinebuf;
63 static char* mptr;
64
65 /* Flag: Expand macros in this line */
66 static int ExpandMacros = 1;
67
68
69
70 /*****************************************************************************/
71 /*                   Low level preprocessor token handling                   */
72 /*****************************************************************************/
73
74
75
76 /* Types of preprocessor tokens */
77 typedef enum {
78     PP_DEFINE,
79     PP_ELIF,
80     PP_ELSE,
81     PP_ENDIF,
82     PP_ERROR,
83     PP_IF,
84     PP_IFDEF,
85     PP_IFNDEF,
86     PP_INCLUDE,
87     PP_LINE,
88     PP_PRAGMA,
89     PP_UNDEF,
90     PP_ILLEGAL
91 } pptoken_t;
92
93
94
95 /* Preprocessor keyword to token mapping table */
96 static const struct PPToken {
97     const char* Key;            /* Keyword */
98     pptoken_t   Tok;            /* Token */
99 } PPTokens[] = {
100     {   "define",       PP_DEFINE       },
101     {   "elif",         PP_ELIF         },
102     {   "else",         PP_ELSE         },
103     {   "endif",        PP_ENDIF        },
104     {   "error",        PP_ERROR        },
105     {   "if",           PP_IF           },
106     {   "ifdef",        PP_IFDEF        },
107     {   "ifndef",       PP_IFNDEF       },
108     {   "include",      PP_INCLUDE      },
109     {   "line",         PP_LINE         },
110     {   "pragma",       PP_PRAGMA       },
111     {   "undef",        PP_UNDEF        },
112 };
113
114 /* Number of preprocessor tokens */
115 #define PPTOKEN_COUNT   (sizeof(PPTokens) / sizeof(PPTokens[0]))
116
117
118
119 static int CmpToken (const void* Key, const void* Elem)
120 /* Compare function for bsearch */
121 {
122     return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
123 }
124
125
126
127 static pptoken_t FindPPToken (const char* Ident)
128 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
129  * is not a valid preprocessor token.
130  */
131 {
132     struct PPToken* P;
133     P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
134     return P? P->Tok : PP_ILLEGAL;
135 }
136
137
138
139 /*****************************************************************************/
140 /*                                   Code                                    */
141 /*****************************************************************************/
142
143
144
145 static void keepch (char c)
146 /* Put character c into translation buffer. */
147 {
148     *mptr++ = c;
149 }
150
151
152
153 static void keepstr (const char* S)
154 /* Put string str into translation buffer. */
155 {
156     unsigned Len = strlen (S);
157     memcpy (mptr, S, Len);
158     mptr += Len;
159 }
160
161
162
163 static void OldStyleComment (void)
164 /* Remove an old style C comment from line. */
165 {
166     /* Remember the current line number, so we can output better error
167      * messages if the comment is not terminated in the current file.
168      */
169     unsigned StartingLine = GetCurrentLine();
170
171     /* Skip the start of comment chars */
172     NextChar ();
173     NextChar ();
174
175     /* Skip the comment */
176     while (CurC != '*' || NextC != '/') {
177         if (CurC == '\0') {
178             if (NextLine () == 0) {
179                 PPError ("End-of-file reached in comment starting at line %u",
180                          StartingLine);
181                 return;
182             }
183         } else {
184             if (CurC == '/' && NextC == '*') {
185                 PPWarning ("`/*' found inside a comment");
186             }
187             NextChar ();
188         }
189     }
190
191     /* Skip the end of comment chars */
192     NextChar ();
193     NextChar ();
194 }
195
196
197
198 static void NewStyleComment (void)
199 /* Remove a new style C comment from line. */
200 {
201     /* Beware: Because line continuation chars are handled when reading
202      * lines, we may only skip til the end of the source line, which
203      * may not be the same as the end of the input line. The end of the
204      * source line is denoted by a lf (\n) character.
205      */
206     do {
207         NextChar ();
208     } while (CurC != '\n' && CurC != '\0');
209     if (CurC == '\n') {
210         NextChar ();
211     }
212 }
213
214
215
216 static void SkipBlank (void)
217 /* Skip blanks and tabs in the input stream. */
218 {
219     while (IsBlank (CurC)) {
220         NextChar ();
221     }
222 }
223
224
225
226 static char* CopyQuotedString (char* Target)
227 /* Copy a single or double quoted string from the input to Target. Return the
228  * new target pointer. Target will not be terminated after the copy.
229  */
230 {
231     /* Remember the quote character, copy it to the target buffer and skip it */
232     char Quote = CurC;
233     *Target++  = CurC;
234     NextChar ();
235
236     /* Copy the characters inside the string */
237     while (CurC != '\0' && CurC != Quote) {
238         /* Keep an escaped char */
239         if (CurC == '\\') {
240             *Target++ = CurC;
241             NextChar ();
242         }
243         /* Copy the character */
244         *Target++ = CurC;
245         NextChar ();
246     }
247
248     /* If we had a terminating quote, copy it */
249     if (CurC != '\0') {
250         *Target++ = CurC;
251         NextChar ();
252     }
253
254     /* Return the new target pointer */
255     return Target;
256 }
257
258
259
260 /*****************************************************************************/
261 /*                                Macro stuff                                */
262 /*****************************************************************************/
263
264
265
266 static int MacName (char* Ident)
267 /* Get a macro symbol name into Ident.  If we have an error, print a
268  * diagnostic message and clear the line.
269  */
270 {
271     if (IsSym (Ident) == 0) {
272         PPError ("Identifier expected");
273         ClearLine ();
274         return 0;
275     } else {
276         return 1;
277     }
278 }
279
280
281
282 static void ExpandMacroArgs (Macro* M)
283 /* Expand the arguments of a macro */
284 {
285     ident       Ident;
286     const char* Replacement;
287     const char* SavePtr;
288
289     /* Save the current line pointer and setup the new ones */
290     SavePtr = lptr;
291     InitLine (M->Replacement);
292
293     /* Copy the macro replacement checking for parameters to replace */
294     while (CurC != '\0') {
295         /* If the next token is an identifier, check for a macro arg */
296         if (IsIdent (CurC)) {
297             SymName (Ident);
298             Replacement = FindMacroArg (M, Ident);
299             if (Replacement) {
300                 /* Macro arg, keep the replacement */
301                 keepstr (Replacement);
302             } else {
303                 /* No macro argument, keep the original identifier */
304                 keepstr (Ident);
305             }
306         } else if (CurC == '#' && IsIdent (NextC)) {
307             NextChar ();
308             SymName (Ident);
309             Replacement = FindMacroArg (M, Ident);
310             if (Replacement) {
311                 keepch ('\"');
312                 keepstr (Replacement);
313                 keepch ('\"');
314             } else {
315                 keepch ('#');
316                 keepstr (Ident);
317             }
318         } else if (IsQuote (CurC)) {
319             mptr = CopyQuotedString (mptr);
320         } else {
321             *mptr++ = CurC;
322             NextChar ();
323         }
324     }
325
326     /* Reset the line pointer */
327     InitLine (SavePtr);
328 }
329
330
331
332 static int MacroCall (Macro* M)
333 /* Process a function like macro */
334 {
335     int         ArgCount;       /* Macro argument count */
336     unsigned    ParCount;       /* Number of open parenthesis */
337     char        Buf[LINESIZE];  /* Argument buffer */
338     const char* ArgStart;
339     char*       B;
340
341     /* Expect an argument list */
342     SkipBlank ();
343     if (CurC != '(') {
344         PPError ("Illegal macro call");
345         return 0;
346     }
347
348     /* Eat the left paren */
349     NextChar ();
350
351     /* Read the actual macro arguments and store pointers to these arguments
352      * into the array of actual arguments in the macro definition.
353      */
354     ArgCount = 0;
355     ParCount = 0;
356     ArgStart = Buf;
357     B        = Buf;
358     while (1) {
359         if (CurC == '(') {
360             /* Nested parenthesis */
361             *B++ = CurC;
362             NextChar ();
363             ++ParCount;
364         } else if (IsQuote (CurC)) {
365             B = CopyQuotedString (B);
366         } else if (CurC == ',' || CurC == ')') {
367             if (ParCount == 0) {
368                 /* End of actual argument */
369                 *B++ = '\0';
370                 while (IsBlank(*ArgStart)) {
371                     ++ArgStart;
372                 }
373                 if (ArgCount < M->ArgCount) {
374                     M->ActualArgs[ArgCount++] = ArgStart;
375                 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
376                     /* Be sure not to count the single empty argument for a
377                      * macro that does not have arguments.
378                      */
379                     ++ArgCount;
380                 }
381
382                 /* Check for end of macro param list */
383                 if (CurC == ')') {
384                     NextChar ();
385                     break;
386                 }
387
388                 /* Start the next param */
389                 ArgStart = B;
390                 NextChar ();
391             } else {
392                 /* Comma or right paren inside nested parenthesis */
393                 if (CurC == ')') {
394                     --ParCount;
395                 }
396                 *B++ = CurC;
397                 NextChar ();
398             }
399         } else if (IsBlank (CurC)) {
400             /* Squeeze runs of blanks */
401             *B++ = ' ';
402             SkipBlank ();
403         } else if (CurC == '/' && NextC == '*') {
404             *B++ = ' ';
405             OldStyleComment ();
406         } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
407             *B++ = ' ';
408             NewStyleComment ();
409         } else if (CurC == '\0') {
410             /* End of line inside macro argument list - read next line */
411             if (NextLine () == 0) {
412                 return 0;
413             }
414         } else {
415             /* Just copy the character */
416             *B++ = CurC;
417             NextChar ();
418         }
419     }
420
421     /* Compare formal argument count with actual */
422     if (M->ArgCount != ArgCount) {
423         PPError ("Macro argument count mismatch");
424         /* Be sure to make enough empty arguments available */
425         while (ArgCount < M->ArgCount) {
426             M->ActualArgs [ArgCount++] = "";
427         }
428     }
429
430     /* Preprocess the line, replacing macro parameters */
431     ExpandMacroArgs (M);
432
433     /* Done */
434     return 1;
435 }
436
437
438
439 static void ExpandMacro (Macro* M)
440 /* Expand a macro */
441 {
442     /* Check if this is a function like macro */
443     if (M->ArgCount >= 0) {
444         /* Function like macro */
445         if (MacroCall (M) == 0) {
446             ClearLine ();
447         }
448     } else {
449         /* Just copy the replacement text */
450         keepstr (M->Replacement);
451     }
452 }
453
454
455
456 static void DefineMacro (void)
457 /* Handle a macro definition. */
458 {
459     char*       saveptr;
460     ident       Ident;
461     char        Buf[LINESIZE];
462     Macro*      M;
463     Macro*      Existing;
464
465     /* Read the macro name */
466     SkipBlank ();
467     if (!MacName (Ident)) {
468         return;
469     }
470
471     /* Get an existing macro definition with this name */
472     Existing = FindMacro (Ident);
473
474     /* Create a new macro definition */
475     M = NewMacro (Ident);
476
477     /* Check if this is a function like macro */
478     if (CurC == '(') {
479
480         /* Skip the left paren */
481         NextChar ();
482
483         /* Set the marker that this is a function like macro */
484         M->ArgCount = 0;
485
486         /* Read the formal parameter list */
487         while (1) {
488             SkipBlank ();
489             if (CurC == ')')
490                 break;
491             if (MacName (Ident) == 0) {
492                 return;
493             }
494             AddMacroArg (M, Ident);
495             SkipBlank ();
496             if (CurC != ',')
497                 break;
498             NextChar ();
499         }
500
501         /* Check for a right paren and eat it if we find one */
502         if (CurC != ')') {
503             PPError ("`)' expected");
504             ClearLine ();
505             return;
506         }
507         NextChar ();
508     }
509
510     /* Insert the macro into the macro table and allocate the ActualArgs array */
511     InsertMacro (M);
512
513     /* Remove whitespace and comments from the line, store the preprocessed
514      * line into Buf.
515      */
516     SkipBlank ();
517     saveptr = mptr;
518     Pass1 (lptr, Buf);
519     mptr = saveptr;
520
521     /* Create a copy of the replacement */
522     M->Replacement = xstrdup (Buf);
523
524     /* If we have an existing macro, check if the redefinition is identical.
525      * Print a diagnostic if not.
526      */
527     if (Existing) {
528         if (MacroCmp (M, Existing) != 0) {
529             PPError ("Macro redefinition is not identical");
530         }
531     }
532 }
533
534
535
536 /*****************************************************************************/
537 /*                               Preprocessing                               */
538 /*****************************************************************************/
539
540
541
542 static int Pass1 (const char* From, char* To)
543 /* Preprocessor pass 1. Remove whitespace and comments. */
544 {
545     int         done;
546     ident       Ident;
547     int         HaveParen;
548
549     /* Initialize reading from "From" */
550     InitLine (From);
551
552     /* Target is "To" */
553     mptr = To;
554
555     /* Loop removing ws and comments */
556     done = 1;
557     while (CurC != '\0') {
558         if (IsBlank (CurC)) {
559             keepch (' ');
560             SkipBlank ();
561         } else if (IsIdent (CurC)) {
562             SymName (Ident);
563             if (Preprocessing && strcmp(Ident, "defined") == 0) {
564                 /* Handle the "defined" operator */
565                 SkipBlank();
566                 HaveParen = 0;
567                 if (CurC == '(') {
568                     HaveParen = 1;
569                     NextChar ();
570                     SkipBlank();
571                 }
572                 if (!IsIdent (CurC)) {
573                     PPError ("Identifier expected");
574                     *mptr++ = '0';
575                 } else {
576                     SymName (Ident);
577                     *mptr++ = IsMacro (Ident)? '1' : '0';
578                     if (HaveParen) {
579                         SkipBlank();
580                         if (CurC != ')') {
581                             PPError ("`)' expected");
582                         } else {
583                             NextChar ();
584                         }
585                     }
586                 }
587             } else {
588                 if (MaybeMacro (Ident[0])) {
589                     done = 0;
590                 }
591                 keepstr (Ident);
592             }
593         } else if (IsQuote (CurC)) {
594             mptr = CopyQuotedString (mptr);
595         } else if (CurC == '/' && NextC == '*') {
596             keepch (' ');
597             OldStyleComment ();
598         } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
599             keepch (' ');
600             NewStyleComment ();
601         } else {
602             *mptr++ = CurC;
603             NextChar ();
604         }
605     }
606     keepch ('\0');
607     return done;
608 }
609
610
611
612 static int Pass2 (const char* From, char* To)
613 /* Preprocessor pass 2.  Perform macro substitution. */
614 {
615     int         no_chg;
616     ident       Ident;
617     Macro*      M;
618
619     /* Initialize reading from "From" */
620     InitLine (From);
621
622     /* Target is "To" */
623     mptr = To;
624
625     /* Loop substituting macros */
626     no_chg = 1;
627     while (CurC != '\0') {
628         /* If we have an identifier, check if it's a macro */
629         if (IsIdent (CurC)) {
630             SymName (Ident);
631             M = FindMacro (Ident);
632             if (M) {
633                 ExpandMacro (M);
634                 no_chg = 0;
635             } else {
636                 keepstr (Ident);
637             }
638         } else if (IsQuote (CurC)) {
639             mptr = CopyQuotedString (mptr);
640         } else {
641             *mptr++ = CurC;
642             NextChar ();
643         }
644     }
645     return no_chg;
646 }
647
648
649
650 static void xlateline (void)
651 /* Translate one line. */
652 {
653     int cnt;
654     int Done;
655
656     Done = Pass1 (line, mline);
657     if (ExpandMacros == 0) {
658         Done = 1;
659         ExpandMacros = 1;       /* Reset to default */
660     }
661     cnt = 5;
662     do {
663         /* Swap mline and line */
664         char* p = line;
665         line = mline;
666         mline = p;
667         if (Done)
668             break;
669         Done = Pass2 (line, mline);
670         keepch ('\0');
671     } while (--cnt);
672
673     /* Reinitialize line parsing */
674     InitLine (line);
675 }
676
677
678
679 static void DoUndef (void)
680 /* Process the #undef directive */
681 {
682     ident Ident;
683
684     SkipBlank ();
685     if (MacName (Ident)) {
686         UndefineMacro (Ident);
687     }
688 }
689
690
691
692 static int PushIf (int Skip, int Invert, int Cond)
693 /* Push a new if level onto the if stack */
694 {
695     /* Check for an overflow of the if stack */
696     if (IfIndex >= MAX_IFS-1) {
697         PPError ("Too many nested #if clauses");
698         return 1;
699     }
700
701     /* Push the #if condition */
702     ++IfIndex;
703     if (Skip) {
704         IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
705         return 1;
706     } else {
707         IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
708         return (Invert ^ Cond);
709     }
710 }
711
712
713
714 static int DoIf (int Skip)
715 /* Process #if directive */
716 {
717     ExprDesc lval;
718     char* S;
719
720     /* We're about to abuse the compiler expression parser to evaluate the
721      * #if expression. Save the current tokens to come back here later.
722      * NOTE: Yes, this is a hack, but it saves a complete separate expression
723      * evaluation for the preprocessor.
724      */
725     Token sv1 = CurTok;
726     Token sv2 = NextTok;
727
728     /* Make sure the line infos for the tokens won't get removed */
729     if (sv1.LI) {
730         UseLineInfo (sv1.LI);
731     }
732     if (sv2.LI) {
733         UseLineInfo (sv2.LI);
734     }
735
736     /* Remove the #if from the line and add two semicolons as sentinels */
737     SkipBlank ();
738     S = line;
739     while (CurC != '\0') {
740         *S++ = CurC;
741         NextChar ();
742     }
743     *S++ = ';';
744     *S++ = ';';
745     *S   = '\0';
746
747     /* Start over parsing from line */
748     InitLine (line);
749
750     /* Switch into special preprocessing mode */
751     Preprocessing = 1;
752
753     /* Expand macros in this line */
754     xlateline ();
755
756     /* Prime the token pump (remove old tokens from the stream) */
757     NextToken ();
758     NextToken ();
759
760     /* Call the expression parser */
761     ConstExpr (&lval);
762
763     /* End preprocessing mode */
764     Preprocessing = 0;
765
766     /* Reset the old tokens */
767     CurTok  = sv1;
768     NextTok = sv2;
769
770     /* Set the #if condition according to the expression result */
771     return PushIf (Skip, 1, lval.ConstVal != 0);
772 }
773
774
775
776 static int DoIfDef (int skip, int flag)
777 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
778 {
779     ident Ident;
780
781     SkipBlank ();
782     if (MacName (Ident) == 0) {
783         return 0;
784     } else {
785         return PushIf (skip, flag, IsMacro(Ident));
786     }
787 }
788
789
790
791 static void DoInclude (void)
792 /* Open an include file. */
793 {
794     char        RTerm;
795     unsigned    DirSpec;
796
797
798     /* Skip blanks */
799     SkipBlank ();
800
801     /* Get the next char and check for a valid file name terminator. Setup
802      * the include directory spec (SYS/USR) by looking at the terminator.
803      */
804     switch (CurC) {
805
806         case '\"':
807             RTerm   = '\"';
808             DirSpec = INC_USER;
809             break;
810
811         case '<':
812             RTerm   = '>';
813             DirSpec = INC_SYS;
814             break;
815
816         default:
817             PPError ("`\"' or `<' expected");
818             goto Done;
819     }
820     NextChar ();
821
822     /* Copy the filename into mline. Since mline has the same size as the
823      * input line, we don't need to check for an overflow here.
824      */
825     mptr = mline;
826     while (CurC != '\0' && CurC != RTerm) {
827         *mptr++ = CurC;
828         NextChar ();
829     }
830     *mptr = '\0';
831
832     /* Check if we got a terminator */
833     if (CurC != RTerm) {
834         /* No terminator found */
835         PPError ("Missing terminator or file name too long");
836         goto Done;
837     }
838
839     /* Open the include file */
840     OpenIncludeFile (mline, DirSpec);
841
842 Done:
843     /* Clear the remaining line so the next input will come from the new
844      * file (if open)
845      */
846     ClearLine ();
847 }
848
849
850
851 static void DoError (void)
852 /* Print an error */
853 {
854     SkipBlank ();
855     if (CurC == '\0') {
856         PPError ("Invalid #error directive");
857     } else {
858         PPError ("#error: %s", lptr);
859     }
860
861     /* Clear the rest of line */
862     ClearLine ();
863 }
864
865
866
867 void Preprocess (void)
868 /* Preprocess a line */
869 {
870     int         Skip;
871     ident       Directive;
872
873     /* Skip white space at the beginning of the line */
874     SkipBlank ();
875
876     /* Check for stuff to skip */
877     Skip = 0;
878     while (CurC == '\0' || CurC == '#' || Skip) {
879
880         /* Check for preprocessor lines lines */
881         if (CurC == '#') {
882             NextChar ();
883             SkipBlank ();
884             if (CurC == '\0') {
885                 /* Ignore the empty preprocessor directive */
886                 continue;
887             }
888             if (!IsSym (Directive)) {
889                 PPError ("Preprocessor directive expected");
890                 ClearLine ();
891             } else {
892                 switch (FindPPToken (Directive)) {
893
894                     case PP_DEFINE:
895                         if (!Skip) {
896                             DefineMacro ();
897                         }
898                         break;
899
900                     case PP_ELIF:
901                         if (IfIndex >= 0) {
902                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
903
904                                 /* Handle as #else/#if combination */
905                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
906                                     Skip = !Skip;
907                                 }
908                                 IfStack[IfIndex] |= IFCOND_ELSE;
909                                 Skip = DoIf (Skip);
910
911                                 /* #elif doesn't need a terminator */
912                                 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
913                             } else {
914                                 PPError ("Duplicate #else/#elif");
915                             }
916                         } else {
917                             PPError ("Unexpected #elif");
918                         }
919                         break;
920
921                     case PP_ELSE:
922                         if (IfIndex >= 0) {
923                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
924                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
925                                     Skip = !Skip;
926                                 }
927                                 IfStack[IfIndex] |= IFCOND_ELSE;
928                             } else {
929                                 PPError ("Duplicate #else");
930                             }
931                         } else {
932                             PPError ("Unexpected `#else'");
933                         }
934                         break;
935
936                     case PP_ENDIF:
937                         if (IfIndex >= 0) {
938                             /* Remove any clauses on top of stack that do not
939                              * need a terminating #endif.
940                              */
941                             while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
942                                 --IfIndex;
943                             }
944
945                             /* Stack may not be empty here or something is wrong */
946                             CHECK (IfIndex >= 0);
947
948                             /* Remove the clause that needs a terminator */
949                             Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
950                         } else {
951                             PPError ("Unexpected `#endif'");
952                         }
953                         break;
954
955                     case PP_ERROR:
956                         if (!Skip) {
957                             DoError ();
958                         }
959                         break;
960
961                     case PP_IF:
962                         Skip = DoIf (Skip);
963                         break;
964
965                     case PP_IFDEF:
966                         Skip = DoIfDef (Skip, 1);
967                         break;
968
969                     case PP_IFNDEF:
970                         Skip = DoIfDef (Skip, 0);
971                         break;
972
973                     case PP_INCLUDE:
974                         if (!Skip) {
975                             DoInclude ();
976                         }
977                         break;
978
979                     case PP_LINE:
980                         /* Not allowed in strict ANSI mode */
981                         if (!Skip && ANSI) {
982                             PPError ("Preprocessor directive expected");
983                             ClearLine ();
984                         }
985                         break;
986
987                     case PP_PRAGMA:
988                         if (!Skip) {
989                             /* Don't expand macros in this line */
990                             ExpandMacros = 0;
991                             /* #pragma is handled on the scanner level */
992                             goto Done;
993                         }
994                         break;
995
996                     case PP_UNDEF:
997                         if (!Skip) {
998                             DoUndef ();
999                         }
1000                         break;
1001
1002                     default:
1003                         PPError ("Preprocessor directive expected");
1004                         ClearLine ();
1005                 }
1006             }
1007
1008         }
1009         if (NextLine () == 0) {
1010             if (IfIndex >= 0) {
1011                 PPError ("`#endif' expected");
1012             }
1013             return;
1014         }
1015         SkipBlank ();
1016     }
1017
1018 Done:
1019     xlateline ();
1020     Print (stdout, 2, "line: %s\n", line);
1021 }
1022