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