]> git.sur5r.net Git - cc65/blob - src/ld65/scanner.c
Added dbg file generation
[cc65] / src / ld65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*              Configuration file scanner for the ld65 linker               */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2000 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <ctype.h>
41
42 /* common */
43 #include "xsprintf.h"
44
45 /* ld65 */
46 #include "global.h"
47 #include "error.h"
48 #include "scanner.h"
49
50
51
52 /*****************************************************************************/
53 /*                                   Data                                    */
54 /*****************************************************************************/
55
56
57
58 /* Current token and attributes */
59 cfgtok_t        CfgTok;
60 char            CfgSVal [CFG_MAX_IDENT_LEN+1];
61 unsigned long   CfgIVal;
62
63 /* Error location */
64 unsigned                CfgErrorLine;
65 unsigned                CfgErrorCol;
66
67 /* Input sources for the configuration */
68 static const char*      CfgName         = 0;
69 static const char*      CfgBuf          = 0;
70
71 /* Other input stuff */
72 static int              C               = ' ';
73 static unsigned         InputLine       = 1;
74 static unsigned         InputCol        = 0;
75 static FILE*            InputFile       = 0;
76
77
78
79 /*****************************************************************************/
80 /*                              Error handling                               */
81 /*****************************************************************************/
82
83
84
85 void CfgWarning (const char* Format, ...)
86 /* Print a warning message adding file name and line number of the config file */
87 {
88     char Buf [512];
89     va_list ap;
90
91     va_start (ap, Format);
92     xvsprintf (Buf, sizeof (Buf), Format, ap);
93     va_end (ap);
94
95     Warning ("%s(%u): %s", CfgName, CfgErrorLine, Buf);
96 }
97
98
99
100 void CfgError (const char* Format, ...)
101 /* Print an error message adding file name and line number of the config file */
102 {
103     char Buf [512];
104     va_list ap;
105
106     va_start (ap, Format);
107     xvsprintf (Buf, sizeof (Buf), Format, ap);
108     va_end (ap);
109
110     Error ("%s(%u): %s", CfgName, CfgErrorLine, Buf);
111 }
112
113
114
115 /*****************************************************************************/
116 /*                                   Code                                    */
117 /*****************************************************************************/
118
119
120
121 static void NextChar (void)
122 /* Read the next character from the input file */
123 {
124     if (CfgBuf) {
125         /* Read from buffer */
126         C = (unsigned char)(*CfgBuf);
127         if (C == 0) {
128             C = EOF;
129         } else {
130             ++CfgBuf;
131         }
132     } else {
133         /* Read from the file */
134         C = getc (InputFile);
135     }
136
137     /* Count columns */
138     if (C != EOF) {
139         ++InputCol;
140     }
141
142     /* Count lines */
143     if (C == '\n') {
144         ++InputLine;
145         InputCol = 0;
146     }
147 }
148
149
150
151 static unsigned DigitVal (int C)
152 /* Return the value for a numeric digit */
153 {
154     if (isdigit (C)) {
155         return C - '0';
156     } else {
157         return toupper (C) - 'A' + 10;
158     }
159 }
160
161
162
163 void CfgNextTok (void)
164 /* Read the next token from the input stream */
165 {
166     unsigned I;
167
168
169 Again:
170     /* Skip whitespace */
171     while (isspace (C)) {
172         NextChar ();
173     }
174
175     /* Remember the current position */
176     CfgErrorLine = InputLine;
177     CfgErrorCol  = InputCol;
178
179     /* Identifier? */
180     if (C == '_' || isalpha (C)) {
181
182         /* Read the identifier */
183         I = 0;
184         while (C == '_' || isalnum (C)) {
185             if (I < CFG_MAX_IDENT_LEN) {
186                 CfgSVal [I++] = C;
187             }
188             NextChar ();
189         }
190         CfgSVal [I] = '\0';
191         CfgTok = CFGTOK_IDENT;
192         return;
193     }
194
195     /* Hex number? */
196     if (C == '$') {
197         NextChar ();
198         if (!isxdigit (C)) {
199             Error ("%s(%u): Hex digit expected", CfgName, InputLine);
200         }
201         CfgIVal = 0;
202         while (isxdigit (C)) {
203             CfgIVal = CfgIVal * 16 + DigitVal (C);
204             NextChar ();
205         }
206         CfgTok = CFGTOK_INTCON;
207         return;
208     }
209
210     /* Decimal number? */
211     if (isdigit (C)) {
212         CfgIVal = 0;
213         while (isdigit (C)) {
214             CfgIVal = CfgIVal * 10 + DigitVal (C);
215             NextChar ();
216         }
217         CfgTok = CFGTOK_INTCON;
218         return;
219     }
220
221     /* Other characters */
222     switch (C) {
223
224         case '{':
225             NextChar ();
226             CfgTok = CFGTOK_LCURLY;
227             break;
228
229         case '}':
230             NextChar ();
231             CfgTok = CFGTOK_RCURLY;
232             break;
233
234         case ';':
235             NextChar ();
236             CfgTok = CFGTOK_SEMI;
237             break;
238
239         case '.':
240             NextChar ();
241             CfgTok = CFGTOK_DOT;
242             break;
243
244         case ',':
245             NextChar ();
246             CfgTok = CFGTOK_COMMA;
247             break;
248
249         case '=':
250             NextChar ();
251             CfgTok = CFGTOK_EQ;
252             break;
253
254         case ':':
255             NextChar ();
256             CfgTok = CFGTOK_COLON;
257             break;
258
259         case '\"':
260             NextChar ();
261             I = 0;
262             while (C != '\"') {
263                 if (C == EOF || C == '\n') {
264                     Error ("%s(%u): Unterminated string", CfgName, InputLine);
265                 }
266                 if (I < CFG_MAX_IDENT_LEN) {
267                     CfgSVal [I++] = C;
268                 }
269                 NextChar ();
270             }
271             NextChar ();
272             CfgSVal [I] = '\0';
273             CfgTok = CFGTOK_STRCON;
274             break;
275
276         case '#':
277             /* Comment */
278             while (C != '\n' && C != EOF) {
279                 NextChar ();
280             }
281             if (C != EOF) {
282                 goto Again;
283             }
284             CfgTok = CFGTOK_EOF;
285             break;
286
287         case '%':
288             NextChar ();
289             switch (C) {
290
291                 case 'O':
292                     NextChar ();
293                     if (OutputName) {
294                         strncpy (CfgSVal, OutputName, CFG_MAX_IDENT_LEN);
295                         CfgSVal [CFG_MAX_IDENT_LEN] = '\0';
296                     } else {
297                         CfgSVal [0] = '\0';
298                     }
299                     CfgTok = CFGTOK_STRCON;
300                     break;
301
302                 case 'S':
303                     NextChar ();
304                     CfgIVal = StartAddr;
305                     CfgTok = CFGTOK_INTCON;
306                     break;
307
308                 default:
309                     CfgError ("Invalid format specification");
310             }
311             break;
312
313         case EOF:
314             CfgTok = CFGTOK_EOF;
315             break;
316
317         default:
318             Error ("%s(%u): Invalid character `%c'", CfgName, InputLine, C);
319
320     }
321 }
322
323
324
325 void CfgConsume (cfgtok_t T, const char* Msg)
326 /* Skip a token, print an error message if not found */
327 {
328     if (CfgTok != T) {
329         CfgError (Msg);
330     }
331     CfgNextTok ();
332 }
333
334
335
336 void CfgConsumeSemi (void)
337 /* Consume a semicolon */
338 {
339     CfgConsume (CFGTOK_SEMI, "`;' expected");
340 }
341
342
343
344 void CfgConsumeColon (void)
345 /* Consume a colon */
346 {
347     CfgConsume (CFGTOK_COLON, "`:' expected");
348 }
349
350
351
352 void CfgOptionalComma (void)
353 /* Consume a comma if there is one */
354 {
355     if (CfgTok == CFGTOK_COMMA) {
356         CfgNextTok ();
357     }
358 }
359
360
361
362 void CfgOptionalAssign (void)
363 /* Consume an equal sign if there is one */
364 {
365     if (CfgTok == CFGTOK_EQ) {
366         CfgNextTok ();
367     }
368 }
369
370
371
372 void CfgAssureInt (void)
373 /* Make sure the next token is an integer */
374 {
375     if (CfgTok != CFGTOK_INTCON) {
376         CfgError ("Integer constant expected");
377     }
378 }
379
380
381
382 void CfgAssureStr (void)
383 /* Make sure the next token is a string constant */
384 {
385     if (CfgTok != CFGTOK_STRCON) {
386         CfgError ("String constant expected");
387     }
388 }
389
390
391
392 void CfgAssureIdent (void)
393 /* Make sure the next token is an identifier */
394 {
395     if (CfgTok != CFGTOK_IDENT) {
396         CfgError ("Identifier expected");
397     }
398 }
399
400
401
402 void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
403 /* Check the range of CfgIVal */
404 {
405     if (CfgIVal < Lo || CfgIVal > Hi) {
406         CfgError ("Range error");
407     }
408 }
409
410
411
412 void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
413 /* Map an identifier to one of the special tokens in the table */
414 {
415     unsigned I;
416
417     /* We need an identifier */
418     if (CfgTok == CFGTOK_IDENT) {
419
420         /* Make it upper case */
421         I = 0;
422         while (CfgSVal [I]) {
423             CfgSVal [I] = toupper (CfgSVal [I]);
424             ++I;
425         }
426
427         /* Linear search */
428         for (I = 0; I < Size; ++I) {
429             if (strcmp (CfgSVal, Table [I].Ident) == 0) {
430                 CfgTok = Table [I].Tok;
431                 return;
432             }
433         }
434
435     }
436
437     /* Not found or no identifier */
438     Error ("%s(%u): %s expected", CfgName, InputLine, Name);
439 }
440
441
442
443 void CfgBoolToken (void)
444 /* Map an identifier or integer to a boolean token */
445 {
446     static const IdentTok Booleans [] = {
447         {   "YES",      CFGTOK_TRUE     },
448         {   "NO",       CFGTOK_FALSE    },
449         {   "TRUE",     CFGTOK_TRUE     },
450         {   "FALSE",    CFGTOK_FALSE    },
451     };
452
453     /* If we have an identifier, map it to a boolean token */
454     if (CfgTok == CFGTOK_IDENT) {
455         CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
456     } else {
457         /* We expected an integer here */
458         if (CfgTok != CFGTOK_INTCON) {
459             CfgError ("Boolean value expected");
460         }
461         CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
462     }
463 }
464
465
466
467 void CfgSetName (const char* Name)
468 /* Set a name for a config file */
469 {
470     CfgName = Name;
471 }
472
473
474
475 const char* CfgGetName (void)
476 /* Get the name of the config file */
477 {
478     return CfgName? CfgName : "";
479 }
480
481
482
483 void CfgSetBuf (const char* Buf)
484 /* Set a memory buffer for the config */
485 {
486     CfgBuf = Buf;
487 }
488
489
490
491 int CfgAvail (void)
492 /* Return true if we have a configuration available */
493 {
494     return CfgName != 0 || CfgBuf != 0;
495 }
496
497
498
499 void CfgOpenInput (void)
500 /* Open the input file if we have one */
501 {
502     /* If we have a config name given, open the file, otherwise we will read
503      * from a buffer.
504      */
505     if (!CfgBuf) {
506
507         /* Open the file */
508         InputFile = fopen (CfgName, "r");
509         if (InputFile == 0) {
510             Error ("Cannot open `%s': %s", CfgName, strerror (errno));
511         }
512
513     }
514
515     /* Initialize variables */
516     C         = ' ';
517     InputLine = 1;
518     InputCol  = 0;
519
520     /* Start the ball rolling ... */
521     CfgNextTok ();
522 }
523
524
525
526 void CfgCloseInput (void)
527 /* Close the input file if we have one */
528 {
529     /* Close the input file if we had one */
530     if (InputFile) {
531         (void) fclose (InputFile);
532         InputFile = 0;
533     }
534 }
535
536
537
538