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