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