Colorize diagnostics.

This commit is contained in:
Kugel Fuhr
2025-07-07 16:47:36 +02:00
parent ffcff0fa61
commit b1eb1bf6ab
6 changed files with 473 additions and 34 deletions

View File

@@ -106,6 +106,7 @@ Short options:
Long options: Long options:
--auto-import Mark unresolved symbols as import --auto-import Mark unresolved symbols as import
--bin-include-dir dir Set a search path for binary includes --bin-include-dir dir Set a search path for binary includes
--color [on|auto|off] Color diagnostics (default: auto)
--cpu type Set cpu type --cpu type Set cpu type
--create-dep name Create a make dependency file --create-dep name Create a make dependency file
--create-full-dep name Create a full make dependency file --create-full-dep name Create a full make dependency file
@@ -120,7 +121,7 @@ Long options:
--list-bytes n Maximum number of bytes per listing line --list-bytes n Maximum number of bytes per listing line
--memory-model model Set the memory model --memory-model model Set the memory model
--pagelength n Set the page length for the listing --pagelength n Set the page length for the listing
--relax-checks Disables some error checks --relax-checks Relax some checks (see docs)
--smart Enable smart mode --smart Enable smart mode
--target sys Set the target system --target sys Set the target system
--verbose Increase verbosity --verbose Increase verbosity
@@ -146,6 +147,14 @@ Here is a description of all the command line options:
name="search paths">. name="search paths">.
<label id="option-color">
<tag><tt>--color</tt></tag>
This option controls if the assembler will use colors when printing
diagnostics. The default is "auto" which will enable colors if the output
goes to a terminal (not to a file).
<label id="option--cpu"> <label id="option--cpu">
<tag><tt>--cpu type</tt></tag> <tag><tt>--cpu type</tt></tag>
@@ -186,12 +195,6 @@ Here is a description of all the command line options:
information to the assembler. information to the assembler.
<tag><tt>-d, --debug</tt></tag>
Enables debug mode, something that should not be needed for mere
mortals:-)
<label id="option--feature"> <label id="option--feature">
<tag><tt>--feature name</tt></tag> <tag><tt>--feature name</tt></tag>

View File

@@ -38,6 +38,8 @@
#include <stdarg.h> #include <stdarg.h>
/* common */ /* common */
#include "cmdline.h"
#include "consprop.h"
#include "strbuf.h" #include "strbuf.h"
/* ca65 */ /* ca65 */
@@ -45,6 +47,7 @@
#include "filetab.h" #include "filetab.h"
#include "lineinfo.h" #include "lineinfo.h"
#include "nexttok.h" #include "nexttok.h"
#include "spool.h"
@@ -64,6 +67,14 @@ unsigned WarningCount = 0;
/* Maximum number of additional notifications */ /* Maximum number of additional notifications */
#define MAX_NOTES 8 #define MAX_NOTES 8
/* Diagnostic category */
typedef enum { DC_NOTE, DC_WARN, DC_ERR, DC_FATAL, DC_COUNT } DiagCat;
/* Descriptions for diagnostic categories */
const char* DiagCatDesc[DC_COUNT] = {
"Note", "Warning", "Error", "Fatal error"
};
/*****************************************************************************/ /*****************************************************************************/
@@ -72,27 +83,53 @@ unsigned WarningCount = 0;
static void VPrintMsg (const FilePos* Pos, const char* Desc, static void VPrintMsg (const FilePos* Pos, DiagCat Cat, const char* Format,
const char* Format, va_list ap) va_list ap)
/* Format and output an error/warning message. */ /* Format and output an error/warning message. */
{ {
StrBuf S = STATIC_STRBUF_INITIALIZER; StrBuf S = AUTO_STRBUF_INITIALIZER;
StrBuf Msg = AUTO_STRBUF_INITIALIZER;
StrBuf Loc = AUTO_STRBUF_INITIALIZER;
/* Determine the description for the category and its color */
const char* Desc = DiagCatDesc[Cat];
const char* Color;
switch (Cat) {
case DC_NOTE: Color = CP_Cyan (); break;
case DC_WARN: Color = CP_Yellow (); break;
case DC_ERR: Color = CP_BrightRed (); break;
case DC_FATAL: Color = CP_BrightRed (); break;
default: FAIL ("Unexpected Cat value"); break;
}
/* Format the actual message */ /* Format the actual message */
StrBuf Msg = STATIC_STRBUF_INITIALIZER;
SB_VPrintf (&Msg, Format, ap); SB_VPrintf (&Msg, Format, ap);
SB_Terminate (&Msg); SB_Terminate (&Msg);
/* Format the message header */ /* Format the location. If the file position is valid, we use the file
SB_Printf (&S, "%s:%u: %s: ", ** position, otherwise the program name. This allows to print fatal
SB_GetConstBuf (GetFileName (Pos->Name)), ** errors in the startup phase.
Pos->Line, */
Desc); if (Pos->Name == EMPTY_STRING_ID) {
SB_CopyStr (&Loc, ProgName);
} else {
SB_Printf (&Loc, "%s:%u", SB_GetConstBuf (GetFileName (Pos->Name)),
Pos->Line);
}
SB_Terminate (&Loc);
/* Append the message to the message header */ /* Format the full message */
SB_Append (&S, &Msg); SB_Printf (&S, "%s%s: %s%s:%s %s%s",
CP_White (),
SB_GetConstBuf (&Loc),
Color,
Desc,
CP_White (),
SB_GetConstBuf (&Msg),
CP_Reset ());
/* Delete the formatted message */ /* Delete the formatted message and the location string */
SB_Done (&Loc);
SB_Done (&Msg); SB_Done (&Msg);
/* Add a new line and terminate the generated full message */ /* Add a new line and terminate the generated full message */
@@ -183,7 +220,7 @@ void PNotification (const FilePos* Pos, const char* Format, ...)
/* Output the message */ /* Output the message */
va_list ap; va_list ap;
va_start (ap, Format); va_start (ap, Format);
VPrintMsg (Pos, "Note", Format, ap); VPrintMsg (Pos, DC_NOTE, Format, ap);
va_end (ap); va_end (ap);
} }
@@ -202,7 +239,7 @@ static void WarningMsg (const Collection* LineInfos, const char* Format, va_list
const LineInfo* LI = CollConstAt (LineInfos, 0); const LineInfo* LI = CollConstAt (LineInfos, 0);
/* Output a warning for this position */ /* Output a warning for this position */
VPrintMsg (GetSourcePos (LI), "Warning", Format, ap); VPrintMsg (GetSourcePos (LI), DC_WARN, Format, ap);
/* Add additional notifications if necessary */ /* Add additional notifications if necessary */
AddNotifications (LineInfos); AddNotifications (LineInfos);
@@ -243,7 +280,7 @@ void PWarning (const FilePos* Pos, unsigned Level, const char* Format, ...)
if (Level <= WarnLevel) { if (Level <= WarnLevel) {
va_list ap; va_list ap;
va_start (ap, Format); va_start (ap, Format);
VPrintMsg (Pos, "Warning", Format, ap); VPrintMsg (Pos, DC_WARN, Format, ap);
va_end (ap); va_end (ap);
/* Count warnings */ /* Count warnings */
@@ -280,7 +317,7 @@ void ErrorMsg (const Collection* LineInfos, const char* Format, va_list ap)
const LineInfo* LI = CollConstAt (LineInfos, 0); const LineInfo* LI = CollConstAt (LineInfos, 0);
/* Output an error for this position */ /* Output an error for this position */
VPrintMsg (GetSourcePos (LI), "Error", Format, ap); VPrintMsg (GetSourcePos (LI), DC_ERR, Format, ap);
/* Add additional notifications if necessary */ /* Add additional notifications if necessary */
AddNotifications (LineInfos); AddNotifications (LineInfos);
@@ -317,7 +354,7 @@ void PError (const FilePos* Pos, const char* Format, ...)
{ {
va_list ap; va_list ap;
va_start (ap, Format); va_start (ap, Format);
VPrintMsg (Pos, "Error", Format, ap); VPrintMsg (Pos, DC_ERR, Format, ap);
va_end (ap); va_end (ap);
/* Count errors */ /* Count errors */
@@ -371,18 +408,12 @@ void ErrorSkip (const char* Format, ...)
void Fatal (const char* Format, ...) void Fatal (const char* Format, ...)
/* Print a message about a fatal error and die */ /* Print a message about a fatal error and die */
{ {
/* Output the message ... */
va_list ap; va_list ap;
StrBuf S = STATIC_STRBUF_INITIALIZER;
va_start (ap, Format); va_start (ap, Format);
SB_VPrintf (&S, Format, ap); VPrintMsg (&CurTok.Pos, DC_FATAL, Format, ap);
SB_Terminate (&S);
va_end (ap); va_end (ap);
fprintf (stderr, "Fatal error: %s\n", SB_GetConstBuf (&S));
SB_Done (&S);
/* And die... */ /* And die... */
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }

View File

@@ -42,6 +42,7 @@
#include "addrsize.h" #include "addrsize.h"
#include "chartype.h" #include "chartype.h"
#include "cmdline.h" #include "cmdline.h"
#include "consprop.h"
#include "debugflag.h" #include "debugflag.h"
#include "mmodel.h" #include "mmodel.h"
#include "print.h" #include "print.h"
@@ -111,6 +112,7 @@ static void Usage (void)
"Long options:\n" "Long options:\n"
" --auto-import\t\t\tMark unresolved symbols as import\n" " --auto-import\t\t\tMark unresolved symbols as import\n"
" --bin-include-dir dir\t\tSet a search path for binary includes\n" " --bin-include-dir dir\t\tSet a search path for binary includes\n"
" --color [on|auto|off]\t\tColor diagnostics (default: auto)\n"
" --cpu type\t\t\tSet cpu type\n" " --cpu type\t\t\tSet cpu type\n"
" --create-dep name\t\tCreate a make dependency file\n" " --create-dep name\t\tCreate a make dependency file\n"
" --create-full-dep name\tCreate a full make dependency file\n" " --create-full-dep name\tCreate a full make dependency file\n"
@@ -129,7 +131,8 @@ static void Usage (void)
" --smart\t\t\tEnable smart mode\n" " --smart\t\t\tEnable smart mode\n"
" --target sys\t\t\tSet the target system\n" " --target sys\t\t\tSet the target system\n"
" --verbose\t\t\tIncrease verbosity\n" " --verbose\t\t\tIncrease verbosity\n"
" --version\t\t\tPrint the assembler version\n", " --version\t\t\tPrint the assembler version\n"
" --warnings-as-errors\t\tTreat warnings as errors\n",
ProgName); ProgName);
} }
@@ -492,6 +495,22 @@ static void OptBinIncludeDir (const char* Opt attribute ((unused)), const char*
static void OptColor(const char* Opt, const char* Arg)
/* Handle the --color option */
{
if (strcmp (Arg, "off") == 0 || strcmp (Arg, "false") == 0) {
CP_SetColorMode (CM_OFF);
} else if (strcmp (Arg, "auto") == 0) {
CP_SetColorMode (CM_AUTO);
} else if (strcmp (Arg, "on") == 0 || strcmp (Arg, "true") == 0) {
CP_SetColorMode (CM_ON);
} else {
AbEnd ("Invalid argument to %s: %s", Opt, Arg);
}
}
static void OptCPU (const char* Opt attribute ((unused)), const char* Arg) static void OptCPU (const char* Opt attribute ((unused)), const char* Arg)
/* Handle the --cpu option */ /* Handle the --cpu option */
{ {
@@ -1013,6 +1032,7 @@ int main (int argc, char* argv [])
static const LongOpt OptTab[] = { static const LongOpt OptTab[] = {
{ "--auto-import", 0, OptAutoImport }, { "--auto-import", 0, OptAutoImport },
{ "--bin-include-dir", 1, OptBinIncludeDir }, { "--bin-include-dir", 1, OptBinIncludeDir },
{ "--color", 1, OptColor },
{ "--cpu", 1, OptCPU }, { "--cpu", 1, OptCPU },
{ "--create-dep", 1, OptCreateDep }, { "--create-dep", 1, OptCreateDep },
{ "--create-full-dep", 1, OptCreateFullDep }, { "--create-full-dep", 1, OptCreateFullDep },
@@ -1040,6 +1060,9 @@ int main (int argc, char* argv [])
unsigned I; unsigned I;
/* Initialize console output */
CP_Init ();
/* Initialize the cmdline module */ /* Initialize the cmdline module */
InitCmdLine (&argc, &argv, "ca65"); InitCmdLine (&argc, &argv, "ca65");

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 2026, Kugelfuhr */ /* (C) 2025, Kugelfuhr */
/* */ /* */
/* */ /* */
/* This software is provided 'as-is', without any expressed or implied */ /* This software is provided 'as-is', without any expressed or implied */

208
src/common/consprop.c Normal file
View File

@@ -0,0 +1,208 @@
/*****************************************************************************/
/* */
/* consprop.c */
/* */
/* Console properties */
/* */
/* */
/* */
/* (C) 2025, Kugelfuhr */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdlib.h>
#include <unistd.h>
#ifdef _WIN32
#include <windows.h>
#define isatty _isatty
#endif
/* common */
#include "consprop.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
#ifdef _WIN32
static unsigned CodePage; /* Windows code page on startup */
#endif
/* State variables */
static int IsTTY = 1; /* True if console is a tty */
static int IsUTF8 = 1; /* True if console is in UTF-8 mode */
static int Color = 0; /* True if we should use color */
static ColorMode CMode = CM_AUTO; /* Current color mode */
/* ANSI escape sequences for color */
const char CP_ColorSeq[CC_COUNT][2][8] = {
{ "", "\x1b[30m", }, /* Black */
{ "", "\x1b[31m", }, /* Red */
{ "", "\x1b[32m", }, /* Green */
{ "", "\x1b[33m", }, /* Brown */
{ "", "\x1b[34m", }, /* Blue */
{ "", "\x1b[35m", }, /* Magenta */
{ "", "\x1b[36m", }, /* Cyan */
{ "", "\x1b[37m", }, /* LightGray */
{ "", "\x1b[90m", }, /* Gray */
{ "", "\x1b[91m", }, /* BrightRed */
{ "", "\x1b[92m", }, /* BrightGreen */
{ "", "\x1b[93m", }, /* Yellow */
{ "", "\x1b[94m", }, /* BrightBlue */
{ "", "\x1b[95m", }, /* BrightMagenta */
{ "", "\x1b[96m", }, /* BrightCyan */
{ "", "\x1b[97m", }, /* White */
};
/* Box drawing sequences */
const char CP_BoxDrawingSeq[BD_COUNT][2][4] = {
{ "-", "\xE2\x94\x80", }, /* "─" - Horizontal */
{ "|", "\xE2\x94\x82", }, /* "│" - Vertical */
{ "+", "\xE2\x94\x8C", }, /* "┌" - ULCorner */
{ "+", "\xE2\x94\x94", }, /* "└" - LLCorner */
{ "+", "\xE2\x94\x90", }, /* "┐" - URCorner */
{ "+", "\xE2\x94\x98", }, /* "┘" - LRCorner */
{ "+", "\xE2\x94\x9C", }, /* "├" - VerticalRight */
{ "+", "\xE2\x94\xA4", }, /* "┤" - VerticalLeft */
{ "+", "\xE2\x94\xAC", }, /* "┬" - HorizontalDown */
{ "+", "\xE2\x94\xB4", }, /* "┴" - HorizontalUp */
};
/*****************************************************************************/
/* Helpers */
/*****************************************************************************/
#ifdef _WIN32
static void Cleanup()
/* Cleanup on program exit */
{
SetConsoleOutputCP (CodePage);
}
#endif
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void CP_Init (void)
/* Init console properties. Must be called before using any other function or
** data from this module.
*/
{
IsTTY = (isatty (STDOUT_FILENO) && isatty (STDERR_FILENO));
CP_SetColorMode (CMode);
#ifdef _WIN32
if (IsTTY) {
CodePage = GetConsoleOutputCP ();
if (SetConsoleOutputCP (CP_UTF8)) {
IsUTF8 = 1;
atexit (Cleanup);
}
if (Color) {
HANDLE StdOut = GetStdHandle (STD_OUTPUT_HANDLE);
if (StdOut != INVALID_HANDLE_VALUE) {
DWORD Mode;
if (GetConsoleMode (StdOut, &Mode)) {
Mode |= ENABLE_PROCESSED_OUTPUT |
ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode (StdOut, Mode);
}
}
}
}
#endif
}
int CP_IsTTY (void)
/* Return true if console output goes to a tty */
{
return IsTTY;
}
int CP_IsUTF8 (void)
/* Return true if the console supports UTF-8 */
{
return IsUTF8;
}
int CP_HasColor (void)
/* Return true if the console supports color and it should be used */
{
return Color;
}
ColorMode CP_GetColorMode (void)
/* Return the current color mode */
{
return CMode;
}
void CP_SetColorMode (ColorMode M)
/* Set the color mode */
{
CMode = M;
switch (CMode) {
case CM_AUTO: Color = IsTTY; break;
case CM_ON: Color = 1; break;
default: Color = 0; break;
}
}
void CP_DisableUTF8 (void)
/* Disable UTF-8 support */
{
IsUTF8 = 0;
}
void CP_EnableUTF8 (void)
/* Enable UTF-8 support */
{
IsUTF8 = 1;
}

174
src/common/consprop.h Normal file
View File

@@ -0,0 +1,174 @@
/*****************************************************************************/
/* */
/* consprop.h */
/* */
/* Console properties */
/* */
/* */
/* */
/* (C) 2025, Kugelfuhr */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#ifndef CONSPROP_H
#define CONSPROP_H
/* common */
#include "check.h"
#include "consprop.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Color mode for the program */
enum ColorMode { CM_OFF, CM_AUTO, CM_ON };
typedef enum ColorMode ColorMode;
/* Colors */
enum ConsoleColor {
CC_BLACK, CC_RED, CC_GREEN, CC_BROWN,
CC_BLUE, CC_MAGENTA, CC_CYAN, CC_LIGHTGRAY,
CC_GRAY, CC_BRIGHTRED, CC_BRIGHTGREEN, CC_YELLOW,
CC_BRIGHTBLUE, CC_BRIGHTMAGENTA, CC_BRIGHTCYAN, CC_WHITE,
CC_COUNT, /* Number of colors */
};
typedef enum ConsoleColor ConsoleColor;
/* Box drawing characters */
enum BoxDrawing {
BD_HORIZONTAL,
BD_VERTICAL,
BD_ULCORNER,
BD_LLCORNER,
BD_URCORNER,
BD_LRCORNER,
BD_RVERTICAL,
BD_LVERTICAL,
BD_DHORIZONTAL,
BD_UHORIZONTAL,
BD_COUNT, /* Number of available box drawing chars */
};
typedef enum BoxDrawing BoxDrawing;
/* ANSI escape sequences for color - don't use directly */
extern const char CP_ColorSeq[CC_COUNT][2][8];
/* Box drawing characters - don't use directly */
extern const char CP_BoxDrawingSeq[BD_COUNT][2][4];
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void CP_Init (void);
/* Init console properties. Must be called before using any other function or
** data from this module.
**/
int CP_IsTTY (void);
/* Return true if console output goes to a tty */
int CP_IsUTF8 (void);
/* Return true if the console supports UTF-8 */
int CP_HasColor (void);
/* Return true if the console supports color and it should be used */
ColorMode CP_GetColorMode (void);
/* Return the current color mode */
void CP_SetColorMode (ColorMode M);
/* Set the color mode */
void CP_DisableUTF8 (void);
/* Disable UTF-8 support */
void CP_EnableUTF8 (void);
/* Enable UTF-8 support */
static inline const char* CP_Color (ConsoleColor C)
/* Return the ANSI escape sequence for a specific color or an empty string */
{
PRECONDITION (C >= 0 && C < CC_COUNT);
return CP_ColorSeq[C][CP_HasColor ()];
}
static inline const char* CP_Reset (void)
/* Return the ANSI escape sequence to reset the colors or an empty string */
{
return CP_HasColor () ? "\x1b[0m" : "";
}
/* Return specific colors */
static inline const char* CP_Black (void) { return CP_Color (CC_BLACK); }
static inline const char* CP_Red (void) { return CP_Color (CC_RED); }
static inline const char* CP_Green (void) { return CP_Color (CC_GREEN); }
static inline const char* CP_Brown (void) { return CP_Color (CC_BROWN); }
static inline const char* CP_Blue (void) { return CP_Color (CC_BLUE); }
static inline const char* CP_Magenta (void) { return CP_Color (CC_MAGENTA); }
static inline const char* CP_Cyan (void) { return CP_Color (CC_CYAN); }
static inline const char* CP_LightGray (void) { return CP_Color (CC_LIGHTGRAY); }
static inline const char* CP_Gray (void) { return CP_Color (CC_GRAY); }
static inline const char* CP_BrightRed (void) { return CP_Color (CC_BRIGHTRED); }
static inline const char* CP_BrightGreen (void) { return CP_Color (CC_BRIGHTGREEN); }
static inline const char* CP_Yellow (void) { return CP_Color (CC_YELLOW); }
static inline const char* CP_BrightBlue (void) { return CP_Color (CC_BRIGHTBLUE); }
static inline const char* CP_BrightMagenta (void) { return CP_Color (CC_BRIGHTMAGENTA); }
static inline const char* CP_BrightCyan (void) { return CP_Color (CC_BRIGHTCYAN); }
static inline const char* CP_White (void) { return CP_Color (CC_WHITE); }
static inline const char* CP_BoxDrawing (BoxDrawing B)
/* Return the UTF-8 sequence for the box drawing characters */
{
PRECONDITION (B >= 0 && B < BD_COUNT);
return CP_BoxDrawingSeq[B][CP_IsUTF8 ()];
}
/* Return specific box drawing character sequences */
static inline const char* CP_Horizontal (void) { return CP_BoxDrawing (BD_HORIZONTAL); }
static inline const char* CP_Vertical (void) { return CP_BoxDrawing (BD_VERTICAL); }
static inline const char* CP_ULCorner (void) { return CP_BoxDrawing (BD_ULCORNER); }
static inline const char* CP_LLCorner (void) { return CP_BoxDrawing (BD_LLCORNER); }
static inline const char* CP_URCorner (void) { return CP_BoxDrawing (BD_URCORNER); }
static inline const char* CP_LRCorner (void) { return CP_BoxDrawing (BD_LRCORNER); }
static inline const char* CP_VerticalRight (void) { return CP_BoxDrawing (BD_RVERTICAL); }
static inline const char* CP_VerticalLeft (void) { return CP_BoxDrawing (BD_LVERTICAL); }
static inline const char* CP_HorizontalDown (void) { return CP_BoxDrawing (BD_DHORIZONTAL); }
static inline const char* CP_HorizontalUp (void) { return CP_BoxDrawing (BD_UHORIZONTAL); }
/* End of consprop.h */
#endif