From bb9c98f4c9ce005b5becc7d47f5b38ccc9500e91 Mon Sep 17 00:00:00 2001
From: acqn <acqn163@outlook.com>
Date: Sun, 24 Jul 2022 23:19:05 +0800
Subject: [PATCH] Added support for the #line directive.

---
 src/cc65/input.c   | 40 ++++++++++++++++++--
 src/cc65/input.h   |  6 +++
 src/cc65/preproc.c | 93 +++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 134 insertions(+), 5 deletions(-)

diff --git a/src/cc65/input.c b/src/cc65/input.c
index 18441f5c8..c6037091f 100644
--- a/src/cc65/input.c
+++ b/src/cc65/input.c
@@ -92,6 +92,7 @@ struct AFile {
     FILE*       F;              /* Input file stream */
     IFile*      Input;          /* Points to corresponding IFile */
     int         SearchPath;     /* True if we've added a path for this file */
+    char*       PName;          /* Presumed name of the file */
     PPIfStack   IfStack;        /* PP #if stack */
     int         MissingNL;      /* Last input line was missing a newline */
 };
@@ -159,6 +160,7 @@ static AFile* NewAFile (IFile* IF, FILE* F)
     AF->Line  = 0;
     AF->F     = F;
     AF->Input = IF;
+    AF->PName = 0;
     AF->IfStack.Index = -1;
     AF->MissingNL = 0;
 
@@ -209,6 +211,9 @@ static AFile* NewAFile (IFile* IF, FILE* F)
 static void FreeAFile (AFile* AF)
 /* Free an AFile structure */
 {
+    if (AF->PName != 0) {
+        xfree (AF->PName);
+    }
     xfree (AF);
 }
 
@@ -599,8 +604,8 @@ const char* GetCurrentFile (void)
 {
     unsigned AFileCount = CollCount (&AFiles);
     if (AFileCount > 0) {
-        const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
-        return AF->Input->Name;
+        const AFile* AF = CollLast (&AFiles);
+        return AF->PName == 0 ? AF->Input->Name : AF->PName;
     } else {
         /* No open file */
         return "(outside file scope)";
@@ -614,7 +619,7 @@ unsigned GetCurrentLine (void)
 {
     unsigned AFileCount = CollCount (&AFiles);
     if (AFileCount > 0) {
-        const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
+        const AFile* AF = CollLast (&AFiles);
         return AF->Line;
     } else {
         /* No open file */
@@ -624,6 +629,35 @@ unsigned GetCurrentLine (void)
 
 
 
+void SetCurrentLine (unsigned LineNum)
+/* Set the line number in the current input file */
+{
+    unsigned AFileCount = CollCount (&AFiles);
+    if (AFileCount > 0) {
+        AFile* AF = CollLast (&AFiles);
+        AF->Line = LineNum;
+    }
+}
+
+
+
+void SetCurrentFilename (const char* Name)
+/* Set the presumed name of the current input file */
+{
+    unsigned AFileCount = CollCount (&AFiles);
+    if (AFileCount > 0) {
+        size_t Len = strlen (Name);
+        AFile* AF = CollLast (&AFiles);
+        if (AF->PName != 0) {
+            xfree (AF->PName);
+        }
+        AF->PName = xmalloc (Len + 1);
+        memcpy (AF->PName, Name, Len + 1);
+    }
+}
+
+
+
 static void WriteEscaped (FILE* F, const char* Name)
 /* Write a file name to a dependency file escaping spaces */
 {
diff --git a/src/cc65/input.h b/src/cc65/input.h
index 779217b9b..cf529e169 100644
--- a/src/cc65/input.h
+++ b/src/cc65/input.h
@@ -122,6 +122,12 @@ const char* GetCurrentFile (void);
 unsigned GetCurrentLine (void);
 /* Return the line number in the current input file */
 
+void SetCurrentLine (unsigned LineNum);
+/* Set the line number in the current input file */
+
+void SetCurrentFilename (const char* Name);
+/* Set the presumed name of the current input file */
+
 void CreateDependencies (void);
 /* Create dependency files requested by the user */
 
diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c
index 6dcbeea70..3b73ee493 100644
--- a/src/cc65/preproc.c
+++ b/src/cc65/preproc.c
@@ -57,6 +57,7 @@
 #include "ppexpr.h"
 #include "preproc.h"
 #include "scanner.h"
+#include "scanstrbuf.h"
 #include "standard.h"
 
 
@@ -1294,6 +1295,95 @@ Done:
 
 
 
+static unsigned GetLineDirectiveNum (void)
+/* Get a decimal digit-sequence from the input. Return 0 on errors. */
+{
+    unsigned long Num = 0;
+    StrBuf Buf = AUTO_STRBUF_INITIALIZER;
+
+    /* The only non-decimal-numeric character allowed in the digit-sequence is
+    ** the digit separator '\'' as of C23, but we haven't supported it yet.
+    */
+    SkipWhitespace (0);
+    while (IsDigit (CurC))
+    {
+        SB_AppendChar (&Buf, CurC);
+        NextChar ();
+    }
+
+    /* Ensure the buffer is terminated with a '\0' */
+    SB_Terminate (&Buf);
+    if (SkipWhitespace (0) != 0 || CurC == '\0') {
+        const char* Str = SB_GetConstBuf (&Buf);
+        if (Str[0] == '\0') {
+            PPWarning ("#line directive interprets number as decimal, not octal");
+        } else {
+            Num = strtoul (Str, 0, 10);
+            if (Num > 2147483647) {
+                PPError ("#line directive requires an integer argument not greater than 2147483647");
+                ClearLine ();
+                Num = 0;
+            } else if (Num == 0) {
+                PPError ("#line directive requires a positive integer argument");
+                ClearLine ();
+            }
+        }
+    } else {
+        PPError ("#line directive requires a simple decimal digit sequence");
+        ClearLine ();
+    }
+
+    /* Done with the buffer */
+    SB_Done (&Buf);
+
+    return (unsigned)Num;
+}
+
+
+
+static void DoLine (void)
+/* Process #line directive */
+{
+    unsigned LineNum;
+
+    /* Macro-replace a single line with support for the "defined" operator */
+    SB_Clear (MLine);
+    PreprocessDirective (Line, MLine, MSM_NONE);
+
+    /* Read from the processed line */
+    SB_Reset (MLine);
+    MLine = InitLine (MLine);
+
+    /* Parse and check the specified line number */
+    LineNum = GetLineDirectiveNum ();
+    if (LineNum != 0) {
+        /* Parse and check the optional filename argument */
+        if (SB_GetIndex (Line) < SB_GetLen (Line)) {
+            StrBuf Filename = AUTO_STRBUF_INITIALIZER;
+            if (SB_GetString (Line, &Filename)) {
+                SB_Terminate (&Filename);
+                SetCurrentFilename (SB_GetConstBuf (&Filename));
+            } else {
+                PPError ("Invalid filename for #line directive");
+                LineNum = 0;
+            }
+            SB_Done (&Filename);
+        }
+
+        /* #line actually sets the line number of the next line */
+        if (LineNum > 0) {
+            SetCurrentLine (LineNum - 1);
+            /* Check for extra tokens at the end */
+            CheckExtraTokens ("line");
+        }
+    }
+
+    /* Restore input source */
+    MLine = InitLine (MLine);
+}
+
+
+
 static void DoPragma (void)
 /* Handle a #pragma line by converting the #pragma preprocessor directive into
 ** the _Pragma() compiler operator.
@@ -1476,9 +1566,8 @@ static int ParseDirectives (unsigned ModeFlags)
                         break;
 
                     case PPD_LINE:
-                        /* Should do something in C99 at least, but we ignore it */
                         if (!PPSkip) {
-                            ClearLine ();
+                            DoLine ();
                         }
                         break;