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.
35 /*****************************************************************************/
37 /*****************************************************************************/
41 /* Set when the preprocessor calls expr() recursively */
42 unsigned char Preprocessing = 0;
44 /* Management data for #if */
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;
53 /* Buffer for macro expansion */
56 /* Structure used when expanding macros */
57 typedef struct MacroExp MacroExp;
59 Collection ActualArgs; /* Actual arguments */
60 StrBuf Replacement; /* Replacement with arguments substituted */
61 Macro* M; /* The macro we're handling */
66 /*****************************************************************************/
68 /*****************************************************************************/
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.
77 static void MacroReplacement (StrBuf* Source, StrBuf* Target);
78 /* Perform macro replacement. */
82 /*****************************************************************************/
83 /* Low level preprocessor token handling */
84 /*****************************************************************************/
88 /* Types of preprocessor tokens */
107 /* Preprocessor keyword to token mapping table */
108 static const struct PPToken {
109 const char* Key; /* Keyword */
110 pptoken_t Tok; /* Token */
112 { "define", PP_DEFINE },
115 { "endif", PP_ENDIF },
116 { "error", PP_ERROR },
118 { "ifdef", PP_IFDEF },
119 { "ifndef", PP_IFNDEF },
120 { "include", PP_INCLUDE },
122 { "pragma", PP_PRAGMA },
123 { "undef", PP_UNDEF },
126 /* Number of preprocessor tokens */
127 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
131 static int CmpToken (const void* Key, const void* Elem)
132 /* Compare function for bsearch */
134 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
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.
145 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
146 return P? P->Tok : PP_ILLEGAL;
151 /*****************************************************************************/
152 /* struct MacroExp */
153 /*****************************************************************************/
157 static MacroExp* InitMacroExp (MacroExp* E, Macro* M)
158 /* Initialize a MacroExp structure */
160 InitCollection (&E->ActualArgs);
161 InitStrBuf (&E->Replacement);
168 static void DoneMacroExp (MacroExp* E)
169 /* Cleanup after use of a MacroExp structure */
173 /* Delete the list with actual arguments */
174 for (I = 0; I < CollCount (&E->ActualArgs); ++I) {
175 FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I));
177 DoneCollection (&E->ActualArgs);
178 DoneStrBuf (&E->Replacement);
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!
188 /* Create a new string buffer */
189 StrBuf* A = NewStrBuf ();
191 /* Move the contents of Arg to A */
194 /* Add A to the actual arguments */
195 CollAppend (&E->ActualArgs, A);
200 static StrBuf* ME_GetActual (MacroExp* E, unsigned Index)
201 /* Return an actual macro argument with the given index */
203 return CollAt (&E->ActualArgs, Index);
208 static int ME_ArgIsVariadic (const MacroExp* E)
209 /* Return true if the next actual argument we will add is a variadic one */
211 return (E->M->Variadic &&
212 E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1);
217 /*****************************************************************************/
219 /*****************************************************************************/
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.
230 /* Add a starting quote */
231 SB_AppendChar (Target, '\"');
233 /* Replace any characters inside the string may not be part of a string
236 while ((C = SB_Get (Source)) != '\0') {
240 SB_AppendChar (Target, '\\');
243 SB_AppendChar (Target, C);
248 /* Add the closing quote */
249 SB_AppendChar (Target, '\"');
254 static void OldStyleComment (void)
255 /* Remove an old style C comment from line. */
257 /* Remember the current line number, so we can output better error
258 * messages if the comment is not terminated in the current file.
260 unsigned StartingLine = GetCurrentLine();
262 /* Skip the start of comment chars */
266 /* Skip the comment */
267 while (CurC != '*' || NextC != '/') {
269 if (NextLine () == 0) {
270 PPError ("End-of-file reached in comment starting at line %u",
275 if (CurC == '/' && NextC == '*') {
276 PPWarning ("`/*' found inside a comment");
282 /* Skip the end of comment chars */
289 static void NewStyleComment (void)
290 /* Remove a new style C comment from line. */
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.
299 } while (CurC != '\n' && CurC != '\0');
307 static void SkipWhitespace (void)
308 /* Skip white space in the input stream. */
310 while (IsSpace (CurC)) {
317 static void CopyQuotedString (StrBuf* Target)
318 /* Copy a single or double quoted string from the input to Target. */
320 /* Remember the quote character, copy it to the target buffer and skip it */
322 SB_AppendChar (Target, CurC);
325 /* Copy the characters inside the string */
326 while (CurC != '\0' && CurC != Quote) {
327 /* Keep an escaped char */
329 SB_AppendChar (Target, CurC);
332 /* Copy the character */
333 SB_AppendChar (Target, CurC);
337 /* If we had a terminating quote, copy it */
339 SB_AppendChar (Target, CurC);
346 /*****************************************************************************/
348 /*****************************************************************************/
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.
357 if (IsSym (Ident) == 0) {
358 PPError ("Identifier expected");
368 static void ReadMacroArgs (MacroExp* E)
369 /* Identify the arguments to a macro call */
371 unsigned Parens; /* Number of open parenthesis */
372 StrBuf Arg = STATIC_STRBUF_INITIALIZER;
374 /* Read the actual macro arguments */
379 /* Nested parenthesis */
380 SB_AppendChar (&Arg, CurC);
384 } else if (IsQuote (CurC)) {
386 /* Quoted string - just copy */
387 CopyQuotedString (&Arg);
389 } else if (CurC == ',' || CurC == ')') {
392 /* Comma or right paren inside nested parenthesis */
396 SB_AppendChar (&Arg, CurC);
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.
402 SB_AppendChar (&Arg, CurC);
405 /* End of actual argument. Remove whitespace from the end. */
406 while (IsSpace (SB_LookAtLast (&Arg))) {
410 /* If this is not the single empty argument for a macro with
411 * an empty argument list, remember it.
413 if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) {
414 ME_AppendActual (E, &Arg);
417 /* Check for end of macro param list */
423 /* Start the next param */
427 } else if (IsSpace (CurC)) {
428 /* Squeeze runs of blanks within an arg */
429 if (SB_NotEmpty (&Arg)) {
430 SB_AppendChar (&Arg, ' ');
433 } else if (CurC == '/' && NextC == '*') {
434 if (SB_NotEmpty (&Arg)) {
435 SB_AppendChar (&Arg, ' ');
438 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
439 if (SB_NotEmpty (&Arg)) {
440 SB_AppendChar (&Arg, ' ');
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, ' ');
448 if (NextLine () == 0) {
453 /* Just copy the character */
454 SB_AppendChar (&Arg, CurC);
459 /* Deallocate string buf resources */
465 static void MacroArgSubst (MacroExp* E)
466 /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */
475 /* Remember the current input and switch to the macro replacement. */
476 SB_Reset (&E->M->Replacement);
477 OldSource = InitLine (&E->M->Replacement);
479 /* Argument handling loop */
480 while (CurC != '\0') {
482 /* If we have an identifier, check if it's a macro */
485 /* Check if it's a macro argument */
486 if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
488 /* A macro argument. Get the corresponding actual argument. */
489 Arg = ME_GetActual (E, ArgIdx);
491 /* Copy any following whitespace */
492 HaveSpace = IsSpace (CurC);
497 /* If a ## operator follows, we have to insert the actual
498 * argument as is, otherwise it must be macro replaced.
500 if (CurC == '#' && NextC == '#') {
502 /* ### Add placemarker if necessary */
503 SB_Append (&E->Replacement, Arg);
507 /* Replace the formal argument by a macro replaced copy
511 MacroReplacement (Arg, &E->Replacement);
513 /* If we skipped whitespace before, re-add it now */
515 SB_AppendChar (&E->Replacement, ' ');
522 /* An identifier, keep it */
523 SB_AppendStr (&E->Replacement, Ident);
527 } else if (CurC == '#' && NextC == '#') {
534 /* Since we need to concatenate the token sequences, remove
535 * any whitespace that was added to target, since it must come
538 while (IsSpace (SB_LookAtLast (&E->Replacement))) {
539 SB_Drop (&E->Replacement, 1);
542 /* If the next token is an identifier which is a macro argument,
543 * replace it, otherwise do nothing.
547 /* Check if it's a macro argument */
548 if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
550 /* Get the corresponding actual argument and add it. */
551 SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
555 /* Just an ordinary identifier - add as is */
556 SB_AppendStr (&E->Replacement, Ident);
561 } else if (CurC == '#' && E->M->ArgCount >= 0) {
563 /* A # operator within a macro expansion of a function like
564 * macro. Read the following identifier and check if it's a
569 if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) {
570 PPError ("`#' is not followed by a macro parameter");
572 /* Make a valid string from Replacement */
573 Arg = ME_GetActual (E, ArgIdx);
575 Stringize (Arg, &E->Replacement);
578 } else if (IsQuote (CurC)) {
579 CopyQuotedString (&E->Replacement);
581 SB_AppendChar (&E->Replacement, CurC);
587 /* Remove whitespace from the end of the line */
588 while (IsSpace (SB_LookAtLast (&E->Replacement))) {
589 SB_Drop (&E->Replacement, 1);
593 /* Switch back the input */
594 InitLine (OldSource);
599 static void MacroCall (StrBuf* Target, Macro* M)
600 /* Process a function like macro */
604 /* Eat the left paren */
607 /* Initialize our MacroExp structure */
608 InitMacroExp (&E, M);
610 /* Read the actual macro arguments */
613 /* Compare formal and actual argument count */
614 if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) {
616 StrBuf Arg = STATIC_STRBUF_INITIALIZER;
618 /* Argument count mismatch */
619 PPError ("Macro argument count mismatch");
621 /* Be sure to make enough empty arguments available */
622 while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) {
623 ME_AppendActual (&E, &Arg);
627 /* Replace macro arguments handling the # and ## operators */
630 /* Do macro replacement on the macro that already has the parameters
634 MacroReplacement (&E.Replacement, Target);
637 /* Free memory allocated for the macro expansion structure */
643 static void ExpandMacro (StrBuf* Target, Macro* M)
644 /* Expand a macro into Target */
646 /* Check if this is a function like macro */
647 //printf ("Expanding %s(%u)\n", M->Name, ++V);
648 if (M->ArgCount >= 0) {
650 int Whitespace = IsSpace (CurC);
655 /* Function like macro but no parameter list */
656 SB_AppendStr (Target, M->Name);
658 SB_AppendChar (Target, ' ');
661 /* Function like macro */
662 MacroCall (Target, M);
668 InitMacroExp (&E, M);
670 /* Handle # and ## operators for object like macros */
673 /* Do macro replacement on the macro that already has the parameters
677 MacroReplacement (&E.Replacement, Target);
680 /* Free memory allocated for the macro expansion structure */
684 //printf ("Done with %s(%u)\n", M->Name, V--);
689 static void DefineMacro (void)
690 /* Handle a macro definition. */
697 /* Read the macro name */
699 if (!MacName (Ident)) {
703 /* Remember if we're in C89 mode */
704 C89 = (IS_Get (&Standard) == STD_C89);
706 /* Get an existing macro definition with this name */
707 Existing = FindMacro (Ident);
709 /* Create a new macro definition */
710 M = NewMacro (Ident);
712 /* Check if this is a function like macro */
715 /* Skip the left paren */
718 /* Set the marker that this is a function like macro */
721 /* Read the formal parameter list */
728 /* The next token must be either an identifier, or - if not in
729 * C89 mode - the ellipsis.
731 if (!C89 && CurC == '.') {
734 if (CurC != '.' || NextC != '.') {
735 PPError ("`...' expected");
741 strcpy (Ident, "__VA_ARGS__");
744 /* Must be macro argument name */
745 if (MacName (Ident) == 0) {
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");
756 /* Add the macro argument */
757 AddMacroArg (M, Ident);
759 if (M->Variadic || CurC != ',') {
765 /* Check for a right paren and eat it if we find one */
767 PPError ("`)' expected");
774 /* Skip whitespace before the macro replacement */
777 /* Insert the macro into the macro table and allocate the ActualArgs array */
780 /* Remove whitespace and comments from the line, store the preprocessed
781 * line into the macro replacement buffer.
783 Pass1 (Line, &M->Replacement);
785 /* Remove whitespace from the end of the line */
786 while (IsSpace (SB_LookAtLast (&M->Replacement))) {
787 SB_Drop (&M->Replacement, 1);
790 //printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
792 /* If we have an existing macro, check if the redefinition is identical.
793 * Print a diagnostic if not.
795 if (Existing && MacroCmp (M, Existing) != 0) {
796 PPError ("Macro redefinition is not identical");
802 /*****************************************************************************/
804 /*****************************************************************************/
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.
817 /* Switch to the new input source */
818 StrBuf* OldSource = InitLine (Source);
820 /* Loop removing ws and comments */
822 while (CurC != '\0') {
823 if (IsSpace (CurC)) {
824 /* Squeeze runs of blanks */
825 if (!IsSpace (SB_LookAtLast (Target))) {
826 SB_AppendChar (Target, ' ');
829 } else if (IsSym (Ident)) {
830 if (Preprocessing && strcmp (Ident, "defined") == 0) {
831 /* Handle the "defined" operator */
840 SB_AppendChar (Target, IsMacro (Ident)? '1' : '0');
844 PPError ("`)' expected");
850 PPError ("Identifier expected");
851 SB_AppendChar (Target, '0');
855 SB_AppendStr (Target, Ident);
857 } else if (IsQuote (CurC)) {
858 CopyQuotedString (Target);
859 } else if (CurC == '/' && NextC == '*') {
860 if (!IsSpace (SB_LookAtLast (Target))) {
861 SB_AppendChar (Target, ' ');
864 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
865 if (!IsSpace (SB_LookAtLast (Target))) {
866 SB_AppendChar (Target, ' ');
870 SB_AppendChar (Target, CurC);
875 /* Switch back to the old source */
876 InitLine (OldSource);
878 /* Return the number of identifiers found in the line */
884 static void MacroReplacement (StrBuf* Source, StrBuf* Target)
885 /* Perform macro replacement. */
890 /* Remember the current input and switch to Source */
891 StrBuf* OldSource = InitLine (Source);
893 /* Loop substituting macros */
894 while (CurC != '\0') {
895 /* If we have an identifier, check if it's a macro */
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);
902 /* An identifier, keep it */
903 SB_AppendStr (Target, Ident);
905 } else if (IsQuote (CurC)) {
906 CopyQuotedString (Target);
907 } else if (IsSpace (CurC)) {
908 if (!IsSpace (SB_LookAtLast (Target))) {
909 SB_AppendChar (Target, CurC);
913 SB_AppendChar (Target, CurC);
918 /* Switch back the input */
919 InitLine (OldSource);
924 static void PreprocessLine (void)
925 /* Translate one line. */
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.
931 if (Pass1 (Line, MLine) > 0) {
932 MLine = InitLine (MLine);
935 MacroReplacement (Line, MLine);
938 /* Read from the new line */
940 MLine = InitLine (MLine);
945 static void DoUndef (void)
946 /* Process the #undef directive */
951 if (MacName (Ident)) {
952 UndefineMacro (Ident);
958 static int PushIf (int Skip, int Invert, int Cond)
959 /* Push a new if level onto the if stack */
961 /* Check for an overflow of the if stack */
962 if (IfIndex >= MAX_IFS-1) {
963 PPError ("Too many nested #if clauses");
967 /* Push the #if condition */
970 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
973 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
974 return (Invert ^ Cond);
980 static int DoIf (int Skip)
981 /* Process #if directive */
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.
990 Token SavedCurTok = CurTok;
991 Token SavedNextTok = NextTok;
993 /* Make sure the line infos for the tokens won't get removed */
994 if (SavedCurTok.LI) {
995 UseLineInfo (SavedCurTok.LI);
997 if (SavedNextTok.LI) {
998 UseLineInfo (SavedNextTok.LI);
1002 /* Remove the #if from the line */
1005 while (CurC != '\0') {
1011 /* Start over parsing from line */
1015 /* Switch into special preprocessing mode */
1018 /* Expand macros in this line */
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.
1025 SB_AppendStr (Line, ";;");
1027 /* Load CurTok and NextTok with tokens from the new input */
1031 /* Call the expression parser */
1032 ConstExpr (hie1, &Expr);
1034 /* End preprocessing mode */
1037 /* Reset the old tokens */
1038 CurTok = SavedCurTok;
1039 NextTok = SavedNextTok;
1041 /* Set the #if condition according to the expression result */
1042 return PushIf (Skip, 1, Expr.IVal != 0);
1047 static int DoIfDef (int skip, int flag)
1048 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
1053 if (MacName (Ident) == 0) {
1056 return PushIf (skip, flag, IsMacro(Ident));
1062 static void DoInclude (void)
1063 /* Open an include file. */
1067 StrBuf Filename = STATIC_STRBUF_INITIALIZER;
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.
1089 PPError ("`\"' or `<' expected");
1094 /* Get a copy of the filename */
1095 while (CurC != '\0' && CurC != RTerm) {
1096 SB_AppendChar (&Filename, CurC);
1099 SB_Terminate (&Filename);
1101 /* Check if we got a terminator */
1102 if (CurC != RTerm) {
1103 /* No terminator found */
1104 PPError ("Missing terminator or file name too long");
1108 /* Open the include file */
1109 OpenIncludeFile (SB_GetConstBuf (&Filename), DirSpec);
1112 /* Free the allocated filename data */
1113 DoneStrBuf (&Filename);
1115 /* Clear the remaining line so the next input will come from the new
1123 static void DoError (void)
1124 /* Print an error */
1128 PPError ("Invalid #error directive");
1130 PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
1133 /* Clear the rest of line */
1139 static void DoPragma (void)
1140 /* Handle a #pragma line by converting the #pragma preprocessor directive into
1141 * the _Pragma() compiler operator.
1144 /* Skip blanks following the #pragma directive */
1147 /* Copy the remainder of the line into MLine removing comments and ws */
1149 Pass1 (Line, MLine);
1151 /* Convert the directive into the operator */
1152 SB_CopyStr (Line, "_Pragma (");
1154 Stringize (MLine, Line);
1155 SB_AppendChar (Line, ')');
1157 /* Initialize reading from line */
1164 void Preprocess (void)
1165 /* Preprocess a line */
1170 /* Create the output buffer if we don't already have one */
1172 MLine = NewStrBuf ();
1175 /* Skip white space at the beginning of the line */
1178 /* Check for stuff to skip */
1180 while (CurC == '\0' || CurC == '#' || Skip) {
1182 /* Check for preprocessor lines lines */
1187 /* Ignore the empty preprocessor directive */
1190 if (!IsSym (Directive)) {
1191 PPError ("Preprocessor directive expected");
1194 switch (FindPPToken (Directive)) {
1204 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1206 /* Handle as #else/#if combination */
1207 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1210 IfStack[IfIndex] |= IFCOND_ELSE;
1213 /* #elif doesn't need a terminator */
1214 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
1216 PPError ("Duplicate #else/#elif");
1219 PPError ("Unexpected #elif");
1225 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1226 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1229 IfStack[IfIndex] |= IFCOND_ELSE;
1231 PPError ("Duplicate #else");
1234 PPError ("Unexpected `#else'");
1240 /* Remove any clauses on top of stack that do not
1241 * need a terminating #endif.
1243 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1247 /* Stack may not be empty here or something is wrong */
1248 CHECK (IfIndex >= 0);
1250 /* Remove the clause that needs a terminator */
1251 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1253 PPError ("Unexpected `#endif'");
1268 Skip = DoIfDef (Skip, 1);
1272 Skip = DoIfDef (Skip, 0);
1282 /* Should do something in C99 at least, but we ignore it */
1302 PPError ("Preprocessor directive expected");
1308 if (NextLine () == 0) {
1310 PPError ("`#endif' expected");
1320 if (Verbosity > 1 && SB_NotEmpty (Line)) {
1321 printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (),
1322 SB_GetLen (Line), SB_GetConstBuf (Line));