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