]> git.sur5r.net Git - cc65/blob - src/da65/scanner.c
Use non-POSIX values for S_IREAD and S_IWRITE.
[cc65] / src / da65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*           Configuration file scanner for the da65 disassembler            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2005 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
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
41 /* common */
42 #include "chartype.h"
43 #include "xsprintf.h"
44 #include "xmalloc.h"
45 #include "strbuf.h"
46
47 /* ld65 */
48 #include "global.h"
49 #include "error.h"
50 #include "scanner.h"
51
52
53
54 /*****************************************************************************/
55 /*                                   Data                                    */
56 /*****************************************************************************/
57
58
59
60 /* Current token and attributes */
61 unsigned        InfoTok;
62 char            InfoSVal [CFG_MAX_IDENT_LEN+1];
63 long            InfoIVal;
64
65 /* Error location */
66 unsigned                InfoErrorLine;
67 unsigned                InfoErrorCol;
68
69 /* Input sources for the configuration */
70 static const char*      InfoFile        = 0;
71
72 /* Other input stuff */
73 static int              C               = ' ';
74 static unsigned         InputLine       = 1;
75 static unsigned         InputCol        = 0;
76 static FILE*            InputFile       = 0;
77 static char*            InputSrcName    = 0;
78
79
80
81 /*****************************************************************************/
82 /*                              Error handling                               */
83 /*****************************************************************************/
84
85
86
87 void InfoWarning (const char* Format, ...)
88 /* Print a warning message adding file name and line number of the config file */
89 {
90     char Buf [512];
91     va_list ap;
92
93     va_start (ap, Format);
94     xvsprintf (Buf, sizeof (Buf), Format, ap);
95     va_end (ap);
96
97     fprintf (stderr, "%s(%u): Warning: %s\n",
98             InputSrcName, InfoErrorLine, Buf);
99 }
100
101
102
103 void InfoError (const char* Format, ...)
104 /* Print an error message adding file name and line number of the config file */
105 {
106     char Buf [512];
107     va_list ap;
108
109     va_start (ap, Format);
110     xvsprintf (Buf, sizeof (Buf), Format, ap);
111     va_end (ap);
112
113     fprintf (stderr, "%s(%u): Error: %s\n",
114             InputSrcName, InfoErrorLine, Buf);
115     exit (EXIT_FAILURE);
116 }
117
118
119
120
121 /*****************************************************************************/
122 /*                                   Code                                    */
123 /*****************************************************************************/
124
125
126
127 static void NextChar (void)
128 /* Read the next character from the input file */
129 {
130     /* Read from the file */
131     C = getc (InputFile);
132
133     /* Count columns */
134     if (C != EOF) {
135         ++InputCol;
136     }
137
138     /* Count lines */
139     if (C == '\n') {
140         ++InputLine;
141         InputCol = 0;
142     }
143 }
144
145
146
147 static unsigned DigitVal (int C)
148 /* Return the value for a numeric digit */
149 {
150     if (IsDigit (C)) {
151         return C - '0';
152     } else {
153         return toupper (C) - 'A' + 10;
154     }
155 }
156
157
158
159 static void SkipBlanks (int SingleLine)
160 {
161     while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
162         NextChar ();
163     }
164 }
165
166
167
168 static long GetDecimalToken (void)
169 {
170     long Value = 0;
171
172     while (C != EOF && IsDigit (C)) {
173         Value = Value * 10 + DigitVal (C);
174         NextChar ();
175     }
176     return Value;
177 }
178
179
180
181 static int GetEncodedChar (char* Buf, unsigned* IPtr, unsigned Size)
182 {
183     char Decoded = 0;
184     int Count;
185
186     if (C == EOF) {
187         return -1;
188     } else if (C != '\\') {
189         Decoded = C;
190         NextChar ();
191         goto Store;
192     }
193     NextChar (); /* consume '\\' */
194     if (C == EOF) {
195         return -1;
196     } else if (IsODigit (C)) {
197         Count = 3;
198         do {
199             Decoded = Decoded * 8 + DigitVal (C);
200             NextChar ();
201             --Count;
202         } while (Count > 0 && C != EOF && IsODigit (C));
203     } else if (C == 'x') {
204         NextChar (); /* consume 'x' */
205         Count = 2;
206         while (Count > 0 && C != EOF && IsXDigit (C)) {
207             Decoded = Decoded * 16 + DigitVal (C);
208             NextChar ();
209             --Count;
210         }
211     } else {
212         switch (C) {
213             case '"': case '\'': case '\\':
214                         Decoded = C;        break;
215             case 't':   Decoded = '\t';     break;
216             case 'r':   Decoded = '\r';     break;
217             case 'n':   Decoded = '\n';     break;
218             default:    return -1;
219         }
220         NextChar ();
221     }
222 Store:
223     if (*IPtr < Size - 1) {
224         Buf [(*IPtr)++] = Decoded;
225     }
226     Buf [*IPtr] = 0;
227     return 0;
228 }
229
230
231
232 static void LineMarkerOrComment ()
233 /* Handle a line beginning with '#'. Possible interpretations are:
234 ** - #line <lineno> ["<filename>"]          (C preprocessor input)
235 ** - # <lineno> "<filename>" [<flag>]...    (gcc preprocessor output)
236 ** - #<comment>
237 */
238 {
239     unsigned long LineNo = 0;
240     int LineDirective = 0;
241     StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
242
243     /* Skip the first "# " */
244     NextChar ();
245     SkipBlanks (1);
246
247     /* Check "line" */
248     if (C == 'l') {
249         char MaybeLine [6];
250         unsigned I;
251         for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
252             MaybeLine [I] = C;
253             NextChar ();
254         }
255         MaybeLine [I] = 0;
256         if (strcmp (MaybeLine, "line") != 0) {
257             goto NotMarker;
258         }
259         LineDirective = 1;
260         SkipBlanks (1);
261     }
262
263     /* Get line number */
264     if (C == EOF || !IsDigit (C)) {
265         goto NotMarker;
266     }
267     LineNo = GetDecimalToken ();
268     SkipBlanks (1);
269
270     /* Get the source file name */
271     if (C != '\"') {
272         /* The source file name is missing */
273         if (LineDirective && C == '\n') {
274             /* got #line <lineno> */
275             NextChar ();
276             InputLine = LineNo;
277             goto Last;
278         } else {
279             goto NotMarker;
280         }
281     }
282     NextChar ();
283     while (C != EOF && C != '\n' && C != '\"') {
284         char DecodeBuf [2];
285         unsigned I = 0;
286         if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
287             goto BadMarker;
288         }
289         SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
290     }
291     if (C != '\"') {
292         goto BadMarker;
293     }
294     NextChar ();
295
296     /* Ignore until the end of line */
297     while (C != EOF && C != '\n') {
298         NextChar ();
299     }
300
301     /* Accepted a line marker */
302     SB_Terminate (&SrcNameBuf);
303     xfree (InputSrcName);
304     InputSrcName = SB_GetBuf (&SrcNameBuf);
305     SB_Init (&SrcNameBuf);
306     InputLine = (unsigned)LineNo;
307     NextChar ();
308     goto Last;
309
310 BadMarker:
311     InfoWarning ("Bad line marker");
312 NotMarker:
313     while (C != EOF && C != '\n') {
314         NextChar ();
315     }
316     NextChar ();
317 Last:
318     SB_Done (&SrcNameBuf);
319 }
320
321
322
323 void InfoNextTok (void)
324 /* Read the next token from the input stream */
325 {
326     unsigned I;
327     char DecodeBuf [2];
328
329 Again:
330     /* Skip whitespace */
331     SkipBlanks (0);
332
333     /* Remember the current position */
334     InfoErrorLine = InputLine;
335     InfoErrorCol  = InputCol;
336
337     /* Identifier? */
338     if (C == '_' || IsAlpha (C)) {
339
340         /* Read the identifier */
341         I = 0;
342         while (C == '_' || IsAlNum (C)) {
343             if (I < CFG_MAX_IDENT_LEN) {
344                 InfoSVal [I++] = C;
345             }
346             NextChar ();
347         }
348         InfoSVal [I] = '\0';
349         InfoTok = INFOTOK_IDENT;
350         return;
351     }
352
353     /* Hex number? */
354     if (C == '$') {
355         NextChar ();
356         if (!IsXDigit (C)) {
357             InfoError ("Hex digit expected");
358         }
359         InfoIVal = 0;
360         while (IsXDigit (C)) {
361             InfoIVal = InfoIVal * 16 + DigitVal (C);
362             NextChar ();
363         }
364         InfoTok = INFOTOK_INTCON;
365         return;
366     }
367
368     /* Decimal number? */
369     if (IsDigit (C)) {
370         InfoIVal = GetDecimalToken ();
371         InfoTok = INFOTOK_INTCON;
372         return;
373     }
374
375     /* Other characters */
376     switch (C) {
377
378         case '{':
379             NextChar ();
380             InfoTok = INFOTOK_LCURLY;
381             break;
382
383         case '}':
384             NextChar ();
385             InfoTok = INFOTOK_RCURLY;
386             break;
387
388         case ';':
389             NextChar ();
390             InfoTok = INFOTOK_SEMI;
391             break;
392
393         case '.':
394             NextChar ();
395             InfoTok = INFOTOK_DOT;
396             break;
397
398         case ',':
399             NextChar ();
400             InfoTok = INFOTOK_COMMA;
401             break;
402
403         case '=':
404             NextChar ();
405             InfoTok = INFOTOK_EQ;
406             break;
407
408         case ':':
409             NextChar ();
410             InfoTok = INFOTOK_COLON;
411             break;
412
413         case '\"':
414             NextChar ();
415             I = 0;
416             while (C != EOF && C != '\"') {
417                 if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
418                     if (C == EOF) {
419                         InfoError ("Unterminated string");
420                     } else  {
421                         InfoError ("Invalid escape char: %c", C);
422                     }
423                 }
424             }
425             if (C != '\"') {
426                 InfoError ("Unterminated string");
427             }
428             NextChar ();
429             InfoTok = INFOTOK_STRCON;
430             break;
431
432         case '\'':
433             NextChar ();
434             if (C == EOF || IsControl (C) || C == '\'') {
435                 InfoError ("Invalid character constant");
436             }
437             if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
438                 InfoError ("Invalid character constant");
439             }
440             InfoIVal = DecodeBuf [0];
441             if (C != '\'') {
442                 InfoError ("Unterminated character constant");
443             }
444             NextChar ();
445             InfoTok = INFOTOK_CHARCON;
446             break;
447
448         case '#':
449             /* # lineno "sourcefile" or # comment */
450             if (SyncLines && InputCol == 1) {
451                 LineMarkerOrComment ();
452             } else {
453                 do {
454                     NextChar ();
455                 } while (C != EOF && C != '\n');
456                 NextChar ();
457             }
458             if (C != EOF) {
459                 goto Again;
460             }
461             InfoTok = INFOTOK_EOF;
462             break;
463
464         case '/':
465             /* C++ style comment */
466             NextChar ();
467             if (C != '/') {
468                 InfoError ("Invalid token `/'");
469             }
470             do {
471                 NextChar ();
472             } while (C != '\n' && C != EOF);
473             if (C != EOF) {
474                 goto Again;
475             }
476             InfoTok = INFOTOK_EOF;
477             break;
478
479         case EOF:
480             InfoTok = INFOTOK_EOF;
481             break;
482
483         default:
484             InfoError ("Invalid character `%c'", C);
485
486     }
487 }
488
489
490
491 void InfoConsume (unsigned T, const char* Msg)
492 /* Skip a token, print an error message if not found */
493 {
494     if (InfoTok != T) {
495         InfoError (Msg);
496     }
497     InfoNextTok ();
498 }
499
500
501
502 void InfoConsumeLCurly (void)
503 /* Consume a left curly brace */
504 {
505     InfoConsume (INFOTOK_LCURLY, "`{' expected");
506 }
507
508
509
510 void InfoConsumeRCurly (void)
511 /* Consume a right curly brace */
512 {
513     InfoConsume (INFOTOK_RCURLY, "`}' expected");
514 }
515
516
517
518 void InfoConsumeSemi (void)
519 /* Consume a semicolon */
520 {
521     InfoConsume (INFOTOK_SEMI, "`;' expected");
522 }
523
524
525
526 void InfoConsumeColon (void)
527 /* Consume a colon */
528 {
529     InfoConsume (INFOTOK_COLON, "`:' expected");
530 }
531
532
533
534 void InfoOptionalComma (void)
535 /* Consume a comma if there is one */
536 {
537     if (InfoTok == INFOTOK_COMMA) {
538         InfoNextTok ();
539     }
540 }
541
542
543
544 void InfoOptionalAssign (void)
545 /* Consume an equal sign if there is one */
546 {
547     if (InfoTok == INFOTOK_EQ) {
548         InfoNextTok ();
549     }
550 }
551
552
553
554 void InfoAssureInt (void)
555 /* Make sure the next token is an integer */
556 {
557     if (InfoTok != INFOTOK_INTCON) {
558         InfoError ("Integer constant expected");
559     }
560 }
561
562
563
564 void InfoAssureStr (void)
565 /* Make sure the next token is a string constant */
566 {
567     if (InfoTok != INFOTOK_STRCON) {
568         InfoError ("String constant expected");
569     }
570 }
571
572
573
574 void InfoAssureChar (void)
575 /* Make sure the next token is a char constant */
576 {
577     if (InfoTok != INFOTOK_STRCON) {
578         InfoError ("Character constant expected");
579     }
580 }
581
582
583
584 void InfoAssureIdent (void)
585 /* Make sure the next token is an identifier */
586 {
587     if (InfoTok != INFOTOK_IDENT) {
588         InfoError ("Identifier expected");
589     }
590 }
591
592
593
594 void InfoRangeCheck (long Lo, long Hi)
595 /* Check the range of InfoIVal */
596 {
597     if (InfoIVal < Lo || InfoIVal > Hi) {
598         InfoError ("Range error");
599     }
600 }
601
602
603
604 void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
605 /* Map an identifier to one of the special tokens in the table */
606 {
607     unsigned I;
608
609     /* We need an identifier */
610     if (InfoTok == INFOTOK_IDENT) {
611
612         /* Make it upper case */
613         I = 0;
614         while (InfoSVal [I]) {
615             InfoSVal [I] = toupper (InfoSVal [I]);
616             ++I;
617         }
618
619         /* Linear search */
620         for (I = 0; I < Size; ++I) {
621             if (strcmp (InfoSVal, Table [I].Ident) == 0) {
622                 InfoTok = Table [I].Tok;
623                 return;
624             }
625         }
626
627     }
628
629     /* Not found or no identifier */
630     InfoError ("%s expected", Name);
631 }
632
633
634
635 void InfoBoolToken (void)
636 /* Map an identifier or integer to a boolean token */
637 {
638     static const IdentTok Booleans [] = {
639         {   "YES",      INFOTOK_TRUE     },
640         {   "NO",       INFOTOK_FALSE    },
641         {   "TRUE",     INFOTOK_TRUE     },
642         {   "FALSE",    INFOTOK_FALSE    },
643         {   "ON",       INFOTOK_TRUE     },
644         {   "OFF",      INFOTOK_FALSE    },
645     };
646
647     /* If we have an identifier, map it to a boolean token */
648     if (InfoTok == INFOTOK_IDENT) {
649         InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
650     } else {
651         /* We expected an integer here */
652         if (InfoTok != INFOTOK_INTCON) {
653             InfoError ("Boolean value expected");
654         }
655         InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
656     }
657 }
658
659
660
661 void InfoSetName (const char* Name)
662 /* Set a name for a config file */
663 {
664     InfoFile = Name;
665     xfree(InputSrcName);
666     InputSrcName = xstrdup(Name);
667 }
668
669
670
671 int InfoAvail ()
672 /* Return true if we have an info file given */
673 {
674     return (InfoFile != 0);
675 }
676
677
678
679 void InfoOpenInput (void)
680 /* Open the input file */
681 {
682     /* Open the file */
683     InputFile = fopen (InfoFile, "r");
684     if (InputFile == 0) {
685         Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
686     }
687
688     /* Initialize variables */
689     C         = ' ';
690     InputLine = 1;
691     InputCol  = 0;
692
693     /* Start the ball rolling ... */
694     InfoNextTok ();
695 }
696
697
698
699 void InfoCloseInput (void)
700 /* Close the input file if we have one */
701 {
702     /* Close the input file if we had one */
703     if (InputFile) {
704         (void) fclose (InputFile);
705         InputFile = 0;
706     }
707 }