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