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