]> git.sur5r.net Git - cc65/blob - src/cc65/input.c
Removed unneeded include files.
[cc65] / src / cc65 / input.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  input.c                                  */
4 /*                                                                           */
5 /*                            Input file handling                            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2010, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 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 <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41
42 /* common */
43 #include "check.h"
44 #include "coll.h"
45 #include "fname.h"
46 #include "print.h"
47 #include "strbuf.h"
48 #include "xmalloc.h"
49
50 /* cc65 */
51 #include "codegen.h"
52 #include "error.h"
53 #include "global.h"
54 #include "incpath.h"
55 #include "input.h"
56 #include "lineinfo.h"
57 #include "output.h"
58
59
60
61 /*****************************************************************************/
62 /*                                   Data                                    */
63 /*****************************************************************************/
64
65
66
67 /* The current input line */
68 StrBuf* Line;
69
70 /* Current and next input character */
71 char CurC  = '\0';
72 char NextC = '\0';
73
74 /* Maximum count of nested includes */
75 #define MAX_INC_NESTING         16
76
77 /* Struct that describes an input file */
78 typedef struct IFile IFile;
79 struct IFile {
80     unsigned        Index;      /* File index */
81     unsigned        Usage;      /* Usage counter */
82     unsigned long   Size;       /* File size */
83     unsigned long   MTime;      /* Time of last modification */
84     InputType       Type;       /* Type of input file */
85     char            Name[1];    /* Name of file (dynamically allocated) */
86 };
87
88 /* Struct that describes an active input file */
89 typedef struct AFile AFile;
90 struct AFile {
91     unsigned    Line;           /* Line number for this file */
92     FILE*       F;              /* Input file stream */
93     IFile*      Input;          /* Points to corresponding IFile */
94     int         SearchPath;     /* True if we've added a path for this file */
95 };
96
97 /* List of all input files */
98 static Collection IFiles = STATIC_COLLECTION_INITIALIZER;
99
100 /* List of all active files */
101 static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
102
103 /* Input stack used when preprocessing. */
104 static Collection InputStack = STATIC_COLLECTION_INITIALIZER;
105
106
107
108 /*****************************************************************************/
109 /*                               struct IFile                                */
110 /*****************************************************************************/
111
112
113
114 static IFile* NewIFile (const char* Name, InputType Type)
115 /* Create and return a new IFile */
116 {
117     /* Get the length of the name */
118     unsigned Len = strlen (Name);
119
120     /* Allocate a IFile structure */
121     IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
122
123     /* Initialize the fields */
124     IF->Index = CollCount (&IFiles) + 1;
125     IF->Usage = 0;
126     IF->Size  = 0;
127     IF->MTime = 0;
128     IF->Type  = Type;
129     memcpy (IF->Name, Name, Len+1);
130
131     /* Insert the new structure into the IFile collection */
132     CollAppend (&IFiles, IF);
133
134     /* Return the new struct */
135     return IF;
136 }
137
138
139
140 /*****************************************************************************/
141 /*                               struct AFile                                */
142 /*****************************************************************************/
143
144
145
146 static AFile* NewAFile (IFile* IF, FILE* F)
147 /* Create a new AFile, push it onto the stack, add the path of the file to
148  * the path search list, and finally return a pointer to the new AFile struct.
149  */
150 {
151     StrBuf Path = AUTO_STRBUF_INITIALIZER;
152
153     /* Allocate a AFile structure */
154     AFile* AF = (AFile*) xmalloc (sizeof (AFile));
155
156     /* Initialize the fields */
157     AF->Line  = 0;
158     AF->F     = F;
159     AF->Input = IF;
160
161     /* Increment the usage counter of the corresponding IFile. If this
162      * is the first use, set the file data and output debug info if
163      * requested.
164      */
165     if (IF->Usage++ == 0) {
166
167         /* Get file size and modification time. There a race condition here,
168          * since we cannot use fileno() (non standard identifier in standard
169          * header file), and therefore not fstat. When using stat with the
170          * file name, there's a risk that the file was deleted and recreated
171          * while it was open. Since mtime and size are only used to check
172          * if a file has changed in the debugger, we will ignore this problem
173          * here.
174          */
175         struct stat Buf;
176         if (stat (IF->Name, &Buf) != 0) {
177             /* Error */
178             Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno));
179         }
180         IF->Size  = (unsigned long) Buf.st_size;
181         IF->MTime = (unsigned long) Buf.st_mtime;
182
183         /* Set the debug data */
184         g_fileinfo (IF->Name, IF->Size, IF->MTime);
185     }
186
187     /* Insert the new structure into the AFile collection */
188     CollAppend (&AFiles, AF);
189
190     /* Get the path of this file and add it as an extra search path.
191      * To avoid file search overhead, we will add one path only once.
192      * This is checked by the PushSearchPath function.
193      */
194     SB_CopyBuf (&Path, IF->Name, FindName (IF->Name) - IF->Name);
195     SB_Terminate (&Path);
196     AF->SearchPath = PushSearchPath (UsrIncSearchPath, SB_GetConstBuf (&Path));
197     SB_Done (&Path);
198
199     /* Return the new struct */
200     return AF;
201 }
202
203
204
205 static void FreeAFile (AFile* AF)
206 /* Free an AFile structure */
207 {
208     xfree (AF);
209 }
210
211
212
213 /*****************************************************************************/
214 /*                                   Code                                    */
215 /*****************************************************************************/
216
217
218
219 static IFile* FindFile (const char* Name)
220 /* Find the file with the given name in the list of all files. Since the list
221  * is not large (usually less than 10), I don't care about using hashes or
222  * similar things and do a linear search.
223  */
224 {
225     unsigned I;
226     for (I = 0; I < CollCount (&IFiles); ++I) {
227         /* Get the file struct */
228         IFile* IF = (IFile*) CollAt (&IFiles, I);
229         /* Check the name */
230         if (strcmp (Name, IF->Name) == 0) {
231             /* Found, return the struct */
232             return IF;
233         }
234     }
235
236     /* Not found */
237     return 0;
238 }
239
240
241
242 void OpenMainFile (const char* Name)
243 /* Open the main file. Will call Fatal() in case of failures. */
244 {
245     AFile* MainFile;
246
247
248     /* Setup a new IFile structure for the main file */
249     IFile* IF = NewIFile (Name, IT_MAIN);
250
251     /* Open the file for reading */
252     FILE* F = fopen (Name, "r");
253     if (F == 0) {
254         /* Cannot open */
255         Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
256     }
257
258     /* Allocate a new AFile structure for the file */
259     MainFile = NewAFile (IF, F);
260
261     /* Allocate the input line buffer */
262     Line = NewStrBuf ();
263
264     /* Update the line infos, so we have a valid line info even at start of
265      * the main file before the first line is read.
266      */
267     UpdateLineInfo (MainFile->Input, MainFile->Line, Line);
268 }
269
270
271
272 void OpenIncludeFile (const char* Name, InputType IT)
273 /* Open an include file and insert it into the tables. */
274 {
275     char*  N;
276     FILE*  F;
277     IFile* IF;
278
279     /* Check for the maximum include nesting */
280     if (CollCount (&AFiles) > MAX_INC_NESTING) {
281         PPError ("Include nesting too deep");
282         return;
283     }
284
285     /* Search for the file */
286     N = SearchFile ((IT == IT_SYSINC)? SysIncSearchPath : UsrIncSearchPath, Name);
287     if (N == 0) {
288         PPError ("Include file `%s' not found", Name);
289         return;
290     }
291
292     /* Search the list of all input files for this file. If we don't find
293      * it, create a new IFile object.
294      */
295     IF = FindFile (N);
296     if (IF == 0) {
297         IF = NewIFile (N, IT);
298     }
299
300     /* We don't need N any longer, since we may now use IF->Name */
301     xfree (N);
302
303     /* Open the file */
304     F = fopen (IF->Name, "r");
305     if (F == 0) {
306         /* Error opening the file */
307         PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno));
308         return;
309     }
310
311     /* Debugging output */
312     Print (stdout, 1, "Opened include file `%s'\n", IF->Name);
313
314     /* Allocate a new AFile structure */
315     (void) NewAFile (IF, F);
316 }
317
318
319
320 static void CloseIncludeFile (void)
321 /* Close an include file and switch to the higher level file. Set Input to
322  * NULL if this was the main file.
323  */
324 {
325     AFile* Input;
326
327     /* Get the number of active input files */
328     unsigned AFileCount = CollCount (&AFiles);
329
330     /* Must have an input file when called */
331     PRECONDITION (AFileCount > 0);
332
333     /* Get the current active input file */
334     Input = (AFile*) CollLast (&AFiles);
335
336     /* Close the current input file (we're just reading so no error check) */
337     fclose (Input->F);
338
339     /* Delete the last active file from the active file collection */
340     CollDelete (&AFiles, AFileCount-1);
341
342     /* If we had added an extra search path for this AFile, remove it */
343     if (Input->SearchPath) {
344         PopSearchPath (UsrIncSearchPath);
345     }
346
347     /* Delete the active file structure */
348     FreeAFile (Input);
349 }
350
351
352
353 static void GetInputChar (void)
354 /* Read the next character from the input stream and make CurC and NextC
355  * valid. If end of line is reached, both are set to NUL, no more lines
356  * are read by this function.
357  */
358 {
359     /* Drop all pushed fragments that don't have data left */
360     while (SB_GetIndex (Line) >= SB_GetLen (Line)) {
361         /* Cannot read more from this line, check next line on stack if any */
362         if (CollCount (&InputStack) == 0) {
363             /* This is THE line */
364             break;
365         }
366         FreeStrBuf (Line);
367         Line = CollPop (&InputStack);
368     }
369
370     /* Now get the next characters from the line */
371     if (SB_GetIndex (Line) >= SB_GetLen (Line)) {
372         CurC = NextC = '\0';
373     } else {
374         CurC = SB_AtUnchecked (Line, SB_GetIndex (Line));
375         if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) {
376             /* NextC comes from this fragment */
377             NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1);
378         } else {
379             /* NextC comes from next fragment */
380             if (CollCount (&InputStack) > 0) {
381                 NextC = ' ';
382             } else {
383                 NextC = '\0';
384             }
385         }
386     }
387 }
388
389
390
391 void NextChar (void)
392 /* Skip the current input character and read the next one from the input
393  * stream. CurC and NextC are valid after the call. If end of line is
394  * reached, both are set to NUL, no more lines are read by this function.
395  */
396 {
397     /* Skip the last character read */
398     SB_Skip (Line);
399
400     /* Read the next one */
401     GetInputChar ();
402 }
403
404
405
406 void ClearLine (void)
407 /* Clear the current input line */
408 {
409     unsigned I;
410
411     /* Remove all pushed fragments from the input stack */
412     for (I = 0; I < CollCount (&InputStack); ++I) {
413         FreeStrBuf (CollAtUnchecked (&InputStack, I));
414     }
415     CollDeleteAll (&InputStack);
416
417     /* Clear the contents of Line */
418     SB_Clear (Line);
419     CurC    = '\0';
420     NextC   = '\0';
421 }
422
423
424
425 StrBuf* InitLine (StrBuf* Buf)
426 /* Initialize Line from Buf and read CurC and NextC from the new input line.
427  * The function returns the old input line.
428  */
429 {
430     StrBuf* OldLine = Line;
431     Line  = Buf;
432     CurC  = SB_LookAt (Buf, SB_GetIndex (Buf));
433     NextC = SB_LookAt (Buf, SB_GetIndex (Buf) + 1);
434     return OldLine;
435 }
436
437
438
439 int NextLine (void)
440 /* Get a line from the current input. Returns 0 on end of file. */
441 {
442     AFile*      Input;
443
444     /* Clear the current line */
445     ClearLine ();
446
447     /* If there is no file open, bail out, otherwise get the current input file */
448     if (CollCount (&AFiles) == 0) {
449         return 0;
450     }
451     Input = CollLast (&AFiles);
452
453     /* Read characters until we have one complete line */
454     while (1) {
455
456         /* Read the next character */
457         int C = fgetc (Input->F);
458
459         /* Check for EOF */
460         if (C == EOF) {
461
462             /* Accept files without a newline at the end */
463             if (SB_NotEmpty (Line)) {
464                 ++Input->Line;
465                 break;
466             }
467
468             /* Leave the current file */
469             CloseIncludeFile ();
470
471             /* If there is no file open, bail out, otherwise get the
472              * previous input file and start over.
473              */
474             if (CollCount (&AFiles) == 0) {
475                 return 0;
476             }
477             Input = CollLast (&AFiles);
478             continue;
479         }
480
481         /* Check for end of line */
482         if (C == '\n') {
483
484             /* We got a new line */
485             ++Input->Line;
486
487             /* If the \n is preceeded by a \r, remove the \r, so we can read
488              * DOS/Windows files under *nix.
489              */
490             if (SB_LookAtLast (Line) == '\r') {
491                 SB_Drop (Line, 1);
492             }
493
494             /* If we don't have a line continuation character at the end,
495              * we're done with this line. Otherwise replace the character
496              * by a newline and continue reading.
497              */
498             if (SB_LookAtLast (Line) == '\\') {
499                 Line->Buf[Line->Len-1] = '\n';
500             } else {
501                 break;
502             }
503
504         } else if (C != '\0') {         /* Ignore embedded NULs */
505
506             /* Just some character, add it to the line */
507             SB_AppendChar (Line, C);
508
509         }
510     }
511
512     /* Add a termination character to the string buffer */
513     SB_Terminate (Line);
514
515     /* Initialize the current and next characters. */
516     InitLine (Line);
517
518     /* Create line information for this line */
519     UpdateLineInfo (Input->Input, Input->Line, Line);
520
521     /* Done */
522     return 1;
523 }
524
525
526
527 const char* GetInputFile (const struct IFile* IF)
528 /* Return a filename from an IFile struct */
529 {
530     return IF->Name;
531 }
532
533
534
535 const char* GetCurrentFile (void)
536 /* Return the name of the current input file */
537 {
538     unsigned AFileCount = CollCount (&AFiles);
539     if (AFileCount > 0) {
540         const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
541         return AF->Input->Name;
542     } else {
543         /* No open file. Use the main file if we have one. */
544         unsigned IFileCount = CollCount (&IFiles);
545         if (IFileCount > 0) {
546             const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
547             return IF->Name;
548         } else {
549             return "(outside file scope)";
550         }
551     }
552 }
553
554
555
556 unsigned GetCurrentLine (void)
557 /* Return the line number in the current input file */
558 {
559     unsigned AFileCount = CollCount (&AFiles);
560     if (AFileCount > 0) {
561         const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
562         return AF->Line;
563     } else {
564         /* No open file */
565         return 0;
566     }
567 }
568
569
570
571 static void WriteEscaped (FILE* F, const char* Name)
572 /* Write a file name to a dependency file escaping spaces */
573 {
574     while (*Name) {
575         if (*Name == ' ') {
576             /* Escape spaces */
577             fputc ('\\', F);
578         }
579         fputc (*Name, F);
580         ++Name;
581     }
582 }
583
584
585
586 static void WriteDep (FILE* F, InputType Types)
587 /* Helper function. Writes all file names that match Types to the output */
588 {
589     unsigned I;
590
591     /* Loop over all files */
592     unsigned FileCount = CollCount (&IFiles);
593     for (I = 0; I < FileCount; ++I) {
594
595         /* Get the next input file */
596         const IFile* IF = (const IFile*) CollAt (&IFiles, I);
597
598         /* Ignore it if it is not of the correct type */
599         if ((IF->Type & Types) == 0) {
600             continue;
601         }
602
603         /* If this is not the first file, add a space */
604         if (I > 0) {
605             fputc (' ', F);
606         }
607
608         /* Print the dependency escaping spaces */
609         WriteEscaped (F, IF->Name);
610     }
611 }
612
613
614
615 static void CreateDepFile (const char* Name, InputType Types)
616 /* Create a dependency file with the given name and place dependencies for
617  * all files with the given types there.
618  */
619 {
620     /* Open the file */
621     FILE* F = fopen (Name, "w");
622     if (F == 0) {
623         Fatal ("Cannot open dependency file `%s': %s", Name, strerror (errno));
624     }
625
626     /* If a dependency target was given, use it, otherwise use the output
627      * file name as target, followed by a tab character.
628      */
629     if (SB_IsEmpty (&DepTarget)) {
630         WriteEscaped (F, OutputFilename);
631     } else {
632         WriteEscaped (F, SB_GetConstBuf (&DepTarget));
633     }
634     fputs (":\t", F);
635
636     /* Write out the dependencies for the output file */
637     WriteDep (F, Types);
638     fputs ("\n\n", F);
639
640     /* Write out a phony dependency for the included files */
641     WriteDep (F, Types);
642     fputs (":\n\n", F);
643
644     /* Close the file, check for errors */
645     if (fclose (F) != 0) {
646         remove (Name);
647         Fatal ("Cannot write to dependeny file (disk full?)");
648     }
649 }
650
651
652
653 void CreateDependencies (void)
654 /* Create dependency files requested by the user */
655 {
656     if (SB_NotEmpty (&DepName)) {
657         CreateDepFile (SB_GetConstBuf (&DepName),
658                        IT_MAIN | IT_USRINC);
659     }
660     if (SB_NotEmpty (&FullDepName)) {
661         CreateDepFile (SB_GetConstBuf (&FullDepName),
662                        IT_MAIN | IT_SYSINC | IT_USRINC);
663     }
664 }
665
666