]> git.sur5r.net Git - cc65/blob - src/cc65/preproc.c
8a35d0f27c692e3bb475f098ba65d6a4878d9c77
[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                 /* We have to escape any characters inside replacement that
313                  * may not be part of a string unescaped.
314                  */
315                 while (*Replacement) {
316                     switch (*Replacement) {
317                         case '\"':
318                         case '\\':
319                             keepch ('\\');
320                         /* FALLTHROUGH */
321                         default:
322                             keepch (*Replacement);
323                             break;
324                     }
325                     ++Replacement;
326                 }
327                 keepch ('\"');
328             } else {
329                 keepch ('#');
330                 keepstr (Ident);
331             }
332         } else if (IsQuote (CurC)) {
333             mptr = CopyQuotedString (mptr);
334         } else {
335             *mptr++ = CurC;
336             NextChar ();
337         }
338     }
339
340     /* Reset the line pointer */
341     InitLine (SavePtr);
342 }
343
344
345
346 static int MacroCall (Macro* M)
347 /* Process a function like macro */
348 {
349     int         ArgCount;       /* Macro argument count */
350     unsigned    ParCount;       /* Number of open parenthesis */
351     char        Buf[LINESIZE];  /* Argument buffer */
352     const char* ArgStart;
353     char*       B;
354
355     /* Expect an argument list */
356     SkipBlank ();
357     if (CurC != '(') {
358         PPError ("Illegal macro call");
359         return 0;
360     }
361
362     /* Eat the left paren */
363     NextChar ();
364
365     /* Read the actual macro arguments and store pointers to these arguments
366      * into the array of actual arguments in the macro definition.
367      */
368     ArgCount = 0;
369     ParCount = 0;
370     ArgStart = Buf;
371     B        = Buf;
372     while (1) {
373         if (CurC == '(') {
374             /* Nested parenthesis */
375             *B++ = CurC;
376             NextChar ();
377             ++ParCount;
378         } else if (IsQuote (CurC)) {
379             B = CopyQuotedString (B);
380         } else if (CurC == ',' || CurC == ')') {
381             if (ParCount == 0) {
382                 /* End of actual argument */
383                 *B++ = '\0';
384                 while (IsBlank(*ArgStart)) {
385                     ++ArgStart;
386                 }
387                 if (ArgCount < M->ArgCount) {
388                     M->ActualArgs[ArgCount++] = ArgStart;
389                 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
390                     /* Be sure not to count the single empty argument for a
391                      * macro that does not have arguments.
392                      */
393                     ++ArgCount;
394                 }
395
396                 /* Check for end of macro param list */
397                 if (CurC == ')') {
398                     NextChar ();
399                     break;
400                 }
401
402                 /* Start the next param */
403                 ArgStart = B;
404                 NextChar ();
405             } else {
406                 /* Comma or right paren inside nested parenthesis */
407                 if (CurC == ')') {
408                     --ParCount;
409                 }
410                 *B++ = CurC;
411                 NextChar ();
412             }
413         } else if (IsBlank (CurC)) {
414             /* Squeeze runs of blanks */
415             *B++ = ' ';
416             SkipBlank ();
417         } else if (CurC == '/' && NextC == '*') {
418             *B++ = ' ';
419             OldStyleComment ();
420         } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
421             *B++ = ' ';
422             NewStyleComment ();
423         } else if (CurC == '\0') {
424             /* End of line inside macro argument list - read next line */
425             if (NextLine () == 0) {
426                 return 0;
427             }
428         } else {
429             /* Just copy the character */
430             *B++ = CurC;
431             NextChar ();
432         }
433     }
434
435     /* Compare formal argument count with actual */
436     if (M->ArgCount != ArgCount) {
437         PPError ("Macro argument count mismatch");
438         /* Be sure to make enough empty arguments available */
439         while (ArgCount < M->ArgCount) {
440             M->ActualArgs [ArgCount++] = "";
441         }
442     }
443
444     /* Preprocess the line, replacing macro parameters */
445     ExpandMacroArgs (M);
446
447     /* Done */
448     return 1;
449 }
450
451
452
453 static void ExpandMacro (Macro* M)
454 /* Expand a macro */
455 {
456     /* Check if this is a function like macro */
457     if (M->ArgCount >= 0) {
458         /* Function like macro */
459         if (MacroCall (M) == 0) {
460             ClearLine ();
461         }
462     } else {
463         /* Just copy the replacement text */
464         keepstr (M->Replacement);
465     }
466 }
467
468
469
470 static void DefineMacro (void)
471 /* Handle a macro definition. */
472 {
473     char*       saveptr;
474     ident       Ident;
475     char        Buf[LINESIZE];
476     Macro*      M;
477     Macro*      Existing;
478
479     /* Read the macro name */
480     SkipBlank ();
481     if (!MacName (Ident)) {
482         return;
483     }
484
485     /* Get an existing macro definition with this name */
486     Existing = FindMacro (Ident);
487
488     /* Create a new macro definition */
489     M = NewMacro (Ident);
490
491     /* Check if this is a function like macro */
492     if (CurC == '(') {
493
494         /* Skip the left paren */
495         NextChar ();
496
497         /* Set the marker that this is a function like macro */
498         M->ArgCount = 0;
499
500         /* Read the formal parameter list */
501         while (1) {
502             SkipBlank ();
503             if (CurC == ')')
504                 break;
505             if (MacName (Ident) == 0) {
506                 return;
507             }
508             AddMacroArg (M, Ident);
509             SkipBlank ();
510             if (CurC != ',')
511                 break;
512             NextChar ();
513         }
514
515         /* Check for a right paren and eat it if we find one */
516         if (CurC != ')') {
517             PPError ("`)' expected");
518             ClearLine ();
519             return;
520         }
521         NextChar ();
522     }
523
524     /* Insert the macro into the macro table and allocate the ActualArgs array */
525     InsertMacro (M);
526
527     /* Remove whitespace and comments from the line, store the preprocessed
528      * line into Buf.
529      */
530     SkipBlank ();
531     saveptr = mptr;
532     Pass1 (lptr, Buf);
533     mptr = saveptr;
534
535     /* Create a copy of the replacement */
536     M->Replacement = xstrdup (Buf);
537
538     /* If we have an existing macro, check if the redefinition is identical.
539      * Print a diagnostic if not.
540      */
541     if (Existing) {
542         if (MacroCmp (M, Existing) != 0) {
543             PPError ("Macro redefinition is not identical");
544         }
545     }
546 }
547
548
549
550 /*****************************************************************************/
551 /*                               Preprocessing                               */
552 /*****************************************************************************/
553
554
555
556 static int Pass1 (const char* From, char* To)
557 /* Preprocessor pass 1. Remove whitespace and comments. */
558 {
559     int         done;
560     ident       Ident;
561     int         HaveParen;
562
563     /* Initialize reading from "From" */
564     InitLine (From);
565
566     /* Target is "To" */
567     mptr = To;
568
569     /* Loop removing ws and comments */
570     done = 1;
571     while (CurC != '\0') {
572         if (IsBlank (CurC)) {
573             keepch (' ');
574             SkipBlank ();
575         } else if (IsIdent (CurC)) {
576             SymName (Ident);
577             if (Preprocessing && strcmp(Ident, "defined") == 0) {
578                 /* Handle the "defined" operator */
579                 SkipBlank();
580                 HaveParen = 0;
581                 if (CurC == '(') {
582                     HaveParen = 1;
583                     NextChar ();
584                     SkipBlank();
585                 }
586                 if (!IsIdent (CurC)) {
587                     PPError ("Identifier expected");
588                     *mptr++ = '0';
589                 } else {
590                     SymName (Ident);
591                     *mptr++ = IsMacro (Ident)? '1' : '0';
592                     if (HaveParen) {
593                         SkipBlank();
594                         if (CurC != ')') {
595                             PPError ("`)' expected");
596                         } else {
597                             NextChar ();
598                         }
599                     }
600                 }
601             } else {
602                 if (MaybeMacro (Ident[0])) {
603                     done = 0;
604                 }
605                 keepstr (Ident);
606             }
607         } else if (IsQuote (CurC)) {
608             mptr = CopyQuotedString (mptr);
609         } else if (CurC == '/' && NextC == '*') {
610             keepch (' ');
611             OldStyleComment ();
612         } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
613             keepch (' ');
614             NewStyleComment ();
615         } else {
616             *mptr++ = CurC;
617             NextChar ();
618         }
619     }
620     keepch ('\0');
621     return done;
622 }
623
624
625
626 static int Pass2 (const char* From, char* To)
627 /* Preprocessor pass 2.  Perform macro substitution. */
628 {
629     int         no_chg;
630     ident       Ident;
631     Macro*      M;
632
633     /* Initialize reading from "From" */
634     InitLine (From);
635
636     /* Target is "To" */
637     mptr = To;
638
639     /* Loop substituting macros */
640     no_chg = 1;
641     while (CurC != '\0') {
642         /* If we have an identifier, check if it's a macro */
643         if (IsIdent (CurC)) {
644             SymName (Ident);
645             M = FindMacro (Ident);
646             if (M) {
647                 ExpandMacro (M);
648                 no_chg = 0;
649             } else {
650                 keepstr (Ident);
651             }
652         } else if (IsQuote (CurC)) {
653             mptr = CopyQuotedString (mptr);
654         } else {
655             *mptr++ = CurC;
656             NextChar ();
657         }
658     }
659     return no_chg;
660 }
661
662
663
664 static void xlateline (void)
665 /* Translate one line. */
666 {
667     int cnt;
668     int Done;
669
670     Done = Pass1 (line, mline);
671     if (ExpandMacros == 0) {
672         Done = 1;
673         ExpandMacros = 1;       /* Reset to default */
674     }
675     cnt = 5;
676     do {
677         /* Swap mline and line */
678         char* p = line;
679         line = mline;
680         mline = p;
681         if (Done)
682             break;
683         Done = Pass2 (line, mline);
684         keepch ('\0');
685     } while (--cnt);
686
687     /* Reinitialize line parsing */
688     InitLine (line);
689 }
690
691
692
693 static void DoUndef (void)
694 /* Process the #undef directive */
695 {
696     ident Ident;
697
698     SkipBlank ();
699     if (MacName (Ident)) {
700         UndefineMacro (Ident);
701     }
702 }
703
704
705
706 static int PushIf (int Skip, int Invert, int Cond)
707 /* Push a new if level onto the if stack */
708 {
709     /* Check for an overflow of the if stack */
710     if (IfIndex >= MAX_IFS-1) {
711         PPError ("Too many nested #if clauses");
712         return 1;
713     }
714
715     /* Push the #if condition */
716     ++IfIndex;
717     if (Skip) {
718         IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
719         return 1;
720     } else {
721         IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
722         return (Invert ^ Cond);
723     }
724 }
725
726
727
728 static int DoIf (int Skip)
729 /* Process #if directive */
730 {
731     ExprDesc lval;
732     char* S;
733
734     /* We're about to abuse the compiler expression parser to evaluate the
735      * #if expression. Save the current tokens to come back here later.
736      * NOTE: Yes, this is a hack, but it saves a complete separate expression
737      * evaluation for the preprocessor.
738      */
739     Token sv1 = CurTok;
740     Token sv2 = NextTok;
741
742     /* Make sure the line infos for the tokens won't get removed */
743     if (sv1.LI) {
744         UseLineInfo (sv1.LI);
745     }
746     if (sv2.LI) {
747         UseLineInfo (sv2.LI);
748     }
749
750     /* Remove the #if from the line and add two semicolons as sentinels */
751     SkipBlank ();
752     S = line;
753     while (CurC != '\0') {
754         *S++ = CurC;
755         NextChar ();
756     }
757     *S++ = ';';
758     *S++ = ';';
759     *S   = '\0';
760
761     /* Start over parsing from line */
762     InitLine (line);
763
764     /* Switch into special preprocessing mode */
765     Preprocessing = 1;
766
767     /* Expand macros in this line */
768     xlateline ();
769
770     /* Prime the token pump (remove old tokens from the stream) */
771     NextToken ();
772     NextToken ();
773
774     /* Call the expression parser */
775     ConstExpr (&lval);
776
777     /* End preprocessing mode */
778     Preprocessing = 0;
779
780     /* Reset the old tokens */
781     CurTok  = sv1;
782     NextTok = sv2;
783
784     /* Set the #if condition according to the expression result */
785     return PushIf (Skip, 1, lval.ConstVal != 0);
786 }
787
788
789
790 static int DoIfDef (int skip, int flag)
791 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
792 {
793     ident Ident;
794
795     SkipBlank ();
796     if (MacName (Ident) == 0) {
797         return 0;
798     } else {
799         return PushIf (skip, flag, IsMacro(Ident));
800     }
801 }
802
803
804
805 static void DoInclude (void)
806 /* Open an include file. */
807 {
808     char        RTerm;
809     unsigned    DirSpec;
810
811
812     /* Skip blanks */
813     SkipBlank ();
814
815     /* Get the next char and check for a valid file name terminator. Setup
816      * the include directory spec (SYS/USR) by looking at the terminator.
817      */
818     switch (CurC) {
819
820         case '\"':
821             RTerm   = '\"';
822             DirSpec = INC_USER;
823             break;
824
825         case '<':
826             RTerm   = '>';
827             DirSpec = INC_SYS;
828             break;
829
830         default:
831             PPError ("`\"' or `<' expected");
832             goto Done;
833     }
834     NextChar ();
835
836     /* Copy the filename into mline. Since mline has the same size as the
837      * input line, we don't need to check for an overflow here.
838      */
839     mptr = mline;
840     while (CurC != '\0' && CurC != RTerm) {
841         *mptr++ = CurC;
842         NextChar ();
843     }
844     *mptr = '\0';
845
846     /* Check if we got a terminator */
847     if (CurC != RTerm) {
848         /* No terminator found */
849         PPError ("Missing terminator or file name too long");
850         goto Done;
851     }
852
853     /* Open the include file */
854     OpenIncludeFile (mline, DirSpec);
855
856 Done:
857     /* Clear the remaining line so the next input will come from the new
858      * file (if open)
859      */
860     ClearLine ();
861 }
862
863
864
865 static void DoError (void)
866 /* Print an error */
867 {
868     SkipBlank ();
869     if (CurC == '\0') {
870         PPError ("Invalid #error directive");
871     } else {
872         PPError ("#error: %s", lptr);
873     }
874
875     /* Clear the rest of line */
876     ClearLine ();
877 }
878
879
880
881 void Preprocess (void)
882 /* Preprocess a line */
883 {
884     int         Skip;
885     ident       Directive;
886
887     /* Skip white space at the beginning of the line */
888     SkipBlank ();
889
890     /* Check for stuff to skip */
891     Skip = 0;
892     while (CurC == '\0' || CurC == '#' || Skip) {
893
894         /* Check for preprocessor lines lines */
895         if (CurC == '#') {
896             NextChar ();
897             SkipBlank ();
898             if (CurC == '\0') {
899                 /* Ignore the empty preprocessor directive */
900                 continue;
901             }
902             if (!IsSym (Directive)) {
903                 PPError ("Preprocessor directive expected");
904                 ClearLine ();
905             } else {
906                 switch (FindPPToken (Directive)) {
907
908                     case PP_DEFINE:
909                         if (!Skip) {
910                             DefineMacro ();
911                         }
912                         break;
913
914                     case PP_ELIF:
915                         if (IfIndex >= 0) {
916                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
917
918                                 /* Handle as #else/#if combination */
919                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
920                                     Skip = !Skip;
921                                 }
922                                 IfStack[IfIndex] |= IFCOND_ELSE;
923                                 Skip = DoIf (Skip);
924
925                                 /* #elif doesn't need a terminator */
926                                 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
927                             } else {
928                                 PPError ("Duplicate #else/#elif");
929                             }
930                         } else {
931                             PPError ("Unexpected #elif");
932                         }
933                         break;
934
935                     case PP_ELSE:
936                         if (IfIndex >= 0) {
937                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
938                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
939                                     Skip = !Skip;
940                                 }
941                                 IfStack[IfIndex] |= IFCOND_ELSE;
942                             } else {
943                                 PPError ("Duplicate #else");
944                             }
945                         } else {
946                             PPError ("Unexpected `#else'");
947                         }
948                         break;
949
950                     case PP_ENDIF:
951                         if (IfIndex >= 0) {
952                             /* Remove any clauses on top of stack that do not
953                              * need a terminating #endif.
954                              */
955                             while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
956                                 --IfIndex;
957                             }
958
959                             /* Stack may not be empty here or something is wrong */
960                             CHECK (IfIndex >= 0);
961
962                             /* Remove the clause that needs a terminator */
963                             Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
964                         } else {
965                             PPError ("Unexpected `#endif'");
966                         }
967                         break;
968
969                     case PP_ERROR:
970                         if (!Skip) {
971                             DoError ();
972                         }
973                         break;
974
975                     case PP_IF:
976                         Skip = DoIf (Skip);
977                         break;
978
979                     case PP_IFDEF:
980                         Skip = DoIfDef (Skip, 1);
981                         break;
982
983                     case PP_IFNDEF:
984                         Skip = DoIfDef (Skip, 0);
985                         break;
986
987                     case PP_INCLUDE:
988                         if (!Skip) {
989                             DoInclude ();
990                         }
991                         break;
992
993                     case PP_LINE:
994                         /* Not allowed in strict ANSI mode */
995                         if (!Skip && ANSI) {
996                             PPError ("Preprocessor directive expected");
997                             ClearLine ();
998                         }
999                         break;
1000
1001                     case PP_PRAGMA:
1002                         if (!Skip) {
1003                             /* Don't expand macros in this line */
1004                             ExpandMacros = 0;
1005                             /* #pragma is handled on the scanner level */
1006                             goto Done;
1007                         }
1008                         break;
1009
1010                     case PP_UNDEF:
1011                         if (!Skip) {
1012                             DoUndef ();
1013                         }
1014                         break;
1015
1016                     default:
1017                         PPError ("Preprocessor directive expected");
1018                         ClearLine ();
1019                 }
1020             }
1021
1022         }
1023         if (NextLine () == 0) {
1024             if (IfIndex >= 0) {
1025                 PPError ("`#endif' expected");
1026             }
1027             return;
1028         }
1029         SkipBlank ();
1030     }
1031
1032 Done:
1033     xlateline ();
1034     Print (stdout, 2, "line: %s\n", line);
1035 }
1036