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