static void Stringize (StrBuf* Source, StrBuf* Target) /* Stringize the given string: Add double quotes at start and end and preceed * each occurance of " and \ by a backslash. */ { char C; /* Add a starting quote */ SB_AppendChar (Target, '\"'); /* Replace any characters inside the string may not be part of a string * unescaped. */ while ((C = SB_Get (Source)) != '\0') { switch (C) { case '\"': case '\\': SB_AppendChar (Target, '\\'); /* FALLTHROUGH */ default: SB_AppendChar (Target, C); break; } } /* Add the closing quote */ SB_AppendChar (Target, '\"'); }
static void CopyQuotedString (StrBuf* Target) /* Copy a single or double quoted string from the input to Target. */ { /* Remember the quote character, copy it to the target buffer and skip it */ char Quote = CurC; SB_AppendChar (Target, CurC); NextChar (); /* Copy the characters inside the string */ while (CurC != '\0' && CurC != Quote) { /* Keep an escaped char */ if (CurC == '\\') { SB_AppendChar (Target, CurC); NextChar (); } /* Copy the character */ SB_AppendChar (Target, CurC); NextChar (); } /* If we had a terminating quote, copy it */ if (CurC != '\0') { SB_AppendChar (Target, CurC); NextChar (); } }
static void StringConst (void) /* Parse a quoted string */ { /* String buffer */ StrBuf S = AUTO_STRBUF_INITIALIZER; /* Assume next token is a string constant */ NextTok.Tok = TOK_SCONST; /* Concatenate strings. If at least one of the concenated strings is a wide ** character literal, the whole string is a wide char literal, otherwise ** it's a normal string literal. */ while (1) { /* Check if this is a normal or a wide char string */ if (CurC == 'L' && NextC == '\"') { /* Wide character literal */ NextTok.Tok = TOK_WCSCONST; NextChar (); NextChar (); } else if (CurC == '\"') { /* Skip the quote char */ NextChar (); } else { /* No string */ break; } /* Read until end of string */ while (CurC != '\"') { if (CurC == '\0') { Error ("Unexpected newline"); break; } SB_AppendChar (&S, ParseChar ()); } /* Skip closing quote char if there was one */ NextChar (); /* Skip white space, read new input */ SkipWhite (); } /* Terminate the string */ SB_AppendChar (&S, '\0'); /* Add the whole string to the literal pool */ NextTok.SVal = AddLiteralStr (&S); /* Free the buffer */ SB_Done (&S); }
static void VPrintMsg (const FilePos* Pos, const char* Desc, const char* Format, va_list ap) /* Format and output an error/warning message. */ { StrBuf S = STATIC_STRBUF_INITIALIZER; /* Format the actual message */ StrBuf Msg = STATIC_STRBUF_INITIALIZER; SB_VPrintf (&Msg, Format, ap); SB_Terminate (&Msg); /* Format the message header */ SB_Printf (&S, "%s(%u): %s: ", SB_GetConstBuf (GetFileName (Pos->Name)), Pos->Line, Desc); /* Append the message to the message header */ SB_Append (&S, &Msg); /* Delete the formatted message */ SB_Done (&Msg); /* Add a new line and terminate the generated full message */ SB_AppendChar (&S, '\n'); SB_Terminate (&S); /* Output the full message */ fputs (SB_GetConstBuf (&S), stderr); /* Delete the buffer for the full message */ SB_Done (&S); }
int SB_GetSym (StrBuf* B, StrBuf* Ident, const char* SpecialChars) /* Get a symbol from the string buffer. If SpecialChars is not NULL, it ** points to a string that contains characters allowed within the string in ** addition to letters, digits and the underline. Note: The identifier must ** still begin with a letter. ** Returns 1 if a symbol was found and 0 otherwise but doesn't output any ** errors. */ { /* Handle a NULL argument for SpecialChars transparently */ if (SpecialChars == 0) { SpecialChars = ""; } /* Clear Ident */ SB_Clear (Ident); if (IsIdent (SB_Peek (B))) { char C = SB_Peek (B); do { SB_AppendChar (Ident, C); SB_Skip (B); C = SB_Peek (B); } while (IsIdent (C) || IsDigit (C) || (C != '\0' && strchr (SpecialChars, C) != 0)); SB_Terminate (Ident); return 1; } else { return 0; } }
void AddSubSearchPathFromEnv (SearchPaths* P, const char* EnvVar, const char* SubDir) /* Add a search path from an environment variable, adding a subdirectory to ** the environment variable value. */ { StrBuf Dir = AUTO_STRBUF_INITIALIZER; const char* EnvVal = getenv (EnvVar); if (EnvVal == 0) { /* Not found */ return; } /* Copy the environment variable to the buffer */ SB_CopyStr (&Dir, EnvVal); /* Add a path separator if necessary */ if (SB_NotEmpty (&Dir)) { if (SB_LookAtLast (&Dir) != '\\' && SB_LookAtLast (&Dir) != '/') { SB_AppendChar (&Dir, '/'); } } /* Add the subdirectory and terminate the string */ SB_AppendStr (&Dir, SubDir); SB_Terminate (&Dir); /* Add the search path */ AddSearchPath (P, SB_GetConstBuf (&Dir)); /* Free the temp buffer */ SB_Done (&Dir); }
static void ReadStringConst (int StringTerm) /* Read a string constant into SVal. */ { /* Skip the leading string terminator */ NextChar (); /* Read the string */ while (1) { if (C == StringTerm) { break; } if (C == '\n' || C == EOF) { Error ("Newline in string constant"); break; } /* Append the char to the string */ SB_AppendChar (&CurTok.SVal, C); /* Skip the character */ NextChar (); } /* Skip the trailing terminator */ NextChar (); /* Terminate the string */ SB_Terminate (&CurTok.SVal); }
char* SearchFile (const SearchPaths* P, const char* File) /* Search for a file in a list of directories. Return a pointer to a malloced ** area that contains the complete path, if found, return 0 otherwise. */ { char* Name = 0; StrBuf PathName = AUTO_STRBUF_INITIALIZER; /* Start the search */ unsigned I; for (I = 0; I < CollCount (P); ++I) { /* Copy the next path element into the buffer */ SB_CopyStr (&PathName, CollConstAt (P, I)); /* Add a path separator and the filename */ if (SB_NotEmpty (&PathName)) { SB_AppendChar (&PathName, '/'); } SB_AppendStr (&PathName, File); SB_Terminate (&PathName); /* Check if this file exists */ if (access (SB_GetBuf (&PathName), 0) == 0) { /* The file exists, we're done */ Name = xstrdup (SB_GetBuf (&PathName)); break; } } /* Cleanup and return the result of the search */ SB_Done (&PathName); return Name; }
static void DoInclude (void) /* Open an include file. */ { char RTerm; InputType IT; StrBuf Filename = STATIC_STRBUF_INITIALIZER; /* Preprocess the remainder of the line */ PreprocessLine (); /* Skip blanks */ SkipWhitespace (0); /* Get the next char and check for a valid file name terminator. Setup * the include directory spec (SYS/USR) by looking at the terminator. */ switch (CurC) { case '\"': RTerm = '\"'; IT = IT_USRINC; break; case '<': RTerm = '>'; IT = IT_SYSINC; break; default: PPError ("`\"' or `<' expected"); goto Done; } NextChar (); /* Get a copy of the filename */ while (CurC != '\0' && CurC != RTerm) { SB_AppendChar (&Filename, CurC); NextChar (); } SB_Terminate (&Filename); /* Check if we got a terminator */ if (CurC == RTerm) { /* Open the include file */ OpenIncludeFile (SB_GetConstBuf (&Filename), IT); } else if (CurC == '\0') { /* No terminator found */ PPError ("#include expects \"FILENAME\" or <FILENAME>"); } Done: /* Free the allocated filename data */ SB_Done (&Filename); /* Clear the remaining line so the next input will come from the new * file (if open) */ ClearLine (); }
static void WriteOutBuffer(StrBuf *D) { signed i; /* Fix bug in Lynx where the count cannot be 1 */ if (OutIndex == 1) { OutBuffer[OutIndex++] = 0; } /* Write the byte count to the end of the scanline */ if (OutIndex == 255) { Error ("Sprite is too large for the Lynx"); } SB_AppendChar (D, OutIndex+1); /* Write scanline data */ for (i = 0; i < OutIndex; i++) { SB_AppendChar (D, OutBuffer[i]); } }
static void MacroReplacement (StrBuf* Source, StrBuf* Target) /* Perform macro replacement. */ { ident Ident; Macro* M; /* Remember the current input and switch to Source */ StrBuf* OldSource = InitLine (Source); /* Loop substituting macros */ while (CurC != '\0') { /* If we have an identifier, check if it's a macro */ if (IsSym (Ident)) { /* Check if it's a macro */ if ((M = FindMacro (Ident)) != 0 && !M->Expanding) { /* It's a macro, expand it */ ExpandMacro (Target, M); } else { /* An identifier, keep it */ SB_AppendStr (Target, Ident); } } else if (IsQuote (CurC)) { CopyQuotedString (Target); } else if (IsSpace (CurC)) { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, CurC); } NextChar (); } else { SB_AppendChar (Target, CurC); NextChar (); } } /* Switch back the input */ InitLine (OldSource); }
int SB_GetString (StrBuf* B, StrBuf* S) /* Get a string from the string buffer. Returns 1 if a string was found and 0 ** otherwise. Errors are only output in case of invalid strings (missing end ** of string). */ { char C; /* Clear S */ SB_Clear (S); /* A string starts with quote marks */ if (SB_Peek (B) == '\"') { /* String follows, be sure to concatenate strings */ while (SB_Peek (B) == '\"') { /* Skip the quote char */ SB_Skip (B); /* Read the actual string contents */ while ((C = SB_Peek (B)) != '\"') { if (C == '\0') { Error ("Unexpected end of string"); break; } SB_AppendChar (S, ParseChar (B)); } /* Skip the closing quote char if there was one */ SB_Skip (B); /* Skip white space, read new input */ SB_SkipWhite (B); } /* Terminate the string */ SB_Terminate (S); /* Success */ return 1; } else { /* Not a string */ SB_Terminate (S); return 0; } }
Collection* ParseAttrList (const char* List, const char** NameList, unsigned NameCount) /* Parse a list containing name/value pairs into a sorted collection. Some * attributes may not need a name, so NameList contains these names. If there * were no errors, the function returns a alphabetically sorted collection * containing Attr entries. */ { const char* Name; /* Create a new collection */ Collection* C = NewCollection (); /* Name/value pairs are separated by commas */ const char* L = List; StrBuf B = AUTO_STRBUF_INITIALIZER; while (1) { if (*L == ',' || *L == ':' || *L == '\0') { /* Terminate the string */ SB_Terminate (&B); /* Determine the default name */ if (CollCount (C) >= NameCount) { Name = 0; } else { Name = NameList[CollCount (C)]; } /* Split and add this attribute/value pair */ SplitAddAttr (C, SB_GetConstBuf (&B), Name); /* Done, clear the buffer. */ SB_Clear (&B); if (*L == '\0') { break; } } else { SB_AppendChar (&B, *L); } ++L; } /* Free memory */ SB_Done (&B); /* Return the collection with the attributes */ return C; }
static void DefineSymbol (const char* Def) /* Define a symbol from the command line */ { const char* P; long Val; StrBuf SymName = AUTO_STRBUF_INITIALIZER; /* The symbol must start with a character or underline */ if (!IsIdStart (Def [0])) { InvDef (Def); } P = Def; /* Copy the symbol, checking the rest */ while (IsIdChar (*P)) { SB_AppendChar (&SymName, *P++); } SB_Terminate (&SymName); /* Do we have a value given? */ if (*P != '=') { if (*P != '\0') { InvDef (Def); } Val = 0; } else { /* We have a value */ ++P; if (*P == '$') { ++P; if (sscanf (P, "%lx", &Val) != 1) { InvDef (Def); } } else { if (sscanf (P, "%li", &Val) != 1) { InvDef (Def); } } } /* Define the new symbol */ NewSymbol (SB_GetConstBuf (&SymName), Val); /* Release string memory */ SB_Done (&SymName); }
static void ExpandMacro (StrBuf* Target, Macro* M) /* Expand a macro into Target */ { #if 0 static unsigned V = 0; printf ("Expanding %s(%u)\n", M->Name, ++V); #endif /* Check if this is a function like macro */ if (M->ArgCount >= 0) { int Whitespace = SkipWhitespace (1); if (CurC != '(') { /* Function like macro but no parameter list */ SB_AppendStr (Target, M->Name); if (Whitespace) { SB_AppendChar (Target, ' '); } } else { /* Function like macro */ MacroCall (Target, M); } } else { MacroExp E; InitMacroExp (&E, M); /* Handle # and ## operators for object like macros */ MacroArgSubst (&E); /* Do macro replacement on the macro that already has the parameters * substituted. */ M->Expanding = 1; MacroReplacement (&E.Replacement, Target); M->Expanding = 0; /* Free memory allocated for the macro expansion structure */ DoneMacroExp (&E); } #if 0 printf ("Done with %s(%u)\n", M->Name, V--); #endif }
static void ReadIdent (void) /* Read an identifier from the current input position into Ident. Filling SVal ** starts at the current position with the next character in C. It is assumed ** that any characters already filled in are ok, and the character in C is ** checked. */ { /* Read the identifier */ do { SB_AppendChar (&CurTok.SVal, C); NextChar (); } while (IsIdChar (C)); SB_Terminate (&CurTok.SVal); /* If we should ignore case, convert the identifier to upper case */ if (IgnoreCase) { UpcaseSVal (); } }
static void DefineSymbol (const char* Def) /* Define a symbol from the command line */ { const char* P; long Val; StrBuf SymName = AUTO_STRBUF_INITIALIZER; /* The symbol must start with a character or underline */ if (Def [0] != '_' && !IsAlpha (Def [0])) { InvDef (Def); } P = Def; /* Copy the symbol, checking the remainder */ while (IsAlNum (*P) || *P == '_') { SB_AppendChar (&SymName, *P++); } SB_Terminate (&SymName); /* Do we have a value given? */ if (*P != '=') { InvDef (Def); } else { /* We have a value */ ++P; if (*P == '$') { ++P; if (sscanf (P, "%lx", &Val) != 1) { InvDef (Def); } } else { if (sscanf (P, "%li", &Val) != 1) { InvDef (Def); } } } /* Define the new symbol */ CreateConstExport (GetStringId (SB_GetConstBuf (&SymName)), Val); }
static void DoPragma (void) /* Handle a #pragma line by converting the #pragma preprocessor directive into * the _Pragma() compiler operator. */ { /* Skip blanks following the #pragma directive */ SkipWhitespace (0); /* Copy the remainder of the line into MLine removing comments and ws */ SB_Clear (MLine); Pass1 (Line, MLine); /* Convert the directive into the operator */ SB_CopyStr (Line, "_Pragma ("); SB_Reset (MLine); Stringize (MLine, Line); SB_AppendChar (Line, ')'); /* Initialize reading from line */ SB_Reset (Line); InitLine (Line); }
void AsmInc (const char* Filename, char CommentStart, int IgnoreUnknown) /* Read an assembler include file */ { char Buf[1024]; char* L; const char* Comment; unsigned Line; unsigned Len; long Val; unsigned DVal; int Sign; unsigned Base; unsigned Digits; StrBuf Ident = STATIC_STRBUF_INITIALIZER; /* Try to open the file for reading */ FILE* F = fopen (Filename, "r"); if (F == 0) { Error ("Cannot open asm include file \"%s\": %s", Filename, strerror (errno)); } /* Read line by line, check for NAME = VALUE lines */ Line = 0; while ((L = fgets (Buf, sizeof (Buf), F)) != 0) { /* One more line read */ ++Line; /* Ignore leading white space */ while (IsBlank (*L)) { ++L; } /* Remove trailing whitespace */ Len = strlen (L); while (Len > 0 && IsSpace (L[Len-1])) { --Len; } L[Len] = '\0'; /* If the line is empty or starts with a comment char, ignore it */ if (*L == '\0' || *L == CommentStart) { continue; } /* Read an identifier */ SB_Clear (&Ident); if (IsAlpha (*L) || *L == '_') { SB_AppendChar (&Ident, *L++); while (IsAlNum (*L) || *L == '_') { SB_AppendChar (&Ident, *L++); } SB_Terminate (&Ident); } else { if (!IgnoreUnknown) { Error ("%s(%u): Syntax error", Filename, Line); } continue; } /* Ignore white space */ L = SkipWhitespace (L); /* Check for := or = */ if (*L == '=') { ++L; } else if (*L == ':' && *++L == '=') { ++L; } else { if (!IgnoreUnknown) { Error ("%s(%u): Missing `='", Filename, Line); } continue; } /* Allow white space once again */ L = SkipWhitespace (L); /* A number follows. Read the sign. */ if (*L == '-') { Sign = -1; ++L; } else { Sign = 1; if (*L == '+') { ++L; } } /* Determine the base of the number. Allow $ and % as prefixes for * hex and binary numbers respectively. */ if (*L == '$') { Base = 16; ++L; } else if (*L == '%') { Base = 2; ++L; } else { Base = 10; } /* Decode the number */ Digits = 0; Val = 0; while (IsXDigit (*L) && (DVal = DigitVal (*L)) < Base) { Val = (Val * Base) + DVal; ++Digits; ++L; } /* Must have at least one digit */ if (Digits == 0) { if (!IgnoreUnknown) { Error ("%s(%u): Error in number format", Filename, Line); } continue; } /* Skip whitespace again */ L = SkipWhitespace (L); /* Check for a comment */ if (*L == CommentStart) { Comment = SkipWhitespace (L+1); if (*Comment == '\0') { Comment = 0; } } else { Comment = 0; } /* Check for a comment character or end of line */ if (*L != CommentStart && *L != '\0') { if (!IgnoreUnknown) { Error ("%s(%u): Trailing garbage", Filename, Line); } continue; } /* Apply the sign */ Val *= Sign; /* Define the symbol and the comment */ AddExtLabel (Val, SB_GetConstBuf (&Ident)); SetComment (Val, Comment); } /* Delete the string buffer contents */ SB_Done (&Ident); /* Close the include file ignoring errors (we were just reading). */ (void) fclose (F); }
void AddLiteralChar (char C) /* Add one character to the literal pool */ { SB_AppendChar (&LiteralPool, C); }
StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) /* Generate binary output in Lynx sprite format for the bitmap B. The output * is stored in a string buffer (which is actually a dynamic char array) and * returned. * * The Lynx will draw 4 quadrants: * - Down right * - Up right * - Up left * - Down left * * The sprite will end with a byte 0. */ { enum Mode M; StrBuf* D; signed X, Y; unsigned OX, OY; char ColorBits; char ColorMask; char EdgeIndex; /* Get EdgeIndex */ EdgeIndex = GetEdgeIndex (A); /* Action point of the sprite */ OX = GetActionPointX (A); OY = GetActionPointY (A); if (OX >= GetBitmapWidth (B)) { Error ("Action point X cannot be larger than bitmap width"); } if (OY >= GetBitmapHeight (B)) { Error ("Action point Y cannot be larger than bitmap height"); } printf("OX = %d OY = %d\n", OX, OY); /* Output the image properties */ Print (stdout, 1, "Image is %ux%u with %u colors%s\n", GetBitmapWidth (B), GetBitmapHeight (B), GetBitmapColors (B), BitmapIsIndexed (B)? " (indexed)" : ""); /* Get the sprite mode */ M = GetMode (A); /* Now check if bitmap indexes are ok */ if (GetBitmapColors (B) > 16) { Error ("Too many colors for a Lynx sprite"); } ColorBits = 4; ColorMask = 0x0f; if (GetBitmapColors (B) < 9) { ColorBits = 3; ColorMask = 0x07; } if (GetBitmapColors (B) < 5) { ColorBits = 2; ColorMask = 0x03; } if (GetBitmapColors (B) < 3) { ColorBits = 1; ColorMask = 0x01; } /* Create the output buffer and resize it to the required size. */ D = NewStrBuf (); SB_Realloc (D, 63); /* Convert the image for quadrant bottom right */ for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { signed i = 0; signed LastOpaquePixel = -1; char LineBuffer[512]; /* The maximum size is 508 pixels */ /* Fill the LineBuffer for easier optimisation */ for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { /* Fetch next bit into byte buffer */ LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; if (LineBuffer[i] != EdgeIndex) { LastOpaquePixel = i; } ++i; } encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } if ((OY == 0) && (OX == 0)) { /* Trivial case only one quadrant */ /* Mark end of sprite */ SB_AppendChar (D, 0); /* Return the converted bitmap */ return D; } /* Next quadrant */ SB_AppendChar (D, 1); /* Convert the image for quadrant top right */ for (Y = OY - 1; Y >= 0; --Y) { signed i = 0; signed LastOpaquePixel = -1; char LineBuffer[512]; /* The maximum size is 508 pixels */ /* Fill the LineBuffer for easier optimisation */ for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { /* Fetch next bit into byte buffer */ LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; if (LineBuffer[i] != EdgeIndex) { LastOpaquePixel = i; } ++i; } encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } if (OX == 0) { /* Special case only two quadrants */ /* Mark end of sprite */ SB_AppendChar (D, 0); /* Return the converted bitmap */ return D; } /* Next quadrant */ SB_AppendChar (D, 1); /* Convert the image for quadrant top left */ for (Y = OY - 1; Y >= 0; --Y) { signed i = 0; signed LastOpaquePixel = -1; char LineBuffer[512]; /* The maximum size is 508 pixels */ /* Fill the LineBuffer for easier optimisation */ for (X = OX - 1; X >= 0; --X) { /* Fetch next bit into byte buffer */ LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; if (LineBuffer[i] != EdgeIndex) { LastOpaquePixel = i; } ++i; } encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } /* Next quadrant */ SB_AppendChar (D, 1); /* Convert the image for quadrant bottom left */ for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { signed i = 0; signed LastOpaquePixel = -1; char LineBuffer[512]; /* The maximum size is 508 pixels */ /* Fill the LineBuffer for easier optimisation */ for (X = OX - 1; X >= 0; --X) { /* Fetch next bit into byte buffer */ LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; if (LineBuffer[i] != EdgeIndex) { LastOpaquePixel = i; } ++i; } encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } /* End sprite */ SB_AppendChar (D, 0); /* Return the converted bitmap */ return D; }
static unsigned Pass1 (StrBuf* Source, StrBuf* Target) /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments * and the "defined" operator. */ { unsigned IdentCount; ident Ident; int HaveParen; /* Switch to the new input source */ StrBuf* OldSource = InitLine (Source); /* Loop removing ws and comments */ IdentCount = 0; while (CurC != '\0') { if (SkipWhitespace (0)) { /* Squeeze runs of blanks */ if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } } else if (IsSym (Ident)) { if (Preprocessing && strcmp (Ident, "defined") == 0) { /* Handle the "defined" operator */ SkipWhitespace (0); HaveParen = 0; if (CurC == '(') { HaveParen = 1; NextChar (); SkipWhitespace (0); } if (IsSym (Ident)) { SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); if (HaveParen) { SkipWhitespace (0); if (CurC != ')') { PPError ("`)' expected"); } else { NextChar (); } } } else { PPError ("Identifier expected"); SB_AppendChar (Target, '0'); } } else { ++IdentCount; SB_AppendStr (Target, Ident); } } else if (IsQuote (CurC)) { CopyQuotedString (Target); } else if (CurC == '/' && NextC == '*') { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } NewStyleComment (); } else { SB_AppendChar (Target, CurC); NextChar (); } } /* Switch back to the old source */ InitLine (OldSource); /* Return the number of identifiers found in the line */ return IdentCount; }
static void IFNextChar (CharSource* S) /* Read the next character from the input file */ { /* Check for end of line, read the next line if needed */ while (SB_GetIndex (&S->V.File.Line) >= SB_GetLen (&S->V.File.Line)) { unsigned Len; /* End of current line reached, read next line */ SB_Clear (&S->V.File.Line); while (1) { int N = fgetc (S->V.File.F); if (N == EOF) { /* End of file. Accept files without a newline at the end */ if (SB_NotEmpty (&S->V.File.Line)) { break; } /* No more data - add an empty line to the listing. This ** is a small hack needed to keep the PC output in sync. */ NewListingLine (&EmptyStrBuf, S->V.File.Pos.Name, FCount); C = EOF; return; /* Check for end of line */ } else if (N == '\n') { /* End of line */ break; /* Collect other stuff */ } else { /* Append data to line */ SB_AppendChar (&S->V.File.Line, N); } } /* If we come here, we have a new input line. To avoid problems ** with strange line terminators, remove all whitespace from the ** end of the line, then add a single newline. */ Len = SB_GetLen (&S->V.File.Line); while (Len > 0 && IsSpace (SB_AtUnchecked (&S->V.File.Line, Len-1))) { --Len; } SB_Drop (&S->V.File.Line, SB_GetLen (&S->V.File.Line) - Len); SB_AppendChar (&S->V.File.Line, '\n'); /* Terminate the string buffer */ SB_Terminate (&S->V.File.Line); /* One more line */ S->V.File.Pos.Line++; /* Remember the new line for the listing */ NewListingLine (&S->V.File.Line, S->V.File.Pos.Name, FCount); } /* Set the column pointer */ S->V.File.Pos.Col = SB_GetIndex (&S->V.File.Line); /* Return the next character from the buffer */ C = SB_Get (&S->V.File.Line); }
static void ReadStringConst (int StringTerm) /* Read a string constant into SVal. */ { /* Skip the leading string terminator */ NextChar (); /* Read the string */ while (1) { if (C == StringTerm) { break; } if (C == '\n' || C == EOF) { Error ("Newline in string constant"); break; } if (C == '\\' && StringEscapes) { NextChar (); switch (C) { case EOF: Error ("Unterminated escape sequence in string constant"); break; case '\\': case '\'': case '"': break; case 't': C = '\x09'; break; case 'r': C = '\x0D'; break; case 'n': C = '\x0A'; break; case 'x': NextChar (); if (IsXDigit (C)) { char high_nibble = DigitVal (C) << 4; NextChar (); if (IsXDigit (C)) { C = high_nibble | DigitVal (C); break; } } /* otherwise, fall through */ default: Error ("Unsupported escape sequence in string constant"); break; } } /* Append the char to the string */ SB_AppendChar (&CurTok.SVal, C); /* Skip the character */ NextChar (); } /* Skip the trailing terminator */ NextChar (); /* Terminate the string */ SB_Terminate (&CurTok.SVal); }
static void MacroArgSubst (MacroExp* E) /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ { ident Ident; int ArgIdx; StrBuf* OldSource; StrBuf* Arg; int HaveSpace; /* Remember the current input and switch to the macro replacement. */ int OldIndex = SB_GetIndex (&E->M->Replacement); SB_Reset (&E->M->Replacement); OldSource = InitLine (&E->M->Replacement); /* Argument handling loop */ while (CurC != '\0') { /* If we have an identifier, check if it's a macro */ if (IsSym (Ident)) { /* Check if it's a macro argument */ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { /* A macro argument. Get the corresponding actual argument. */ Arg = ME_GetActual (E, ArgIdx); /* Copy any following whitespace */ HaveSpace = SkipWhitespace (0); /* If a ## operator follows, we have to insert the actual * argument as is, otherwise it must be macro replaced. */ if (CurC == '#' && NextC == '#') { /* ### Add placemarker if necessary */ SB_Append (&E->Replacement, Arg); } else { /* Replace the formal argument by a macro replaced copy * of the actual. */ SB_Reset (Arg); MacroReplacement (Arg, &E->Replacement); /* If we skipped whitespace before, re-add it now */ if (HaveSpace) { SB_AppendChar (&E->Replacement, ' '); } } } else { /* An identifier, keep it */ SB_AppendStr (&E->Replacement, Ident); } } else if (CurC == '#' && NextC == '#') { /* ## operator. */ NextChar (); NextChar (); SkipWhitespace (0); /* Since we need to concatenate the token sequences, remove * any whitespace that was added to target, since it must come * from the input. */ while (IsSpace (SB_LookAtLast (&E->Replacement))) { SB_Drop (&E->Replacement, 1); } /* If the next token is an identifier which is a macro argument, * replace it, otherwise do nothing. */ if (IsSym (Ident)) { /* Check if it's a macro argument */ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { /* Get the corresponding actual argument and add it. */ SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); } else { /* Just an ordinary identifier - add as is */ SB_AppendStr (&E->Replacement, Ident); } } } else if (CurC == '#' && E->M->ArgCount >= 0) { /* A # operator within a macro expansion of a function like * macro. Read the following identifier and check if it's a * macro parameter. */ NextChar (); SkipWhitespace (0); if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) { PPError ("`#' is not followed by a macro parameter"); } else { /* Make a valid string from Replacement */ Arg = ME_GetActual (E, ArgIdx); SB_Reset (Arg); Stringize (Arg, &E->Replacement); } } else if (IsQuote (CurC)) { CopyQuotedString (&E->Replacement); } else { SB_AppendChar (&E->Replacement, CurC); NextChar (); } } #if 0 /* Remove whitespace from the end of the line */ while (IsSpace (SB_LookAtLast (&E->Replacement))) { SB_Drop (&E->Replacement, 1); } #endif /* Switch back the input */ InitLine (OldSource); SB_SetIndex (&E->M->Replacement, OldIndex); }
static void ReadMacroArgs (MacroExp* E) /* Identify the arguments to a macro call */ { unsigned Parens; /* Number of open parenthesis */ StrBuf Arg = STATIC_STRBUF_INITIALIZER; /* Read the actual macro arguments */ Parens = 0; while (1) { if (CurC == '(') { /* Nested parenthesis */ SB_AppendChar (&Arg, CurC); NextChar (); ++Parens; } else if (IsQuote (CurC)) { /* Quoted string - just copy */ CopyQuotedString (&Arg); } else if (CurC == ',' || CurC == ')') { if (Parens) { /* Comma or right paren inside nested parenthesis */ if (CurC == ')') { --Parens; } SB_AppendChar (&Arg, CurC); NextChar (); } else if (CurC == ',' && ME_ArgIsVariadic (E)) { /* It's a comma, but we're inside a variadic macro argument, so * just copy it and proceed. */ SB_AppendChar (&Arg, CurC); NextChar (); } else { /* End of actual argument. Remove whitespace from the end. */ while (IsSpace (SB_LookAtLast (&Arg))) { SB_Drop (&Arg, 1); } /* If this is not the single empty argument for a macro with * an empty argument list, remember it. */ if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { ME_AppendActual (E, &Arg); } /* Check for end of macro param list */ if (CurC == ')') { NextChar (); break; } /* Start the next param */ NextChar (); SB_Clear (&Arg); } } else if (SkipWhitespace (1)) { /* Squeeze runs of blanks within an arg */ if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } } else if (CurC == '/' && NextC == '*') { if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } NewStyleComment (); } else if (CurC == '\0') { /* End of input inside macro argument list */ PPError ("Unterminated argument list invoking macro `%s'", E->M->Name); ClearLine (); break; } else { /* Just copy the character */ SB_AppendChar (&Arg, CurC); NextChar (); } } /* Deallocate string buf resources */ SB_Done (&Arg); }
void NextRawTok (void) /* Read the next raw token from the input stream */ { Macro* M; /* If we've a forced end of assembly, don't read further */ if (ForcedEnd) { CurTok.Tok = TOK_EOF; return; } Restart: /* Check if we have tokens from another input source */ if (InputFromStack ()) { if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) { /* This is a define style macro - expand it */ MacExpandStart (M); goto Restart; } return; } Again: /* Skip whitespace, remember if we had some */ if ((CurTok.WS = IsBlank (C)) != 0) { do { NextChar (); } while (IsBlank (C)); } /* Mark the file position of the next token */ Source->Func->MarkStart (Source); /* Clear the string attribute */ SB_Clear (&CurTok.SVal); /* Generate line info for the current token */ NewAsmLine (); /* Hex number or PC symbol? */ if (C == '$') { NextChar (); /* Hex digit must follow or DollarIsPC must be enabled */ if (!IsXDigit (C)) { if (DollarIsPC) { CurTok.Tok = TOK_PC; return; } else { Error ("Hexadecimal digit expected"); } } /* Read the number */ CurTok.IVal = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsXDigit (C)) { Error ("Number may not end with underline"); } } if (IsXDigit (C)) { if (CurTok.IVal & 0xF0000000) { Error ("Overflow in hexadecimal number"); CurTok.IVal = 0; } CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C); NextChar (); } else { break; } } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Binary number? */ if (C == '%') { NextChar (); /* 0 or 1 must follow */ if (!IsBDigit (C)) { Error ("Binary digit expected"); } /* Read the number */ CurTok.IVal = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsBDigit (C)) { Error ("Number may not end with underline"); } } if (IsBDigit (C)) { if (CurTok.IVal & 0x80000000) { Error ("Overflow in binary number"); CurTok.IVal = 0; } CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C); NextChar (); } else { break; } } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Number? */ if (IsDigit (C)) { char Buf[16]; unsigned Digits; unsigned Base; unsigned I; long Max; unsigned DVal; /* Ignore leading zeros */ while (C == '0') { NextChar (); } /* Read the number into Buf counting the digits */ Digits = 0; while (1) { if (UnderlineInNumbers && C == '_') { while (C == '_') { NextChar (); } if (!IsXDigit (C)) { Error ("Number may not end with underline"); } } if (IsXDigit (C)) { /* Buf is big enough to allow any decimal and hex number to ** overflow, so ignore excess digits here, they will be detected ** when we convert the value. */ if (Digits < sizeof (Buf)) { Buf[Digits++] = C; } NextChar (); } else { break; } } /* Allow zilog/intel style hex numbers with a 'h' suffix */ if (C == 'h' || C == 'H') { NextChar (); Base = 16; Max = 0xFFFFFFFFUL / 16; } else { Base = 10; Max = 0xFFFFFFFFUL / 10; } /* Convert the number using the given base */ CurTok.IVal = 0; for (I = 0; I < Digits; ++I) { if (CurTok.IVal > Max) { Error ("Number out of range"); CurTok.IVal = 0; break; } DVal = DigitVal (Buf[I]); if (DVal >= Base) { Error ("Invalid digits in number"); CurTok.IVal = 0; break; } CurTok.IVal = (CurTok.IVal * Base) + DVal; } /* This is an integer constant */ CurTok.Tok = TOK_INTCON; return; } /* Control command? */ if (C == '.') { /* Remember and skip the dot */ NextChar (); /* Check if it's just a dot */ if (!IsIdStart (C)) { /* Just a dot */ CurTok.Tok = TOK_DOT; } else { /* Read the remainder of the identifier */ SB_AppendChar (&CurTok.SVal, '.'); ReadIdent (); /* Dot keyword, search for it */ CurTok.Tok = FindDotKeyword (); if (CurTok.Tok == TOK_NONE) { /* Not found */ if (!LeadingDotInIdents) { /* Invalid pseudo instruction */ Error ("'%m%p' is not a recognized control command", &CurTok.SVal); goto Again; } /* An identifier with a dot. Check if it's a define style ** macro. */ if ((M = FindDefine (&CurTok.SVal)) != 0) { /* This is a define style macro - expand it */ MacExpandStart (M); goto Restart; } /* Just an identifier with a dot */ CurTok.Tok = TOK_IDENT; } } return; } /* Indirect op for sweet16 cpu. Must check this before checking for local ** symbols, because these may also use the '@' symbol. */ if (CPU == CPU_SWEET16 && C == '@') { NextChar (); CurTok.Tok = TOK_AT; return; } /* Local symbol? */ if (C == LocalStart) { /* Read the identifier. */ ReadIdent (); /* Start character alone is not enough */ if (SB_GetLen (&CurTok.SVal) == 1) { Error ("Invalid cheap local symbol"); goto Again; } /* A local identifier */ CurTok.Tok = TOK_LOCAL_IDENT; return; } /* Identifier or keyword? */ if (IsIdStart (C)) { /* Read the identifier */ ReadIdent (); /* Check for special names. Bail out if we have identified the type of ** the token. Go on if the token is an identifier. */ switch (SB_GetLen (&CurTok.SVal)) { case 1: switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) { case 'A': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_ABS; } else { CurTok.Tok = TOK_A; } return; case 'F': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_FAR; return; } break; case 'S': if ((CPU == CPU_4510) || (CPU == CPU_65816)) { CurTok.Tok = TOK_S; return; } break; case 'X': CurTok.Tok = TOK_X; return; case 'Y': CurTok.Tok = TOK_Y; return; case 'Z': if (C == ':') { NextChar (); CurTok.Tok = TOK_OVERRIDE_ZP; return; } else { if (CPU == CPU_4510) { CurTok.Tok = TOK_Z; return; } } break; default: break; } break; case 2: if ((CPU == CPU_4510) && (toupper (SB_AtUnchecked (&CurTok.SVal, 0)) == 'S') && (toupper (SB_AtUnchecked (&CurTok.SVal, 1)) == 'P')) { CurTok.Tok = TOK_S; return; } /* FALL THROUGH */ default: if (CPU == CPU_SWEET16 && (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) { /* A sweet16 register number in sweet16 mode */ CurTok.Tok = TOK_REG; return; } } /* Check for define style macro */ if ((M = FindDefine (&CurTok.SVal)) != 0) { /* Macro - expand it */ MacExpandStart (M); goto Restart; } else { /* An identifier */ CurTok.Tok = TOK_IDENT; } return; } /* Ok, let's do the switch */ CharAgain: switch (C) { case '+': NextChar (); CurTok.Tok = TOK_PLUS; return; case '-': NextChar (); CurTok.Tok = TOK_MINUS; return; case '/': NextChar (); if (C != '*') { CurTok.Tok = TOK_DIV; } else if (CComments) { /* Remember the position, then skip the '*' */ Collection LineInfos = STATIC_COLLECTION_INITIALIZER; GetFullLineInfo (&LineInfos); NextChar (); do { while (C != '*') { if (C == EOF) { LIError (&LineInfos, "Unterminated comment"); ReleaseFullLineInfo (&LineInfos); DoneCollection (&LineInfos); goto CharAgain; } NextChar (); } NextChar (); } while (C != '/'); NextChar (); ReleaseFullLineInfo (&LineInfos); DoneCollection (&LineInfos); goto Again; } return; case '*': NextChar (); CurTok.Tok = TOK_MUL; return; case '^': NextChar (); CurTok.Tok = TOK_XOR; return; case '&': NextChar (); if (C == '&') { NextChar (); CurTok.Tok = TOK_BOOLAND; } else { CurTok.Tok = TOK_AND; } return; case '|': NextChar (); if (C == '|') { NextChar (); CurTok.Tok = TOK_BOOLOR; } else { CurTok.Tok = TOK_OR; } return; case ':': NextChar (); switch (C) { case ':': NextChar (); CurTok.Tok = TOK_NAMESPACE; break; case '-': CurTok.IVal = 0; do { --CurTok.IVal; NextChar (); } while (C == '-'); CurTok.Tok = TOK_ULABEL; break; case '+': CurTok.IVal = 0; do { ++CurTok.IVal; NextChar (); } while (C == '+'); CurTok.Tok = TOK_ULABEL; break; case '=': NextChar (); CurTok.Tok = TOK_ASSIGN; break; default: CurTok.Tok = TOK_COLON; break; } return; case ',': NextChar (); CurTok.Tok = TOK_COMMA; return; case ';': NextChar (); while (C != '\n' && C != EOF) { NextChar (); } goto CharAgain; case '#': NextChar (); CurTok.Tok = TOK_HASH; return; case '(': NextChar (); CurTok.Tok = TOK_LPAREN; return; case ')': NextChar (); CurTok.Tok = TOK_RPAREN; return; case '[': NextChar (); CurTok.Tok = TOK_LBRACK; return; case ']': NextChar (); CurTok.Tok = TOK_RBRACK; return; case '{': NextChar (); CurTok.Tok = TOK_LCURLY; return; case '}': NextChar (); CurTok.Tok = TOK_RCURLY; return; case '<': NextChar (); if (C == '=') { NextChar (); CurTok.Tok = TOK_LE; } else if (C == '<') { NextChar (); CurTok.Tok = TOK_SHL; } else if (C == '>') { NextChar (); CurTok.Tok = TOK_NE; } else { CurTok.Tok = TOK_LT; } return; case '=': NextChar (); CurTok.Tok = TOK_EQ; return; case '!': NextChar (); CurTok.Tok = TOK_BOOLNOT; return; case '>': NextChar (); if (C == '=') { NextChar (); CurTok.Tok = TOK_GE; } else if (C == '>') { NextChar (); CurTok.Tok = TOK_SHR; } else { CurTok.Tok = TOK_GT; } return; case '~': NextChar (); CurTok.Tok = TOK_NOT; return; case '\'': /* Hack: If we allow ' as terminating character for strings, read ** the following stuff as a string, and check for a one character ** string later. */ if (LooseStringTerm) { ReadStringConst ('\''); if (SB_GetLen (&CurTok.SVal) == 1) { CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0); CurTok.Tok = TOK_CHARCON; } else { CurTok.Tok = TOK_STRCON; } } else { /* Always a character constant */ NextChar (); if (C == EOF || IsControl (C)) { Error ("Illegal character constant"); goto CharAgain; } CurTok.IVal = C; CurTok.Tok = TOK_CHARCON; NextChar (); if (C != '\'') { if (!MissingCharTerm) { Error ("Illegal character constant"); } } else { NextChar (); } } return; case '\"': ReadStringConst ('\"'); CurTok.Tok = TOK_STRCON; return; case '\\': /* Line continuation? */ if (LineCont) { NextChar (); /* Next char should be a LF, if not, will result in an error later */ if (C == '\n') { /* Ignore the '\n' */ NextChar (); goto Again; } else { /* Make it clear what the problem is: */ Error ("EOL expected."); } } break; case '\n': NextChar (); CurTok.Tok = TOK_SEP; return; case EOF: CheckInputStack (); /* In case of the main file, do not close it, but return EOF. */ if (Source && Source->Next) { DoneCharSource (); goto Again; } else { CurTok.Tok = TOK_EOF; } return; } /* If we go here, we could not identify the current character. Skip it ** and try again. */ Error ("Invalid input character: 0x%02X", C & 0xFF); NextChar (); goto Again; }
static void NumericConst (void) /* Parse a numeric constant */ { unsigned Base; /* Temporary number base */ unsigned Prefix; /* Base according to prefix */ StrBuf S = STATIC_STRBUF_INITIALIZER; int IsFloat; char C; unsigned DigitVal; unsigned long IVal; /* Value */ /* Check for a leading hex or octal prefix and determine the possible ** integer types. */ if (CurC == '0') { /* Gobble 0 and examine next char */ NextChar (); if (toupper (CurC) == 'X') { Base = Prefix = 16; NextChar (); /* gobble "x" */ } else { Base = 10; /* Assume 10 for now - see below */ Prefix = 8; /* Actual prefix says octal */ } } else { Base = Prefix = 10; } /* Because floating point numbers don't have octal prefixes (a number ** with a leading zero is decimal), we first have to read the number ** before converting it, so we can determine if it's a float or an ** integer. */ while (IsXDigit (CurC) && HexVal (CurC) < Base) { SB_AppendChar (&S, CurC); NextChar (); } SB_Terminate (&S); /* The following character tells us if we have an integer or floating ** point constant. Note: Hexadecimal floating point constants aren't ** supported in C89. */ IsFloat = (CurC == '.' || (Base == 10 && toupper (CurC) == 'E') || (Base == 16 && toupper (CurC) == 'P' && IS_Get (&Standard) >= STD_C99)); /* If we don't have a floating point type, an octal prefix results in an ** octal base. */ if (!IsFloat && Prefix == 8) { Base = 8; } /* Since we do now know the correct base, convert the remembered input ** into a number. */ SB_Reset (&S); IVal = 0; while ((C = SB_Get (&S)) != '\0') { DigitVal = HexVal (C); if (DigitVal >= Base) { Error ("Numeric constant contains digits beyond the radix"); } IVal = (IVal * Base) + DigitVal; } /* We don't need the string buffer any longer */ SB_Done (&S); /* Distinguish between integer and floating point constants */ if (!IsFloat) { unsigned Types; int HaveSuffix; /* Check for a suffix and determine the possible types */ HaveSuffix = 1; if (toupper (CurC) == 'U') { /* Unsigned type */ NextChar (); if (toupper (CurC) != 'L') { Types = IT_UINT | IT_ULONG; } else { NextChar (); Types = IT_ULONG; } } else if (toupper (CurC) == 'L') { /* Long type */ NextChar (); if (toupper (CurC) != 'U') { Types = IT_LONG | IT_ULONG; } else { NextChar (); Types = IT_ULONG; } } else { HaveSuffix = 0; if (Prefix == 10) { /* Decimal constants are of any type but uint */ Types = IT_INT | IT_LONG | IT_ULONG; } else { /* Octal or hex constants are of any type */ Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG; } } /* Check the range to determine the type */ if (IVal > 0x7FFF) { /* Out of range for int */ Types &= ~IT_INT; /* If the value is in the range 0x8000..0xFFFF, unsigned int is not ** allowed, and we don't have a type specifying suffix, emit a ** warning, because the constant is of type long. */ if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && !HaveSuffix) { Warning ("Constant is long"); } } if (IVal > 0xFFFF) { /* Out of range for unsigned int */ Types &= ~IT_UINT; } if (IVal > 0x7FFFFFFF) { /* Out of range for long int */ Types &= ~IT_LONG; } /* Now set the type string to the smallest type in types */ if (Types & IT_INT) { NextTok.Type = type_int; } else if (Types & IT_UINT) { NextTok.Type = type_uint; } else if (Types & IT_LONG) { NextTok.Type = type_long; } else { NextTok.Type = type_ulong; } /* Set the value and the token */ NextTok.IVal = IVal; NextTok.Tok = TOK_ICONST; } else { /* Float constant */ Double FVal = FP_D_FromInt (IVal); /* Convert to double */ /* Check for a fractional part and read it */ if (CurC == '.') { Double Scale; /* Skip the dot */ NextChar (); /* Read fractional digits */ Scale = FP_D_Make (1.0); while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) { /* Get the value of this digit */ Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale); /* Add it to the float value */ FVal = FP_D_Add (FVal, FracVal); /* Scale base */ Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal)); /* Skip the digit */ NextChar (); } } /* Check for an exponent and read it */ if ((Base == 16 && toupper (CurC) == 'F') || (Base == 10 && toupper (CurC) == 'E')) { unsigned Digits; unsigned Exp; /* Skip the exponent notifier */ NextChar (); /* Read an optional sign */ if (CurC == '-') { NextChar (); } else if (CurC == '+') { NextChar (); } /* Read exponent digits. Since we support only 32 bit floats ** with a maximum exponent of +-/127, we read the exponent ** part as integer with up to 3 digits and drop the remainder. ** This avoids an overflow of Exp. The exponent is always ** decimal, even for hex float consts. */ Digits = 0; Exp = 0; while (IsDigit (CurC)) { if (++Digits <= 3) { Exp = Exp * 10 + HexVal (CurC); } NextChar (); } /* Check for errors: We must have exponent digits, and not more ** than three. */ if (Digits == 0) { Error ("Floating constant exponent has no digits"); } else if (Digits > 3) { Warning ("Floating constant exponent is too large"); } /* Scale the exponent and adjust the value accordingly */ if (Exp) { FVal = FP_D_Mul (FVal, FP_D_Make (pow (10, Exp))); } } /* Check for a suffix and determine the type of the constant */ if (toupper (CurC) == 'F') { NextChar (); NextTok.Type = type_float; } else { NextTok.Type = type_double; } /* Set the value and the token */ NextTok.FVal = FVal; NextTok.Tok = TOK_FCONST; } }
static void FuncSPrintF (void) /* Handle the .SPRINTF function */ { StrBuf Format = STATIC_STRBUF_INITIALIZER; /* User supplied format */ StrBuf R = STATIC_STRBUF_INITIALIZER; /* Result string */ StrBuf F1 = STATIC_STRBUF_INITIALIZER; /* One format spec from F */ StrBuf R1 = STATIC_STRBUF_INITIALIZER; /* One result */ char C; int Done; long IVal; /* Integer value */ /* Skip the .SPRINTF token */ NextTok (); /* Left paren expected */ ConsumeLParen (); /* First argument is a format string. Remember and skip it */ if (!LookAtStrCon ()) { return; } SB_Copy (&Format, &CurTok.SVal); NextTok (); /* Walk over the format string, generating the function result in R */ while (1) { /* Get the next char from the format string and check for EOS */ if (SB_Peek (&Format) == '\0') { break; } /* Check for a format specifier */ if (SB_Peek (&Format) != '%') { /* No format specifier, just copy */ SB_AppendChar (&R, SB_Get (&Format)); continue; } SB_Skip (&Format); if (SB_Peek (&Format) == '%') { /* %% */ SB_AppendChar (&R, '%'); SB_Skip (&Format); continue; } if (SB_Peek (&Format) == '\0') { InvalidFormatString (); break; } /* Since a format specifier follows, we do expect anotehr argument for * the .sprintf function. */ ConsumeComma (); /* We will copy the format spec into F1 checking for the things we * support, and later use xsprintf to do the actual formatting. This * is easier than adding another printf implementation... */ SB_Clear (&F1); SB_AppendChar (&F1, '%'); /* Check for flags */ Done = 0; while ((C = SB_Peek (&Format)) != '\0' && !Done) { switch (C) { case '-': /* FALLTHROUGH */ case '+': /* FALLTHROUGH */ case ' ': /* FALLTHROUGH */ case '#': /* FALLTHROUGH */ case '0': SB_AppendChar (&F1, SB_Get (&Format)); break; default: Done = 1; break; } } /* We do only support a numerical width field */ while (IsDigit (SB_Peek (&Format))) { SB_AppendChar (&F1, SB_Get (&Format)); } /* Precision - only positive numerical fields supported */ if (SB_Peek (&Format) == '.') { SB_AppendChar (&F1, SB_Get (&Format)); while (IsDigit (SB_Peek (&Format))) { SB_AppendChar (&F1, SB_Get (&Format)); } } /* Length modifiers aren't supported, so read the conversion specs */ switch (SB_Peek (&Format)) { case 'd': case 'i': case 'o': case 'u': case 'X': case 'x': /* Our ints are actually longs, so we use the 'l' modifier when * calling xsprintf later. Terminate the format string. */ SB_AppendChar (&F1, 'l'); SB_AppendChar (&F1, SB_Get (&Format)); SB_Terminate (&F1); /* The argument must be a constant expression */ IVal = ConstExpression (); /* Format this argument according to the spec */ SB_Printf (&R1, SB_GetConstBuf (&F1), IVal); /* Append the formatted argument to the result */ SB_Append (&R, &R1); break; case 's': /* Add the format spec and terminate the format */ SB_AppendChar (&F1, SB_Get (&Format)); SB_Terminate (&F1); /* The argument must be a string constant */ if (!LookAtStrCon ()) { /* Make it one */ SB_CopyStr (&CurTok.SVal, "**undefined**"); } /* Format this argument according to the spec */ SB_Printf (&R1, SB_GetConstBuf (&F1), SB_GetConstBuf (&CurTok.SVal)); /* Skip the string constant */ NextTok (); /* Append the formatted argument to the result */ SB_Append (&R, &R1); break; case 'c': /* Add the format spec and terminate the format */ SB_AppendChar (&F1, SB_Get (&Format)); SB_Terminate (&F1); /* The argument must be a constant expression */ IVal = ConstExpression (); /* Check for a valid character range */ if (IVal <= 0 || IVal > 255) { Error ("Char argument out of range"); IVal = ' '; } /* Format this argument according to the spec. Be sure to pass * an int as the char value. */ SB_Printf (&R1, SB_GetConstBuf (&F1), (int) IVal); /* Append the formatted argument to the result */ SB_Append (&R, &R1); break; default: Error ("Invalid format string"); SB_Skip (&Format); break; } } /* Terminate the result string */ SB_Terminate (&R); /* We expect a closing parenthesis, but will not skip it but replace it * by the string token just created. */ if (CurTok.Tok != TOK_RPAREN) { Error ("`)' expected"); } else { CurTok.Tok = TOK_STRCON; SB_Copy (&CurTok.SVal, &R); SB_Terminate (&CurTok.SVal); } /* Delete the string buffers */ SB_Done (&Format); SB_Done (&R); SB_Done (&F1); SB_Done (&R1); }
int NextLine (void) /* Get a line from the current input. Returns 0 on end of file. */ { AFile* Input; /* Clear the current line */ ClearLine (); /* If there is no file open, bail out, otherwise get the current input file */ if (CollCount (&AFiles) == 0) { return 0; } Input = CollLast (&AFiles); /* Read characters until we have one complete line */ while (1) { /* Read the next character */ int C = fgetc (Input->F); /* Check for EOF */ if (C == EOF) { /* Accept files without a newline at the end */ if (SB_NotEmpty (Line)) { ++Input->Line; break; } /* Leave the current file */ CloseIncludeFile (); /* If there is no file open, bail out, otherwise get the * previous input file and start over. */ if (CollCount (&AFiles) == 0) { return 0; } Input = CollLast (&AFiles); continue; } /* Check for end of line */ if (C == '\n') { /* We got a new line */ ++Input->Line; /* If the \n is preceeded by a \r, remove the \r, so we can read * DOS/Windows files under *nix. */ if (SB_LookAtLast (Line) == '\r') { SB_Drop (Line, 1); } /* If we don't have a line continuation character at the end, * we're done with this line. Otherwise replace the character * by a newline and continue reading. */ if (SB_LookAtLast (Line) == '\\') { Line->Buf[Line->Len-1] = '\n'; } else { break; } } else if (C != '\0') { /* Ignore embedded NULs */ /* Just some character, add it to the line */ SB_AppendChar (Line, C); } } /* Add a termination character to the string buffer */ SB_Terminate (Line); /* Initialize the current and next characters. */ InitLine (Line); /* Create line information for this line */ UpdateLineInfo (Input->Input, Input->Line, Line); /* Done */ return 1; }