[PR #11932] Add rbeesley's ansi-color.cmd, a CMD-based color tool (& more) #28810

Open
opened 2026-01-31 09:30:54 +00:00 by claunia · 0 comments
Owner

Original Pull Request: https://github.com/microsoft/terminal/pull/11932

State: closed
Merged: Yes


Adds the Ansi-Color tool, completing #6470.

What started out as an experiment to see the support of ANSI support to
conhost, I wanted to write a Windows equivalent of Daniel Crisman's
BASH script on tldp.org. I didn't like how the BASH script hard coded
in whitespace and I thought Windows could do better. I applied
techniques to speed up the execution and tried to use the Command Script
batch language in ways that pushed the limits for what I thought it
could do. Running this from withing PowerShell, &cmd /c ansi-color.cmd,
I found out that the active code page is already 65001, but when ran
from a Command Prompt, the active code page depends on the regional
settings and I would have a dependency on CHCP to detect the active code
page, change to 65001 if necessary, and restore the previous code page
after running. I found it useful for designing color schemes and writing
shaders, and I started using an earlier version for logging bugs.

Initially it was a single script which I would dump every SGR
combination I could think of, and many which weren't implemented yet,
but as I was looking at other tools which had been written I wanted to
mimic the same output so I could do side-by-side comparisons. First I
wrote crisman.def and then colortool.def. So I had to align text.
Then output was slow for big tables so I wrote to an outbuffer and made
use of macros. I wanted to handle flags instead of needing to change
settings every time, so I added argument parsing and loading external
files.

It's a batch file that has some really useful purposes and does nothing
I've seen before.

I've ran every definition in a Command Prompt, with CP 437 and CP 65001
to make sure it works. Posted as a Gist on my account for about year, a
Portuguese (Brazilian as I recall), speaking user tried the Gist and it
was failing because CHCP is localized. I've taken an approach to try and
make it work for different localizations, but this is a potential
problem. I also ran every definition in PowerShell 7, shelling down to
cmd to actually run it. Lastly, I went through using the flags and
made sure that help would be shown. Error messages generate error levels
when the script exits, so those could be used to use this in an
automated test and catch if there was a problem with the script
executing. It won't be able to validate if the generated output shows
correctly, but it would fail if a definition file is missing or if it
needed to switch to Unicode and wasn't flagged or configured to do so.
For trying to build the table stub and headers, there is some debug code
which will output what was parsed. This is the last debug code in the
script itself, but I found it to be useful at times, so there is a
configuration setting which can turn that debug output back on.
Technically there is also a debug statement to break after adding the
macros and parsing the arguments, but before it does any configuration
changes, changes the code page, or anything. This was useful for making
changes to macros and being able to test them as well as making sure
that flags and arguments are parsed correctly. It was a rather cryptic
discovery to call cmd /c exit -1073741510 to break out, so in part
that was my reason to leave this in. The definition files themselves
could be cleaned up further, but in both of them there are a lot of
Unicode codepoints that could be useful when defining division lines for
the headers so I opted to just comment them out. I initially had the
default definition to require Unicode, but now it just changes to a
non-Unicode output if it can't. And this brings up the last concern. The
way certain settings are set, is that they are defined in the
__TABLE__ section of the definition file. With PARSE_TABLE_DATA,
the script looks line by line for SET * or IF * and if found it will
effectively eval that line. The definition files are written so that for
instance SET "CELL= gYw " could be used to set the code which is used.
If was allow so that there could be a test if Unicode were available
and set something different for ASCII and Unicode. But it also opens the
possibility that a rogue actor could create a ansi-color.cmd
definition file something like SET "foo=bar" & DoSomethingBad.exe.
First of all I'd be flattered that anyone would use this very niche
tool, but it is something I think which needs to be called out.
Essentially the definition files are just batch files, but they are
extremely limited to executing only one line at a time and only
supporting IF and SET commands. I think this is a minimal risk because
at that point batch files would already be more effective, but there
should also be an expectation that a batch file will run something and
the same may not be true for the heavily degraded definition files. I
think the risk is minimal but it is a risk I wanted to make clear. This
really showcases some interesting techniques and ideas, and I hope that
it is useful. All told, there are a couple years of incremental changes
in this PR.

Closes #6470

**Original Pull Request:** https://github.com/microsoft/terminal/pull/11932 **State:** closed **Merged:** Yes --- Adds the Ansi-Color tool, completing #6470. What started out as an experiment to see the support of ANSI support to conhost, I wanted to write a Windows equivalent of Daniel Crisman's [BASH script on tldp.org]. I didn't like how the BASH script hard coded in whitespace and I thought Windows could do better. I applied techniques to speed up the execution and tried to use the Command Script batch language in ways that pushed the limits for what I thought it could do. Running this from withing PowerShell, `&cmd /c ansi-color.cmd`, I found out that the active code page is already 65001, but when ran from a Command Prompt, the active code page depends on the regional settings and I would have a dependency on CHCP to detect the active code page, change to 65001 if necessary, and restore the previous code page after running. I found it useful for designing color schemes and writing shaders, and I started using an earlier version for logging bugs. Initially it was a single script which I would dump every SGR combination I could think of, and many which weren't implemented yet, but as I was looking at other tools which had been written I wanted to mimic the same output so I could do side-by-side comparisons. First I wrote `crisman.def` and then `colortool.def`. So I had to align text. Then output was slow for big tables so I wrote to an outbuffer and made use of macros. I wanted to handle flags instead of needing to change settings every time, so I added argument parsing and loading external files. It's a batch file that has some really useful purposes and does nothing I've seen before. I've ran every definition in a Command Prompt, with CP 437 and CP 65001 to make sure it works. Posted as a Gist on my account for about year, a Portuguese (Brazilian as I recall), speaking user tried the Gist and it was failing because CHCP is localized. I've taken an approach to try and make it work for different localizations, but this is a potential problem. I also ran every definition in PowerShell 7, shelling down to `cmd` to actually run it. Lastly, I went through using the flags and made sure that help would be shown. Error messages generate error levels when the script exits, so those could be used to use this in an automated test and catch if there was a problem with the script executing. It won't be able to validate if the generated output shows correctly, but it would fail if a definition file is missing or if it needed to switch to Unicode and wasn't flagged or configured to do so. For trying to build the table stub and headers, there is some debug code which will output what was parsed. This is the last debug code in the script itself, but I found it to be useful at times, so there is a configuration setting which can turn that debug output back on. Technically there is also a debug statement to break after adding the macros and parsing the arguments, but before it does any configuration changes, changes the code page, or anything. This was useful for making changes to macros and being able to test them as well as making sure that flags and arguments are parsed correctly. It was a rather cryptic discovery to call `cmd /c exit -1073741510` to break out, so in part that was my reason to leave this in. The definition files themselves could be cleaned up further, but in both of them there are a lot of Unicode codepoints that could be useful when defining division lines for the headers so I opted to just comment them out. I initially had the default definition to require Unicode, but now it just changes to a non-Unicode output if it can't. And this brings up the last concern. The way certain settings are set, is that they are defined in the `__TABLE__` section of the definition file. With `PARSE_TABLE_DATA`, the script looks line by line for `SET *` or `IF *` and if found it will effectively eval that line. The definition files are written so that for instance `SET "CELL= gYw "` could be used to set the code which is used. `If` was allow so that there could be a test if Unicode were available and set something different for ASCII and Unicode. But it also opens the possibility that a rogue actor could create a `ansi-color.cmd` definition file something like `SET "foo=bar" & DoSomethingBad.exe`. First of all I'd be flattered that anyone would use this very niche tool, but it is something I think which needs to be called out. Essentially the definition files are just batch files, but they are extremely limited to executing only one line at a time and only supporting IF and SET commands. I think this is a minimal risk because at that point batch files would already be more effective, but there should also be an expectation that a batch file will run something and the same may not be true for the heavily degraded definition files. I think the risk is minimal but it is a risk I wanted to make clear. This really showcases some interesting techniques and ideas, and I hope that it is useful. All told, there are a couple years of incremental changes in this PR. Closes #6470 [BASH script on tldp.org]: https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
claunia added the pull-request label 2026-01-31 09:30:54 +00:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#28810