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