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>
57 /*****************************************************************************/
59 /*****************************************************************************/
63 /* Input line stuff */
64 static char LineBuf [LINESIZE];
66 const char* lptr = LineBuf;
68 /* Current and next input character */
72 /* Maximum count of nested includes */
73 #define MAX_INC_NESTING 16
75 /* Struct that describes an active input file */
76 typedef struct AFile AFile;
78 unsigned Line; /* Line number for this file */
79 FILE* F; /* Input file stream */
80 IFile* Input; /* Points to corresponding IFile */
83 /* List of all input files */
84 static Collection IFiles = STATIC_COLLECTION_INITIALIZER;
86 /* List of all active files */
87 static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
91 /*****************************************************************************/
93 /*****************************************************************************/
97 static IFile* NewIFile (const char* Name)
98 /* Create and return a new IFile */
100 /* Get the length of the name */
101 unsigned Len = strlen (Name);
103 /* Allocate a IFile structure */
104 IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
106 /* Initialize the fields */
107 IF->Index = CollCount (&IFiles) + 1;
111 memcpy (IF->Name, Name, Len+1);
113 /* Insert the new structure into the IFile collection */
114 CollAppend (&IFiles, IF);
116 /* Return the new struct */
122 /*****************************************************************************/
124 /*****************************************************************************/
128 static AFile* NewAFile (IFile* IF, FILE* F)
129 /* Create and return a new AFile */
131 /* Allocate a AFile structure */
132 AFile* AF = (AFile*) xmalloc (sizeof (AFile));
134 /* Initialize the fields */
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
143 if (IF->Usage++ == 0) {
145 /* Get file size and modification time */
147 if (fstat (fileno (F), &Buf) != 0) {
149 Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno));
151 IF->Size = (unsigned long) Buf.st_size;
152 IF->MTime = (unsigned long) Buf.st_mtime;
154 /* Set the debug data */
155 g_fileinfo (IF->Name, IF->Size, IF->MTime);
158 /* Insert the new structure into the AFile collection */
159 CollAppend (&AFiles, AF);
161 /* Return the new struct */
167 static void FreeAFile (AFile* AF)
168 /* Free an AFile structure */
175 /*****************************************************************************/
177 /*****************************************************************************/
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.
188 for (I = 0; I < CollCount (&IFiles); ++I) {
189 /* Get the file struct */
190 IFile* IF = (IFile*) CollAt (&IFiles, I);
192 if (strcmp (Name, IF->Name) == 0) {
193 /* Found, return the struct */
204 void OpenMainFile (const char* Name)
205 /* Open the main file. Will call Fatal() in case of failures. */
207 /* Setup a new IFile structure for the main file */
208 IFile* IF = NewIFile (Name);
210 /* Open the file for reading */
211 FILE* F = fopen (Name, "r");
214 Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
217 /* Allocate a new AFile structure for the file */
218 (void) NewAFile (IF, F);
223 void OpenIncludeFile (const char* Name, unsigned DirSpec)
224 /* Open an include file and insert it into the tables. */
230 /* Check for the maximum include nesting */
231 if (CollCount (&AFiles) > MAX_INC_NESTING) {
232 PPError ("Include nesting too deep");
236 /* Search for the file */
237 N = FindInclude (Name, DirSpec);
239 PPError ("Include file `%s' not found", Name);
243 /* Search the list of all input files for this file. If we don't find
244 * it, create a new IFile object.
251 /* We don't need N any longer, since we may now use IF->Name */
255 F = fopen (IF->Name, "r");
257 /* Error opening the file */
258 PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno));
262 /* Allocate a new AFile structure */
263 (void) NewAFile (IF, F);
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.
275 /* Get the number of active input files */
276 unsigned AFileCount = CollCount (&AFiles);
278 /* Must have an input file when called */
279 PRECONDITION (AFileCount > 0);
281 /* Get the current active input file */
282 Input = (AFile*) CollLast (&AFiles);
284 /* Close the current input file (we're just reading so no error check) */
287 /* Delete the last active file from the active file collection */
288 CollDelete (&AFiles, AFileCount-1);
290 /* Delete the active file structure */
296 void ClearLine (void)
297 /* Clear the current input line */
307 void InitLine (const char* Buf)
308 /* Initialize lptr from Buf and read CurC and NextC from the new input line */
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.
327 if (lptr[0] != '\0') {
343 /* Get a line from the current input. Returns 0 on end of file. */
354 /* If there is no file open, bail out, otherwise get the current input file */
355 if (CollCount (&AFiles) == 0) {
358 Input = (AFile*) CollLast (&AFiles);
360 /* Read lines until we get one with real contents */
363 while (!Done && Len < LINESIZE) {
365 while (fgets (line + Len, LINESIZE - Len, Input->F) == 0) {
370 /* Leave the current file */
373 /* If there is no file open, bail out, otherwise get the
376 if (CollCount (&AFiles) == 0) {
379 Input = (AFile*) CollLast (&AFiles);
383 /* We got a new line */
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).
390 Part = strlen (line + Len);
393 while (Len > 0 && (line[Len-1] == '\n' || line[Len-1] == '\r')) {
398 /* Check if we have a line continuation character at the end. If not,
401 if (Len > 0 && line[Len-1] == '\\') {
402 line[Len-1] = '\n'; /* Replace by newline */
408 /* Got a line. Initialize the current and next characters. */
411 /* Create line information for this line */
412 UpdateLineInfo (Input->Input, Input->Line, line);
420 const char* GetCurrentFile (void)
421 /* Return the name of the current input file */
423 unsigned AFileCount = CollCount (&AFiles);
424 if (AFileCount > 0) {
425 const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
426 return AF->Input->Name;
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);
434 return "(outside file scope)";
441 unsigned GetCurrentLine (void)
442 /* Return the line number in the current input file */
444 unsigned AFileCount = CollCount (&AFiles);
445 if (AFileCount > 0) {
446 const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
456 void WriteDependencies (FILE* F, const char* OutputFile)
457 /* Write a makefile dependency list to the given file */
461 /* Get the number of input files */
462 unsigned IFileCount = CollCount (&IFiles);
464 /* Print the output file followed by a tab char */
465 fprintf (F, "%s:\t", OutputFile);
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);