1 /*****************************************************************************/
5 /* Configuration file scanner for the ld65 linker */
9 /* (C) 1998-2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
55 /*****************************************************************************/
57 /*****************************************************************************/
61 /* Current token and attributes */
63 StrBuf CfgSVal = STATIC_STRBUF_INITIALIZER;
64 unsigned long CfgIVal;
69 /* Input sources for the configuration */
70 static const char* CfgName = 0;
71 static const char* CfgBuf = 0;
73 /* Other input stuff */
75 static FilePos InputPos;
76 static FILE* InputFile = 0;
80 /*****************************************************************************/
82 /*****************************************************************************/
86 void CfgWarning (const FilePos* Pos, const char* Format, ...)
87 /* Print a warning message adding file name and line number of a given file */
89 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
92 va_start (ap, Format);
93 SB_VPrintf (&Buf, Format, ap);
96 Warning ("%s(%lu): %s",
97 GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf));
103 void CfgError (const FilePos* Pos, const char* Format, ...)
104 /* Print an error message adding file name and line number of a given file */
106 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
109 va_start (ap, Format);
110 SB_VPrintf (&Buf, Format, ap);
113 Error ("%s(%lu): %s",
114 GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf));
120 /*****************************************************************************/
122 /*****************************************************************************/
126 static void NextChar (void)
127 /* Read the next character from the input file */
130 /* Read from buffer */
131 C = (unsigned char)(*CfgBuf);
138 /* Read from the file */
139 C = getc (InputFile);
156 static unsigned DigitVal (int C)
157 /* Return the value for a numeric digit */
162 return toupper (C) - 'A' + 10;
168 static void StrVal (void)
169 /* Parse a string value and expand escape sequences */
171 /* Skip the starting double quotes */
174 /* Read input chars */
181 CfgError (&CfgErrorPos, "Unterminated string");
191 CfgError (&CfgErrorPos, "Unterminated '%%' escape sequence");
195 SB_AppendChar (&CfgSVal, '%');
200 /* Replace by output file */
202 SB_AppendStr (&CfgSVal, OutputName);
209 CfgWarning (&CfgErrorPos,
210 "Unkown escape sequence `%%%c'", C);
211 SB_AppendChar (&CfgSVal, '%');
212 SB_AppendChar (&CfgSVal, C);
219 SB_AppendChar (&CfgSVal, C);
224 /* Skip the terminating double quotes */
227 /* Terminate the string */
228 SB_Terminate (&CfgSVal);
230 /* We've read a string value */
231 CfgTok = CFGTOK_STRCON;
236 void CfgNextTok (void)
237 /* Read the next token from the input stream */
240 /* Skip whitespace */
241 while (isspace (C)) {
245 /* Remember the current position */
246 CfgErrorPos = InputPos;
249 if (C == '_' || IsAlpha (C)) {
251 /* Read the identifier */
253 while (C == '_' || IsAlNum (C)) {
254 SB_AppendChar (&CfgSVal, C);
257 SB_Terminate (&CfgSVal);
258 CfgTok = CFGTOK_IDENT;
266 CfgError (&CfgErrorPos, "Hex digit expected");
269 while (isxdigit (C)) {
270 CfgIVal = CfgIVal * 16 + DigitVal (C);
273 CfgTok = CFGTOK_INTCON;
277 /* Decimal number? */
280 while (isdigit (C)) {
281 CfgIVal = CfgIVal * 10 + DigitVal (C);
284 CfgTok = CFGTOK_INTCON;
288 /* Other characters */
293 CfgTok = CFGTOK_MINUS;
298 CfgTok = CFGTOK_PLUS;
313 CfgTok = CFGTOK_LPAR;
318 CfgTok = CFGTOK_RPAR;
323 CfgTok = CFGTOK_LCURLY;
328 CfgTok = CFGTOK_RCURLY;
333 CfgTok = CFGTOK_SEMI;
343 CfgTok = CFGTOK_COMMA;
353 CfgTok = CFGTOK_COLON;
362 while (C != '\n' && C != EOF) {
378 SB_CopyStr (&CfgSVal, OutputName);
382 SB_Terminate (&CfgSVal);
384 CfgTok = CFGTOK_STRCON;
390 CfgTok = CFGTOK_INTCON;
394 CfgError (&CfgErrorPos, "Invalid format specification");
403 CfgError (&CfgErrorPos, "Invalid character `%c'", C);
410 void CfgConsume (cfgtok_t T, const char* Msg)
411 /* Skip a token, print an error message if not found */
414 CfgError (&CfgErrorPos, "%s", Msg);
421 void CfgConsumeSemi (void)
422 /* Consume a semicolon */
424 CfgConsume (CFGTOK_SEMI, "`;' expected");
429 void CfgConsumeColon (void)
430 /* Consume a colon */
432 CfgConsume (CFGTOK_COLON, "`:' expected");
437 void CfgOptionalComma (void)
438 /* Consume a comma if there is one */
440 if (CfgTok == CFGTOK_COMMA) {
447 void CfgOptionalAssign (void)
448 /* Consume an equal sign if there is one */
450 if (CfgTok == CFGTOK_EQ) {
457 void CfgAssureInt (void)
458 /* Make sure the next token is an integer */
460 if (CfgTok != CFGTOK_INTCON) {
461 CfgError (&CfgErrorPos, "Integer constant expected");
467 void CfgAssureStr (void)
468 /* Make sure the next token is a string constant */
470 if (CfgTok != CFGTOK_STRCON) {
471 CfgError (&CfgErrorPos, "String constant expected");
477 void CfgAssureIdent (void)
478 /* Make sure the next token is an identifier */
480 if (CfgTok != CFGTOK_IDENT) {
481 CfgError (&CfgErrorPos, "Identifier expected");
487 void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
488 /* Check the range of CfgIVal */
490 if (CfgIVal < Lo || CfgIVal > Hi) {
491 CfgError (&CfgErrorPos, "Range error");
497 void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
498 /* Map an identifier to one of the special tokens in the table */
502 /* We need an identifier */
503 if (CfgTok == CFGTOK_IDENT) {
505 /* Make it upper case */
506 SB_ToUpper (&CfgSVal);
509 for (I = 0; I < Size; ++I) {
510 if (SB_CompareStr (&CfgSVal, Table[I].Ident) == 0) {
511 CfgTok = Table[I].Tok;
518 /* Not found or no identifier */
519 CfgError (&CfgErrorPos, "%s expected", Name);
524 void CfgBoolToken (void)
525 /* Map an identifier or integer to a boolean token */
527 static const IdentTok Booleans [] = {
528 { "YES", CFGTOK_TRUE },
529 { "NO", CFGTOK_FALSE },
530 { "TRUE", CFGTOK_TRUE },
531 { "FALSE", CFGTOK_FALSE },
534 /* If we have an identifier, map it to a boolean token */
535 if (CfgTok == CFGTOK_IDENT) {
536 CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
538 /* We expected an integer here */
539 if (CfgTok != CFGTOK_INTCON) {
540 CfgError (&CfgErrorPos, "Boolean value expected");
542 CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
548 void CfgSetName (const char* Name)
549 /* Set a name for a config file */
556 const char* CfgGetName (void)
557 /* Get the name of the config file */
562 return "[builtin config]";
570 void CfgSetBuf (const char* Buf)
571 /* Set a memory buffer for the config */
579 /* Return true if we have a configuration available */
581 return CfgName != 0 || CfgBuf != 0;
586 void CfgOpenInput (void)
587 /* Open the input file if we have one */
589 /* If we have a config name given, open the file, otherwise we will read
595 InputFile = fopen (CfgName, "r");
596 if (InputFile == 0) {
597 Error ("Cannot open `%s': %s", CfgName, strerror (errno));
602 /* Initialize variables */
606 InputPos.Name = GetStringId (CfgBuf? "[builtin config]" : CfgName);
608 /* Start the ball rolling ... */
614 void CfgCloseInput (void)
615 /* Close the input file if we have one */
617 /* Close the input file if we had one */
619 (void) fclose (InputFile);