]> git.sur5r.net Git - cc65/blob - src/cc65/input.c
f650dac5e7eb41c2f750929179ab00e9146cdab6
[cc65] / src / cc65 / input.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  input.c                                  */
4 /*                                                                           */
5 /*                            Input file handling                            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 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 <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 "xmalloc.h"
46
47 /* cc65 */
48 #include "asmcode.h" 
49 #include "codegen.h"
50 #include "error.h"
51 #include "incpath.h"
52 #include "lineinfo.h"
53 #include "input.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   Data                                    */
59 /*****************************************************************************/
60
61
62
63 /* Input line stuff */
64 static char LineBuf [LINESIZE];
65 char* line = LineBuf;
66 const char* lptr = LineBuf;
67
68 /* Current and next input character */
69 char CurC  = '\0';
70 char NextC = '\0';
71
72 /* Maximum count of nested includes */
73 #define MAX_INC_NESTING         16
74
75 /* Struct that describes an active input file */
76 typedef struct AFile AFile;
77 struct AFile {
78     unsigned    Line;           /* Line number for this file            */
79     FILE*       F;              /* Input file stream                    */
80     IFile*      Input;          /* Points to corresponding IFile        */
81 };
82
83 /* List of all input files */
84 static Collection IFiles = STATIC_COLLECTION_INITIALIZER;
85
86 /* List of all active files */
87 static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
88
89
90
91 /*****************************************************************************/
92 /*                             Helper functions                              */
93 /*****************************************************************************/
94
95
96
97 static long GetFileSize (FILE* F)
98 /* Calculate the size of the file F, return -1 on error. */
99 {
100     long Size;
101     long CurPos = ftell (F);
102     if (CurPos < 0) {
103         /* Error */
104         return -1;
105     }
106     if (fseek (F, 0, SEEK_END) != 0) {
107         /* Error */
108         return -1;
109     }
110     Size = ftell (F);
111     if (Size < 0) {
112         /* Error */
113         return -1;
114     }
115     if (fseek (F, CurPos, SEEK_SET) != 0) {
116         /* Error */
117         return -1;
118     }
119     return Size;
120 }
121
122
123
124 static long GetFileTime (const char* Name)
125 /* Get the time of last modification for the given file. Return -1 on errors. */
126 {
127     struct stat Buf;
128     if (stat (Name, &Buf) != 0) {
129         /* Error */
130         return -1;
131     }
132     return (long) Buf.st_mtime;
133 }
134
135
136
137 /*****************************************************************************/
138 /*                               struct IFile                                */
139 /*****************************************************************************/
140
141
142
143 static IFile* NewIFile (const char* Name)
144 /* Create and return a new IFile */
145 {
146     /* Get the length of the name */
147     unsigned Len = strlen (Name);
148
149     /* Allocate a IFile structure */
150     IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
151
152     /* Initialize the fields */
153     IF->Index = CollCount (&IFiles) + 1;
154     IF->Usage = 0;
155     IF->Size  = 0;
156     IF->MTime = 0;
157     memcpy (IF->Name, Name, Len+1);
158
159     /* Insert the new structure into the IFile collection */
160     CollAppend (&IFiles, IF);
161
162     /* Return the new struct */
163     return IF;
164 }
165
166
167
168 /*****************************************************************************/
169 /*                               struct AFile                                */
170 /*****************************************************************************/
171
172
173
174 static AFile* NewAFile (IFile* IF, FILE* F)
175 /* Create and return a new AFile */
176 {
177     /* Allocate a AFile structure */
178     AFile* AF = (AFile*) xmalloc (sizeof (AFile));
179
180     /* Initialize the fields */
181     AF->Line  = 0;
182     AF->F     = F;
183     AF->Input = IF;
184
185     /* Increment the usage counter of the corresponding IFile. If this
186      * is the first use, set the file data and output debug info if
187      * requested.
188      */
189     if (IF->Usage++ == 0) {
190
191         long Val;
192
193         /* Get the file size */
194         Val = GetFileSize (AF->F);
195         if (Val < 0) {
196             Fatal ("Cannot seek on `%s': %s", IF->Name, strerror (errno));
197         }
198         IF->Size = Val;
199
200         /* Get the file modification time */
201         Val = GetFileTime (IF->Name);
202         if (Val < 0) {
203             Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno));
204         }
205         IF->MTime = Val;
206
207         /* Set the debug data */
208         g_fileinfo (IF->Name, IF->Size, IF->MTime);
209     }
210
211     /* Insert the new structure into the AFile collection */
212     CollAppend (&AFiles, AF);
213
214     /* Return the new struct */
215     return AF;
216 }
217
218
219
220 static void FreeAFile (AFile* AF)
221 /* Free an AFile structure */
222 {
223     xfree (AF);
224 }
225
226
227
228 /*****************************************************************************/
229 /*                                   Code                                    */
230 /*****************************************************************************/
231
232
233
234 static IFile* FindFile (const char* Name)
235 /* Find the file with the given name in the list of all files. Since the list
236  * is not large (usually less than 10), I don't care about using hashes or
237  * similar things and do a linear search.
238  */
239 {
240     unsigned I;
241     for (I = 0; I < CollCount (&IFiles); ++I) {
242         /* Get the file struct */
243         IFile* IF = (IFile*) CollAt (&IFiles, I);
244         /* Check the name */
245         if (strcmp (Name, IF->Name) == 0) {
246             /* Found, return the struct */
247             return IF;
248         }
249     }
250
251     /* Not found */
252     return 0;
253 }
254
255
256
257 void OpenMainFile (const char* Name)
258 /* Open the main file. Will call Fatal() in case of failures. */
259 {
260     /* Setup a new IFile structure for the main file */
261     IFile* IF = NewIFile (Name);
262
263     /* Open the file for reading */
264     FILE* F = fopen (Name, "r");
265     if (F == 0) {
266         /* Cannot open */
267         Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
268     }
269
270     /* Allocate a new AFile structure for the file */
271     (void) NewAFile (IF, F);
272 }
273
274
275
276 void OpenIncludeFile (const char* Name, unsigned DirSpec)
277 /* Open an include file and insert it into the tables. */
278 {
279     char*  N;
280     FILE*  F;
281     IFile* IF;
282
283     /* Check for the maximum include nesting */
284     if (CollCount (&AFiles) > MAX_INC_NESTING) {
285         PPError ("Include nesting too deep");
286         return;
287     }
288
289     /* Search for the file */
290     N = FindInclude (Name, DirSpec);
291     if (N == 0) {
292         PPError ("Include file `%s' not found", Name);
293         return;
294     }
295
296     /* Search the list of all input files for this file. If we don't find
297      * it, create a new IFile object.
298      */
299     IF = FindFile (N);
300     if (IF == 0) {
301         IF = NewIFile (N);
302     }
303
304     /* We don't need N any longer, since we may now use IF->Name */
305     xfree (N);
306
307     /* Open the file */
308     F = fopen (IF->Name, "r");
309     if (F == 0) {
310         /* Error opening the file */
311         PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno));
312         return;
313     }
314
315     /* Allocate a new AFile structure */
316     (void) NewAFile (IF, F);
317 }
318
319
320
321 static void CloseIncludeFile (void)
322 /* Close an include file and switch to the higher level file. Set Input to
323  * NULL if this was the main file.
324  */
325 {
326     AFile* Input;
327
328     /* Get the number of active input files */
329     unsigned AFileCount = CollCount (&AFiles);
330
331     /* Must have an input file when called */
332     PRECONDITION (AFileCount > 0);
333
334     /* Get the current active input file */
335     Input = (AFile*) CollLast (&AFiles);
336
337     /* Close the current input file (we're just reading so no error check) */
338     fclose (Input->F);
339
340     /* Delete the last active file from the active file collection */
341     CollDelete (&AFiles, AFileCount-1);
342
343     /* Delete the active file structure */
344     FreeAFile (Input);
345 }
346
347
348
349 void ClearLine (void)
350 /* Clear the current input line */
351 {
352     line[0] = '\0';
353     lptr    = line;
354     CurC    = '\0';
355     NextC   = '\0';
356 }
357
358
359
360 void InitLine (const char* Buf)
361 /* Initialize lptr from Buf and read CurC and NextC from the new input line */
362 {
363     lptr = Buf;
364     CurC = lptr[0];
365     if (CurC != '\0') {
366         NextC = lptr[1];
367     } else {
368         NextC = '\0';
369     }
370 }
371
372
373
374 void NextChar (void)
375 /* Read the next character from the input stream and make CurC and NextC
376  * valid. If end of line is reached, both are set to NUL, no more lines
377  * are read by this function.
378  */
379 {
380     if (lptr[0] != '\0') {
381         ++lptr;
382         CurC = lptr[0];
383         if (CurC != '\0') {
384             NextC = lptr[1];
385         } else {
386             NextC = '\0';
387         }
388     } else {
389         CurC = NextC = '\0';
390     }
391 }
392
393
394
395 int NextLine (void)
396 /* Get a line from the current input. Returns 0 on end of file. */
397 {
398     AFile*      Input;
399     unsigned    Len;
400     unsigned    Part;
401     unsigned    Start;
402     int         Done;
403
404     /* Setup the line */
405     ClearLine ();
406
407     /* If there is no file open, bail out, otherwise get the current input file */
408     if (CollCount (&AFiles) == 0) {
409         return 0;
410     }
411     Input = (AFile*) CollLast (&AFiles);
412
413     /* Read lines until we get one with real contents */
414     Len = 0;
415     Done = 0;
416     while (!Done && Len < LINESIZE) {
417
418         while (fgets (line + Len, LINESIZE - Len, Input->F) == 0) {
419
420             /* Assume EOF */
421             ClearLine ();
422
423             /* Leave the current file */
424             CloseIncludeFile ();
425
426             /* If there is no file open, bail out, otherwise get the
427              * current input file
428              */
429             if (CollCount (&AFiles) == 0) {
430                 return 0;
431             }
432             Input = (AFile*) CollLast (&AFiles);
433
434         }
435
436         /* We got a new line */
437         ++Input->Line;
438
439         /* Remove the trailing newline if we have one */
440         Part = strlen (line + Len);
441         Start = Len;
442         Len += Part;
443         while (Len > 0 && line [Len-1] == '\n') {
444             --Len;
445         }
446         line [Len] = '\0';
447
448         /* Check if we have a line continuation character at the end. If not,
449          * we're done.
450          */
451         if (Len > 0 && line[Len-1] == '\\') {
452             line[Len-1] = '\n';         /* Replace by newline */
453         } else {
454             Done = 1;
455         }
456     }
457
458     /* Got a line. Initialize the current and next characters. */
459     InitLine (line);
460
461     /* Create line information for this line */
462     UpdateLineInfo (Input->Input, Input->Line, line);
463
464     /* Done */
465     return 1;
466 }
467
468
469
470 const char* GetCurrentFile (void)
471 /* Return the name of the current input file */
472 {
473     unsigned AFileCount = CollCount (&AFiles);
474     if (AFileCount > 0) {
475         const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
476         return AF->Input->Name;
477     } else {
478         /* No open file. Use the main file if we have one. */
479         unsigned IFileCount = CollCount (&IFiles);
480         if (IFileCount > 0) {
481             const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
482             return IF->Name;
483         } else {
484             return "(outside file scope)";
485         }
486     }
487 }
488
489
490
491 unsigned GetCurrentLine (void)
492 /* Return the line number in the current input file */
493 {
494     unsigned AFileCount = CollCount (&AFiles);
495     if (AFileCount > 0) {
496         const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
497         return AF->Line;
498     } else {
499         /* No open file */
500         return 0;
501     }
502 }
503
504
505
506 void WriteDependencies (FILE* F, const char* OutputFile)
507 /* Write a makefile dependency list to the given file */
508 {
509     unsigned I;
510
511     /* Get the number of input files */
512     unsigned IFileCount = CollCount (&IFiles);
513
514     /* Print the output file followed by a tab char */
515     fprintf (F, "%s:\t", OutputFile);
516
517     /* Loop over all files */
518     for (I = 0; I < IFileCount; ++I) {
519         /* Get the next input file */
520         const IFile* IF = (const IFile*) CollAt (&IFiles, I);
521         /* If this is not the first file, add a space */
522         const char* Format = (I == 0)? "%s" : " %s";
523         /* Print the dependency */
524         fprintf (F, Format, IF->Name);
525     }
526
527     /* End the line */
528     fprintf (F, "\n\n");
529 }
530
531
532