1 /*****************************************************************************/
5 /* Configuration file scanner for the ld65 linker */
9 /* (C) 1998-2013, 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 source for the configuration */
70 static const char* CfgName = 0;
72 /* Other input stuff */
74 static FilePos InputPos;
75 static FILE* InputFile = 0;
79 /*****************************************************************************/
81 /*****************************************************************************/
85 void CfgWarning (const FilePos* Pos, const char* Format, ...)
86 /* Print a warning message adding file name and line number of a given file */
88 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
91 va_start (ap, Format);
92 SB_VPrintf (&Buf, Format, ap);
95 Warning ("%s(%u): %s",
96 GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf));
102 void CfgError (const FilePos* Pos, const char* Format, ...)
103 /* Print an error message adding file name and line number of a given file */
105 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
108 va_start (ap, Format);
109 SB_VPrintf (&Buf, Format, ap);
113 GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf));
119 /*****************************************************************************/
121 /*****************************************************************************/
125 static void NextChar (void)
126 /* Read the next character from the input file */
128 /* Read from the file */
129 C = getc (InputFile);
145 static unsigned DigitVal (int C)
146 /* Return the value for a numeric digit */
151 return toupper (C) - 'A' + 10;
157 static void StrVal (void)
158 /* Parse a string value and expand escape sequences */
160 /* Skip the starting double quotes */
163 /* Read input chars */
170 CfgError (&CfgErrorPos, "Unterminated string");
180 CfgError (&CfgErrorPos, "Unterminated '%%' escape sequence");
184 SB_AppendChar (&CfgSVal, '%');
189 /* Replace by output file */
191 SB_AppendStr (&CfgSVal, OutputName);
198 CfgWarning (&CfgErrorPos,
199 "Unkown escape sequence '%%%c'", C);
200 SB_AppendChar (&CfgSVal, '%');
201 SB_AppendChar (&CfgSVal, C);
208 SB_AppendChar (&CfgSVal, C);
213 /* Skip the terminating double quotes */
216 /* Terminate the string */
217 SB_Terminate (&CfgSVal);
219 /* We've read a string value */
220 CfgTok = CFGTOK_STRCON;
225 void CfgNextTok (void)
226 /* Read the next token from the input stream */
229 /* Skip whitespace */
230 while (isspace (C)) {
234 /* Remember the current position */
235 CfgErrorPos = InputPos;
238 if (C == '_' || IsAlpha (C)) {
240 /* Read the identifier */
242 while (C == '_' || IsAlNum (C)) {
243 SB_AppendChar (&CfgSVal, C);
246 SB_Terminate (&CfgSVal);
247 CfgTok = CFGTOK_IDENT;
255 CfgError (&CfgErrorPos, "Hex digit expected");
258 while (isxdigit (C)) {
259 CfgIVal = CfgIVal * 16 + DigitVal (C);
262 CfgTok = CFGTOK_INTCON;
266 /* Decimal number? */
269 while (isdigit (C)) {
270 CfgIVal = CfgIVal * 10 + DigitVal (C);
273 CfgTok = CFGTOK_INTCON;
277 /* Other characters */
282 CfgTok = CFGTOK_MINUS;
287 CfgTok = CFGTOK_PLUS;
302 CfgTok = CFGTOK_LPAR;
307 CfgTok = CFGTOK_RPAR;
312 CfgTok = CFGTOK_LCURLY;
317 CfgTok = CFGTOK_RCURLY;
322 CfgTok = CFGTOK_SEMI;
332 CfgTok = CFGTOK_COMMA;
342 CfgTok = CFGTOK_COLON;
351 while (C != '\n' && C != EOF) {
367 SB_CopyStr (&CfgSVal, OutputName);
371 SB_Terminate (&CfgSVal);
373 CfgTok = CFGTOK_STRCON;
379 CfgTok = CFGTOK_INTCON;
383 CfgError (&CfgErrorPos, "Invalid format specification");
392 CfgError (&CfgErrorPos, "Invalid character '%c'", C);
399 void CfgConsume (cfgtok_t T, const char* Msg)
400 /* Skip a token, print an error message if not found */
403 CfgError (&CfgErrorPos, "%s", Msg);
410 void CfgConsumeSemi (void)
411 /* Consume a semicolon */
413 CfgConsume (CFGTOK_SEMI, "';' expected");
418 void CfgConsumeColon (void)
419 /* Consume a colon */
421 CfgConsume (CFGTOK_COLON, "':' expected");
426 void CfgOptionalComma (void)
427 /* Consume a comma if there is one */
429 if (CfgTok == CFGTOK_COMMA) {
436 void CfgOptionalAssign (void)
437 /* Consume an equal sign if there is one */
439 if (CfgTok == CFGTOK_EQ) {
446 void CfgAssureInt (void)
447 /* Make sure the next token is an integer */
449 if (CfgTok != CFGTOK_INTCON) {
450 CfgError (&CfgErrorPos, "Integer constant expected");
456 void CfgAssureStr (void)
457 /* Make sure the next token is a string constant */
459 if (CfgTok != CFGTOK_STRCON) {
460 CfgError (&CfgErrorPos, "String constant expected");
466 void CfgAssureIdent (void)
467 /* Make sure the next token is an identifier */
469 if (CfgTok != CFGTOK_IDENT) {
470 CfgError (&CfgErrorPos, "Identifier expected");
476 void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
477 /* Check the range of CfgIVal */
479 if (CfgIVal < Lo || CfgIVal > Hi) {
480 CfgError (&CfgErrorPos, "Range error");
486 void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
487 /* Map an identifier to one of the special tokens in the table */
491 /* We need an identifier */
492 if (CfgTok == CFGTOK_IDENT) {
494 /* Make it upper case */
495 SB_ToUpper (&CfgSVal);
498 for (I = 0; I < Size; ++I) {
499 if (SB_CompareStr (&CfgSVal, Table[I].Ident) == 0) {
500 CfgTok = Table[I].Tok;
507 /* Not found or no identifier */
508 CfgError (&CfgErrorPos, "%s expected, got '%s'", Name, SB_GetConstBuf(&CfgSVal));
513 void CfgBoolToken (void)
514 /* Map an identifier or integer to a boolean token */
516 static const IdentTok Booleans [] = {
517 { "YES", CFGTOK_TRUE },
518 { "NO", CFGTOK_FALSE },
519 { "TRUE", CFGTOK_TRUE },
520 { "FALSE", CFGTOK_FALSE },
523 /* If we have an identifier, map it to a boolean token */
524 if (CfgTok == CFGTOK_IDENT) {
525 CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
527 /* We expected an integer here */
528 if (CfgTok != CFGTOK_INTCON) {
529 CfgError (&CfgErrorPos, "Boolean value expected");
531 CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
537 void CfgSetName (const char* Name)
538 /* Set a name for a config file */
546 /* Return true if we have a configuration available */
553 void CfgOpenInput (void)
554 /* Open the input file if we have one */
557 InputFile = fopen (CfgName, "r");
558 if (InputFile == 0) {
559 Error ("Cannot open '%s': %s", CfgName, strerror (errno));
562 /* Initialize variables */
566 InputPos.Name = GetStringId (CfgName);
568 /* Start the ball rolling ... */
574 void CfgCloseInput (void)
575 /* Close the input file if we have one */
577 /* Close the input file if we had one */
579 (void) fclose (InputFile);