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 /*****************************************************************************/
92 /* Helper functions */
93 /*****************************************************************************/
97 static long GetFileSize (FILE* F)
98 /* Calculate the size of the file F, return -1 on error. */
101 long CurPos = ftell (F);
106 if (fseek (F, 0, SEEK_END) != 0) {
115 if (fseek (F, CurPos, SEEK_SET) != 0) {
124 static long GetFileTime (const char* Name)
125 /* Get the time of last modification for the given file. Return -1 on errors. */
128 if (stat (Name, &Buf) != 0) {
132 return (long) Buf.st_mtime;
137 /*****************************************************************************/
139 /*****************************************************************************/
143 static IFile* NewIFile (const char* Name)
144 /* Create and return a new IFile */
146 /* Get the length of the name */
147 unsigned Len = strlen (Name);
149 /* Allocate a IFile structure */
150 IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
152 /* Initialize the fields */
153 IF->Index = CollCount (&IFiles) + 1;
157 memcpy (IF->Name, Name, Len+1);
159 /* Insert the new structure into the IFile collection */
160 CollAppend (&IFiles, IF);
162 /* Return the new struct */
168 /*****************************************************************************/
170 /*****************************************************************************/
174 static AFile* NewAFile (IFile* IF, FILE* F)
175 /* Create and return a new AFile */
177 /* Allocate a AFile structure */
178 AFile* AF = (AFile*) xmalloc (sizeof (AFile));
180 /* Initialize the fields */
185 /* Increment the usage counter of the corresponding IFile. If this
186 * is the first use, set the file data and output debug info if
189 if (IF->Usage++ == 0) {
193 /* Get the file size */
194 Val = GetFileSize (AF->F);
196 Fatal ("Cannot seek on `%s': %s", IF->Name, strerror (errno));
200 /* Get the file modification time */
201 Val = GetFileTime (IF->Name);
203 Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno));
207 /* Set the debug data */
208 g_fileinfo (IF->Name, IF->Size, IF->MTime);
211 /* Insert the new structure into the AFile collection */
212 CollAppend (&AFiles, AF);
214 /* Return the new struct */
220 static void FreeAFile (AFile* AF)
221 /* Free an AFile structure */
228 /*****************************************************************************/
230 /*****************************************************************************/
234 static IFile* FindFile (const char* Name)
235 /* Find the file with the given name in the list of all files. Since the list
236 * is not large (usually less than 10), I don't care about using hashes or
237 * similar things and do a linear search.
241 for (I = 0; I < CollCount (&IFiles); ++I) {
242 /* Get the file struct */
243 IFile* IF = (IFile*) CollAt (&IFiles, I);
245 if (strcmp (Name, IF->Name) == 0) {
246 /* Found, return the struct */
257 void OpenMainFile (const char* Name)
258 /* Open the main file. Will call Fatal() in case of failures. */
260 /* Setup a new IFile structure for the main file */
261 IFile* IF = NewIFile (Name);
263 /* Open the file for reading */
264 FILE* F = fopen (Name, "r");
267 Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
270 /* Allocate a new AFile structure for the file */
271 (void) NewAFile (IF, F);
276 void OpenIncludeFile (const char* Name, unsigned DirSpec)
277 /* Open an include file and insert it into the tables. */
283 /* Check for the maximum include nesting */
284 if (CollCount (&AFiles) > MAX_INC_NESTING) {
285 PPError ("Include nesting too deep");
289 /* Search for the file */
290 N = FindInclude (Name, DirSpec);
292 PPError ("Include file `%s' not found", Name);
296 /* Search the list of all input files for this file. If we don't find
297 * it, create a new IFile object.
304 /* We don't need N any longer, since we may now use IF->Name */
308 F = fopen (IF->Name, "r");
310 /* Error opening the file */
311 PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno));
315 /* Allocate a new AFile structure */
316 (void) NewAFile (IF, F);
321 static void CloseIncludeFile (void)
322 /* Close an include file and switch to the higher level file. Set Input to
323 * NULL if this was the main file.
328 /* Get the number of active input files */
329 unsigned AFileCount = CollCount (&AFiles);
331 /* Must have an input file when called */
332 PRECONDITION (AFileCount > 0);
334 /* Get the current active input file */
335 Input = (AFile*) CollLast (&AFiles);
337 /* Close the current input file (we're just reading so no error check) */
340 /* Delete the last active file from the active file collection */
341 CollDelete (&AFiles, AFileCount-1);
343 /* Delete the active file structure */
349 void ClearLine (void)
350 /* Clear the current input line */
360 void InitLine (const char* Buf)
361 /* Initialize lptr from Buf and read CurC and NextC from the new input line */
375 /* Read the next character from the input stream and make CurC and NextC
376 * valid. If end of line is reached, both are set to NUL, no more lines
377 * are read by this function.
380 if (lptr[0] != '\0') {
396 /* Get a line from the current input. Returns 0 on end of file. */
407 /* If there is no file open, bail out, otherwise get the current input file */
408 if (CollCount (&AFiles) == 0) {
411 Input = (AFile*) CollLast (&AFiles);
413 /* Read lines until we get one with real contents */
416 while (!Done && Len < LINESIZE) {
418 while (fgets (line + Len, LINESIZE - Len, Input->F) == 0) {
423 /* Leave the current file */
426 /* If there is no file open, bail out, otherwise get the
429 if (CollCount (&AFiles) == 0) {
432 Input = (AFile*) CollLast (&AFiles);
436 /* We got a new line */
439 /* Remove the trailing newline if we have one */
440 Part = strlen (line + Len);
443 while (Len > 0 && line [Len-1] == '\n') {
448 /* Check if we have a line continuation character at the end. If not,
451 if (Len > 0 && line[Len-1] == '\\') {
452 line[Len-1] = '\n'; /* Replace by newline */
458 /* Got a line. Initialize the current and next characters. */
461 /* Create line information for this line */
462 UpdateLineInfo (Input->Input, Input->Line, line);
470 const char* GetCurrentFile (void)
471 /* Return the name of the current input file */
473 unsigned AFileCount = CollCount (&AFiles);
474 if (AFileCount > 0) {
475 const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
476 return AF->Input->Name;
478 /* No open file. Use the main file if we have one. */
479 unsigned IFileCount = CollCount (&IFiles);
480 if (IFileCount > 0) {
481 const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
484 return "(outside file scope)";
491 unsigned GetCurrentLine (void)
492 /* Return the line number in the current input file */
494 unsigned AFileCount = CollCount (&AFiles);
495 if (AFileCount > 0) {
496 const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
506 void WriteDependencies (FILE* F, const char* OutputFile)
507 /* Write a makefile dependency list to the given file */
511 /* Get the number of input files */
512 unsigned IFileCount = CollCount (&IFiles);
514 /* Print the output file followed by a tab char */
515 fprintf (F, "%s:\t", OutputFile);
517 /* Loop over all files */
518 for (I = 0; I < IFileCount; ++I) {
519 /* Get the next input file */
520 const IFile* IF = (const IFile*) CollAt (&IFiles, I);
521 /* If this is not the first file, add a space */
522 const char* Format = (I == 0)? "%s" : " %s";
523 /* Print the dependency */
524 fprintf (F, Format, IF->Name);