#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
-#include "../common/xmalloc.h"
+/* common */
+#include "check.h"
+#include "coll.h"
+#include "xmalloc.h"
+/* cc65 */
#include "asmcode.h"
-#include "check.h"
+#include "codegen.h"
#include "error.h"
-#include "global.h"
#include "incpath.h"
-#include "io.h"
+#include "lineinfo.h"
#include "input.h"
/*****************************************************************************/
-/* Data */
+/* Data */
/*****************************************************************************/
+/* Input line stuff */
+static char LineBuf [LINESIZE];
+char* line = LineBuf;
+const char* lptr = LineBuf;
+
+/* Current and next input character */
+char CurC = '\0';
+char NextC = '\0';
+
/* Maximum count of nested includes */
#define MAX_INC_NESTING 16
-/* Struct that describes an input file */
-typedef struct IFile IFile;
-struct IFile {
- IFile* Next; /* Next file in single linked list */
- IFile* Active; /* Next file in list of active includes */
- unsigned Index; /* File index */
- unsigned Line; /* Line number for this file */
- FILE* F; /* Input file stream */
- char Name[1]; /* Name of file (dynamically allocated) */
+/* Struct that describes an active input file */
+typedef struct AFile AFile;
+struct AFile {
+ unsigned Line; /* Line number for this file */
+ FILE* F; /* Input file stream */
+ IFile* Input; /* Points to corresponding IFile */
};
-/* List of input files */
-static unsigned IFileTotal = 0; /* Total number of files */
-static IFile* IFileList = 0; /* Single linked list of all files */
-static unsigned IFileCount = 0; /* Number of active input files */
-static IFile* Input = 0; /* Single linked list of active files */
+/* List of all input files */
+static Collection IFiles = STATIC_COLLECTION_INITIALIZER;
+
+/* List of all active files */
+static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
/*****************************************************************************/
-/* struct IFile */
+/* struct IFile */
/*****************************************************************************/
-static IFile* NewIFile (const char* Name, FILE* F)
+static IFile* NewIFile (const char* Name)
/* Create and return a new IFile */
{
/* Get the length of the name */
unsigned Len = strlen (Name);
/* Allocate a IFile structure */
- IFile* IF = xmalloc (sizeof (IFile) + Len);
+ IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len);
/* Initialize the fields */
- IF->Index = ++IFileTotal;
- IF->Line = 0;
- IF->F = F;
+ IF->Index = CollCount (&IFiles) + 1;
+ IF->Usage = 0;
+ IF->Size = 0;
+ IF->MTime = 0;
memcpy (IF->Name, Name, Len+1);
- /* Insert the structure into both lists */
- IF->Next = IFileList;
- IFileList = IF;
- IF->Active = Input;
- Input = IF;
- ++IFileCount;
+ /* Insert the new structure into the IFile collection */
+ CollAppend (&IFiles, IF);
/* Return the new struct */
return IF;
+/*****************************************************************************/
+/* struct AFile */
+/*****************************************************************************/
+
+
+
+static AFile* NewAFile (IFile* IF, FILE* F)
+/* Create and return a new AFile */
+{
+ /* Allocate a AFile structure */
+ AFile* AF = (AFile*) xmalloc (sizeof (AFile));
+
+ /* Initialize the fields */
+ AF->Line = 0;
+ AF->F = F;
+ AF->Input = IF;
+
+ /* Increment the usage counter of the corresponding IFile. If this
+ * is the first use, set the file data and output debug info if
+ * requested.
+ */
+ if (IF->Usage++ == 0) {
+
+ /* Get file size and modification time */
+ struct stat Buf;
+ if (fstat (fileno (F), &Buf) != 0) {
+ /* Error */
+ Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno));
+ }
+ IF->Size = (unsigned long) Buf.st_size;
+ IF->MTime = (unsigned long) Buf.st_mtime;
+
+ /* Set the debug data */
+ g_fileinfo (IF->Name, IF->Size, IF->MTime);
+ }
+
+ /* Insert the new structure into the AFile collection */
+ CollAppend (&AFiles, AF);
+
+ /* Return the new struct */
+ return AF;
+}
+
+
+
+static void FreeAFile (AFile* AF)
+/* Free an AFile structure */
+{
+ xfree (AF);
+}
+
+
+
/*****************************************************************************/
/* Code */
/*****************************************************************************/
+static IFile* FindFile (const char* Name)
+/* Find the file with the given name in the list of all files. Since the list
+ * is not large (usually less than 10), I don't care about using hashes or
+ * similar things and do a linear search.
+ */
+{
+ unsigned I;
+ for (I = 0; I < CollCount (&IFiles); ++I) {
+ /* Get the file struct */
+ IFile* IF = (IFile*) CollAt (&IFiles, I);
+ /* Check the name */
+ if (strcmp (Name, IF->Name) == 0) {
+ /* Found, return the struct */
+ return IF;
+ }
+ }
+
+ /* Not found */
+ return 0;
+}
+
+
+
void OpenMainFile (const char* Name)
/* Open the main file. Will call Fatal() in case of failures. */
{
+ /* Setup a new IFile structure for the main file */
+ IFile* IF = NewIFile (Name);
+
/* Open the file for reading */
FILE* F = fopen (Name, "r");
if (F == 0) {
- /* Cannot open */
- Fatal (FAT_CANNOT_OPEN_INPUT, strerror (errno));
+ /* Cannot open */
+ Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
}
- /* Setup a new IFile structure */
- NewIFile (Name, F);
+ /* Allocate a new AFile structure for the file */
+ (void) NewAFile (IF, F);
}
void OpenIncludeFile (const char* Name, unsigned DirSpec)
/* Open an include file and insert it into the tables. */
{
- char* N;
- FILE* F;
+ char* N;
+ FILE* F;
+ IFile* IF;
/* Check for the maximum include nesting */
- if (IFileCount > MAX_INC_NESTING) {
- PPError (ERR_INCLUDE_NESTING);
- return;
+ if (CollCount (&AFiles) > MAX_INC_NESTING) {
+ PPError ("Include nesting too deep");
+ return;
}
/* Search for the file */
N = FindInclude (Name, DirSpec);
if (N == 0) {
- PPError (ERR_INCLUDE_NOT_FOUND, Name);
+ PPError ("Include file `%s' not found", Name);
return;
}
+ /* Search the list of all input files for this file. If we don't find
+ * it, create a new IFile object.
+ */
+ IF = FindFile (N);
+ if (IF == 0) {
+ IF = NewIFile (N);
+ }
+
+ /* We don't need N any longer, since we may now use IF->Name */
+ xfree (N);
+
/* Open the file */
- F = fopen (N, "r");
+ F = fopen (IF->Name, "r");
if (F == 0) {
/* Error opening the file */
- PPError (ERR_INCLUDE_OPEN_FAILURE, N);
- xfree (N);
+ PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno));
return;
}
- /* Allocate a new IFile structure */
- NewIFile (N, F);
-
- /* We don't need the full name any longer */
- xfree (N);
+ /* Allocate a new AFile structure */
+ (void) NewAFile (IF, F);
}
* NULL if this was the main file.
*/
{
+ AFile* Input;
+
+ /* Get the number of active input files */
+ unsigned AFileCount = CollCount (&AFiles);
+
/* Must have an input file when called */
- PRECONDITION (Input != 0);
+ PRECONDITION (AFileCount > 0);
+
+ /* Get the current active input file */
+ Input = (AFile*) CollLast (&AFiles);
/* Close the current input file (we're just reading so no error check) */
fclose (Input->F);
- /* Make this file inactive and the last one active again */
- Input = Input->Active;
+ /* Delete the last active file from the active file collection */
+ CollDelete (&AFiles, AFileCount-1);
+
+ /* Delete the active file structure */
+ FreeAFile (Input);
+}
+
+
+
+void ClearLine (void)
+/* Clear the current input line */
+{
+ line[0] = '\0';
+ lptr = line;
+ CurC = '\0';
+ NextC = '\0';
+}
+
+
+
+void InitLine (const char* Buf)
+/* Initialize lptr from Buf and read CurC and NextC from the new input line */
+{
+ lptr = Buf;
+ CurC = lptr[0];
+ if (CurC != '\0') {
+ NextC = lptr[1];
+ } else {
+ NextC = '\0';
+ }
+}
+
+
+
+void NextChar (void)
+/* Read the next character from the input stream and make CurC and NextC
+ * valid. If end of line is reached, both are set to NUL, no more lines
+ * are read by this function.
+ */
+{
+ if (lptr[0] != '\0') {
+ ++lptr;
+ CurC = lptr[0];
+ if (CurC != '\0') {
+ NextC = lptr[1];
+ } else {
+ NextC = '\0';
+ }
+ } else {
+ CurC = NextC = '\0';
+ }
}
int NextLine (void)
/* Get a line from the current input. Returns 0 on end of file. */
{
+ AFile* Input;
unsigned Len;
unsigned Part;
unsigned Start;
int Done;
/* Setup the line */
- kill ();
+ ClearLine ();
- /* If there is no file open, bail out */
- if (Input == 0) {
+ /* If there is no file open, bail out, otherwise get the current input file */
+ if (CollCount (&AFiles) == 0) {
return 0;
}
+ Input = (AFile*) CollLast (&AFiles);
/* Read lines until we get one with real contents */
Len = 0;
while (fgets (line + Len, LINESIZE - Len, Input->F) == 0) {
- /* eof */
- kill ();
+ /* Assume EOF */
+ ClearLine ();
/* Leave the current file */
CloseIncludeFile ();
- /* If this was the last file, bail out */
- if (Input == 0) {
- return 0;
+ /* If there is no file open, bail out, otherwise get the
+ * current input file
+ */
+ if (CollCount (&AFiles) == 0) {
+ return 0;
}
+ Input = (AFile*) CollLast (&AFiles);
+
}
/* We got a new line */
while (Len > 0 && line [Len-1] == '\n') {
--Len;
}
- line [Len] = '\0';
-
- /* Output the source line in the generated assembler file
- * if requested.
- */
- if (AddSource && line[Start] != '\0') {
- AddCodeLine ("; %s", line+Start);
- }
+ line [Len] = '\0';
/* Check if we have a line continuation character at the end. If not,
* we're done.
*/
if (Len > 0 && line[Len-1] == '\\') {
line[Len-1] = '\n'; /* Replace by newline */
- } else {
- Done = 1;
- }
+ } else {
+ Done = 1;
+ }
}
- /* Got a line */
+ /* Got a line. Initialize the current and next characters. */
+ InitLine (line);
+
+ /* Create line information for this line */
+ UpdateLineInfo (Input->Input, Input->Line, line);
+
+ /* Done */
return 1;
}
const char* GetCurrentFile (void)
/* Return the name of the current input file */
{
- if (Input == 0) {
- return "(outside file scope)";
+ unsigned AFileCount = CollCount (&AFiles);
+ if (AFileCount > 0) {
+ const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
+ return AF->Input->Name;
} else {
- return Input->Name;
+ /* No open file. Use the main file if we have one. */
+ unsigned IFileCount = CollCount (&IFiles);
+ if (IFileCount > 0) {
+ const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
+ return IF->Name;
+ } else {
+ return "(outside file scope)";
+ }
}
}
unsigned GetCurrentLine (void)
/* Return the line number in the current input file */
{
- return Input? Input->Line : 0;
+ unsigned AFileCount = CollCount (&AFiles);
+ if (AFileCount > 0) {
+ const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
+ return AF->Line;
+ } else {
+ /* No open file */
+ return 0;
+ }
+}
+
+
+
+void WriteDependencies (FILE* F, const char* OutputFile)
+/* Write a makefile dependency list to the given file */
+{
+ unsigned I;
+
+ /* Get the number of input files */
+ unsigned IFileCount = CollCount (&IFiles);
+
+ /* Print the output file followed by a tab char */
+ fprintf (F, "%s:\t", OutputFile);
+
+ /* Loop over all files */
+ for (I = 0; I < IFileCount; ++I) {
+ /* Get the next input file */
+ const IFile* IF = (const IFile*) CollAt (&IFiles, I);
+ /* If this is not the first file, add a space */
+ const char* Format = (I == 0)? "%s" : " %s";
+ /* Print the dependency */
+ fprintf (F, Format, IF->Name);
+ }
+
+ /* End the line */
+ fprintf (F, "\n\n");
}