1 /*****************************************************************************/
5 /* Input file handling */
9 /* (C) 2000 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
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. */
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: */
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 */
32 /*****************************************************************************/
39 #include <sys/types.h>
58 /*****************************************************************************/
60 /*****************************************************************************/
64 /* Input line stuff */
65 static char LineBuf [LINESIZE];
67 const char* lptr = LineBuf;
69 /* Current and next input character */
73 /* Maximum count of nested includes */
74 #define MAX_INC_NESTING 16
76 /* Struct that describes an active input file */
77 typedef struct AFile AFile;
79 unsigned Line; /* Line number for this file */
80 FILE* F; /* Input file stream */
81 IFile* Input; /* Points to corresponding IFile */
84 /* List of all input files */
85 static Collection IFiles = STATIC_COLLECTION_INITIALIZER;
87 /* List of all active files */
88 static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
92 /*****************************************************************************/
94 /*****************************************************************************/
98 static IFile* NewIFile (const char* Name)
99 /* Create and return a new IFile */
101 /* Get the length of the name */
102 unsigned Len = strlen (Name);
104 /* Allocate a IFile structure */
105 IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
107 /* Initialize the fields */
108 IF->Index = CollCount (&IFiles) + 1;
112 memcpy (IF->Name, Name, Len+1);
114 /* Insert the new structure into the IFile collection */
115 CollAppend (&IFiles, IF);
117 /* Return the new struct */
123 /*****************************************************************************/
125 /*****************************************************************************/
129 static AFile* NewAFile (IFile* IF, FILE* F)
130 /* Create and return a new AFile */
132 /* Allocate a AFile structure */
133 AFile* AF = (AFile*) xmalloc (sizeof (AFile));
135 /* Initialize the fields */
140 /* Increment the usage counter of the corresponding IFile. If this
141 * is the first use, set the file data and output debug info if
144 if (IF->Usage++ == 0) {
146 /* Get file size and modification time */
148 if (fstat (fileno (F), &Buf) != 0) {
150 Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno));
152 IF->Size = (unsigned long) Buf.st_size;
153 IF->MTime = (unsigned long) Buf.st_mtime;
155 /* Set the debug data */
156 g_fileinfo (IF->Name, IF->Size, IF->MTime);
159 /* Insert the new structure into the AFile collection */
160 CollAppend (&AFiles, AF);
162 /* Return the new struct */
168 static void FreeAFile (AFile* AF)
169 /* Free an AFile structure */
176 /*****************************************************************************/
178 /*****************************************************************************/
182 static IFile* FindFile (const char* Name)
183 /* Find the file with the given name in the list of all files. Since the list
184 * is not large (usually less than 10), I don't care about using hashes or
185 * similar things and do a linear search.
189 for (I = 0; I < CollCount (&IFiles); ++I) {
190 /* Get the file struct */
191 IFile* IF = (IFile*) CollAt (&IFiles, I);
193 if (strcmp (Name, IF->Name) == 0) {
194 /* Found, return the struct */
205 void OpenMainFile (const char* Name)
206 /* Open the main file. Will call Fatal() in case of failures. */
208 /* Setup a new IFile structure for the main file */
209 IFile* IF = NewIFile (Name);
211 /* Open the file for reading */
212 FILE* F = fopen (Name, "r");
215 Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
218 /* Allocate a new AFile structure for the file */
219 (void) NewAFile (IF, F);
224 void OpenIncludeFile (const char* Name, unsigned DirSpec)
225 /* Open an include file and insert it into the tables. */
231 /* Check for the maximum include nesting */
232 if (CollCount (&AFiles) > MAX_INC_NESTING) {
233 PPError ("Include nesting too deep");
237 /* Search for the file */
238 N = FindInclude (Name, DirSpec);
240 PPError ("Include file `%s' not found", Name);
244 /* Search the list of all input files for this file. If we don't find
245 * it, create a new IFile object.
252 /* We don't need N any longer, since we may now use IF->Name */
256 F = fopen (IF->Name, "r");
258 /* Error opening the file */
259 PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno));
263 /* Debugging output */
264 Print (stdout, 1, "Opened include file `%s'\n", IF->Name);
266 /* Allocate a new AFile structure */
267 (void) NewAFile (IF, F);
272 static void CloseIncludeFile (void)
273 /* Close an include file and switch to the higher level file. Set Input to
274 * NULL if this was the main file.
279 /* Get the number of active input files */
280 unsigned AFileCount = CollCount (&AFiles);
282 /* Must have an input file when called */
283 PRECONDITION (AFileCount > 0);
285 /* Get the current active input file */
286 Input = (AFile*) CollLast (&AFiles);
288 /* Close the current input file (we're just reading so no error check) */
291 /* Delete the last active file from the active file collection */
292 CollDelete (&AFiles, AFileCount-1);
294 /* Delete the active file structure */
300 void ClearLine (void)
301 /* Clear the current input line */
311 void InitLine (const char* Buf)
312 /* Initialize lptr from Buf and read CurC and NextC from the new input line */
326 /* Read the next character from the input stream and make CurC and NextC
327 * valid. If end of line is reached, both are set to NUL, no more lines
328 * are read by this function.
331 if (lptr[0] != '\0') {
347 /* Get a line from the current input. Returns 0 on end of file. */
358 /* If there is no file open, bail out, otherwise get the current input file */
359 if (CollCount (&AFiles) == 0) {
362 Input = (AFile*) CollLast (&AFiles);
364 /* Read lines until we get one with real contents */
367 while (!Done && Len < LINESIZE) {
369 while (fgets (line + Len, LINESIZE - Len, Input->F) == 0) {
374 /* Leave the current file */
377 /* If there is no file open, bail out, otherwise get the
380 if (CollCount (&AFiles) == 0) {
383 Input = (AFile*) CollLast (&AFiles);
387 /* We got a new line */
390 /* Remove the trailing cr/lf if we have one. We will ignore both, cr
391 * and lf on all systems since this enables us to compile DOS/Windows
392 * stuff also on unix systems (where fgets does not remove the cr).
394 Part = strlen (line + Len);
397 while (Len > 0 && (line[Len-1] == '\n' || line[Len-1] == '\r')) {
402 /* Check if we have a line continuation character at the end. If not,
405 if (Len > 0 && line[Len-1] == '\\') {
406 line[Len-1] = '\n'; /* Replace by newline */
412 /* Got a line. Initialize the current and next characters. */
415 /* Create line information for this line */
416 UpdateLineInfo (Input->Input, Input->Line, line);
424 const char* GetCurrentFile (void)
425 /* Return the name of the current input file */
427 unsigned AFileCount = CollCount (&AFiles);
428 if (AFileCount > 0) {
429 const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
430 return AF->Input->Name;
432 /* No open file. Use the main file if we have one. */
433 unsigned IFileCount = CollCount (&IFiles);
434 if (IFileCount > 0) {
435 const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
438 return "(outside file scope)";
445 unsigned GetCurrentLine (void)
446 /* Return the line number in the current input file */
448 unsigned AFileCount = CollCount (&AFiles);
449 if (AFileCount > 0) {
450 const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
460 void WriteDependencies (FILE* F, const char* OutputFile)
461 /* Write a makefile dependency list to the given file */
465 /* Get the number of input files */
466 unsigned IFileCount = CollCount (&IFiles);
468 /* Print the output file followed by a tab char */
469 fprintf (F, "%s:\t", OutputFile);
471 /* Loop over all files */
472 for (I = 0; I < IFileCount; ++I) {
473 /* Get the next input file */
474 const IFile* IF = (const IFile*) CollAt (&IFiles, I);
475 /* If this is not the first file, add a space */
476 const char* Format = (I == 0)? "%s" : " %s";
477 /* Print the dependency */
478 fprintf (F, Format, IF->Name);