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