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