2640c6cb25
I forgot a few BASHisms in the code.
565 lines
14 KiB
Bash
Executable File
565 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
# This script will take an HTML compatible hex colour string and turn it into
|
|
# a terminal sequence for true colour and other formats which best approximate
|
|
# its colour value.
|
|
#
|
|
# After that, we can map a few other special named symbols. The idea is to
|
|
# permit shell variables such as `aaffcc bold italic` to indicate what you
|
|
# want a format to look like.
|
|
#
|
|
# The terminal colour and effect language herein implemented is named
|
|
# "SGR Name".
|
|
|
|
debug=0
|
|
|
|
debug_print()
|
|
{
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
echo ${*} >&2
|
|
fi
|
|
}
|
|
|
|
# This is the named color map. Just add new entries and they'll be supported...
|
|
|
|
COLOR_TABLE=""
|
|
|
|
map()
|
|
{
|
|
if [ $# -eq 2 ]
|
|
then
|
|
COLOR_TABLE="${COLOR_TABLE}:${1}=${2}"
|
|
else
|
|
___right="${COLOR_TABLE##*:${1}=}"
|
|
echo ${___right%%:*}
|
|
fi
|
|
}
|
|
|
|
map white "FFFFFF"
|
|
map silver "C0C0C0"
|
|
map gray "808080"
|
|
map grey "808080"
|
|
map black "000000"
|
|
map red "FF0000"
|
|
map maroon "800000"
|
|
map yellow "FFFF00"
|
|
map olive "808000"
|
|
map lime "00FF00"
|
|
map green "008000"
|
|
map aqua "00FFFF"
|
|
map teal "008080"
|
|
map blue "0000FF"
|
|
map navy "000080"
|
|
map fuchsia "FF00FF"
|
|
map purple "800080"
|
|
|
|
# Some VGA color names can be promoted:
|
|
map brown "aa5500"
|
|
map cyan "00aaaa"
|
|
|
|
|
|
# The VGA names can be prefixed with "vga-" to explicitly avoid the collisions with HTML names, above...
|
|
# Based upon wikipedia's article on ANSI. See the table on that page for details.
|
|
map vga-black "000000"
|
|
map vga-red "aa0000"
|
|
map vga-green "00aa00"
|
|
map vga-brown "aa5500"
|
|
map vga-yellow "aa5500"
|
|
map vga-blue "0000aa"
|
|
map vga-magenta "aa00aa"
|
|
map vga-cyan "00aaaa"
|
|
map vga-white "aaaaaa"
|
|
|
|
map vga-brightblack "555555"
|
|
map vga-grey "555555"
|
|
map vga-gray "555555"
|
|
map vga-brightred "ff5555"
|
|
map vga-brightgreen "55ff55"
|
|
map vga-brightyellow "ffff55"
|
|
map vga-brightblue "5555ff"
|
|
map vga-brightmagenta "ff55ff"
|
|
map vga-brightcyan "55ffff"
|
|
map vga-brightwhite "ffffff"
|
|
|
|
enable_greyscale_cheat=0
|
|
|
|
nocsi=0
|
|
if [ "$1" = "no-csi" ]
|
|
then
|
|
nocsi=1
|
|
shift 1
|
|
fi
|
|
|
|
dump_colors()
|
|
{
|
|
all_maps=$(echo ${COLOR_TABLE} | sed -e 's/:/ /g' -e 's/=[0-9a-fA-F]\{6\}//g')
|
|
debug_print "Going through all these colors ${all_maps} (${COLOR_TABLE})"
|
|
for key in ${all_maps}
|
|
do
|
|
debug_print "Processing key ${key}"
|
|
case $key in
|
|
*bright*) text_color="000000" ;;
|
|
*black*) text_color="ffffff" ;;
|
|
*) text_color="000000" ;;
|
|
esac
|
|
printf "%-24s$(map ${key} ) |" ${key}
|
|
printf "`$0 reset reverse bg:${text_color} fg:${key}`native-sample`$0 reset`|"
|
|
if [ -z "${CSHENV_TERMINAL_COLORS+x}" ] || [ ${CSHENV_TERMINAL_COLORS} -ge 256 ]
|
|
then
|
|
printf "`env CSHENV_TERMINAL_COLORS=256 $0 reset reverse bg:${text_color} fg:${key}`8-bit sample`$0 reset`|"
|
|
fi
|
|
if [ -z "${CSHENV_TERMINAL_COLORS+x}" ] || [ ${CSHENV_TERMINAL_COLORS} -ge 16 ]
|
|
then
|
|
printf "`env CSHENV_TERMINAL_COLORS=16 $0 reset reverse bg:${text_color} fg:${key}`4-bit sample`$0 reset`|"
|
|
fi
|
|
if [ -z "${CSHENV_TERMINAL_COLORS+x}" ] || [ ${CSHENV_TERMINAL_COLORS} -ge 8 ]
|
|
then
|
|
printf "`env CSHENV_TERMINAL_COLORS=8 $0 reset reverse bg:${text_color} fg:${key}`3-bit sample`$0 reset`|"
|
|
fi
|
|
printf "\n"
|
|
done
|
|
|
|
exit 0
|
|
}
|
|
|
|
cube_row()
|
|
{
|
|
r_base=${1}
|
|
g=0
|
|
while [ ${g} -lt 6 ]
|
|
do
|
|
u=0
|
|
while [ ${u} -lt 3 ]
|
|
do
|
|
r=$(( r_base + u ))
|
|
|
|
b=0
|
|
while [ ${b} -lt 6 ]
|
|
do
|
|
i=$(( 16 + ( r * 36 ) + ( g * 6 ) + ( b ) ))
|
|
|
|
txt="" && [ ${g} -lt 3 ] && txt=';37;1'
|
|
|
|
printf "[30${txt}m[48;5;${i}m%3i[m " ${i}
|
|
|
|
b=$(( b + 1 ))
|
|
done
|
|
|
|
printf ' '
|
|
|
|
u=$(( u + 1 ))
|
|
done
|
|
|
|
printf '\n'
|
|
|
|
g=$(( g + 1 ))
|
|
done
|
|
printf '\n'
|
|
}
|
|
|
|
greyscale_chart()
|
|
{
|
|
[ ${i} -ge 244 ] && txt=";30"
|
|
|
|
local k=$(( i + 12 ))
|
|
while [ ${i} -lt ${k} ]
|
|
do
|
|
printf "[48;5;${i};37;1${txt}m%3i[m " ${i}
|
|
i=$(( i + 1 ))
|
|
done
|
|
printf '\n'
|
|
}
|
|
|
|
color_chart()
|
|
{
|
|
i=0
|
|
|
|
# Base 16
|
|
while [ ${i} -lt 16 ]
|
|
do
|
|
[ ${i} -gt 0 ] && txt=";30"
|
|
|
|
printf "[37;1${txt};48;5;${i}m%3i[m " ${i}
|
|
i=$(( i + 1 ))
|
|
done
|
|
printf '\n\n'
|
|
|
|
r=0;g=0;b=0
|
|
cube_row 0
|
|
cube_row 3
|
|
i=$(( 6 * 6 * 6 + 16 ))
|
|
|
|
greyscale_chart
|
|
greyscale_chart
|
|
}
|
|
|
|
setup_color_depth()
|
|
{
|
|
use_8_bit=0
|
|
use_4_bit=0
|
|
use_3_bit=0
|
|
|
|
# Must only be set if terminal colors are reduced...
|
|
if [ "${CSHENV_TERMINAL_COLORS+x}" ]
|
|
then
|
|
if [ ${CSHENV_TERMINAL_COLORS} -eq 256 ]
|
|
then
|
|
use_8_bit=1
|
|
elif [ ${CSHENV_TERMINAL_COLORS} -eq 16 ]
|
|
then
|
|
use_4_bit=1
|
|
elif [ ${CSHENV_TERMINAL_COLORS} -eq 8 ]
|
|
then
|
|
use_3_bit=1
|
|
else
|
|
echo "ERROR!" 1>&2 ; exit 255
|
|
fi
|
|
fi
|
|
}
|
|
|
|
render_color()
|
|
{
|
|
command_color=30
|
|
if [ ${use_3_bit} -ne 0 ]
|
|
then
|
|
# Underline colours need 8-bit or more
|
|
if [ ${background} -gt 1 ] ; then return; fi
|
|
basecolor=$(( ${command_color} + ${background}*10))
|
|
next="$(( ${basecolor} + ${legacy_3_bit} ))"
|
|
elif [ ${use_4_bit} -ne 0 ]
|
|
then
|
|
# Underline colours need 8-bit or more
|
|
if [ ${background} -gt 1 ] ; then return; fi
|
|
command_color=$(( ${command_color} + ${intensity_1_bit} * 60 ))
|
|
basecolor=$(( ${command_color} + ${background}*10 ))
|
|
next="$(( ${basecolor} + ${legacy_3_bit} ))"
|
|
elif [ ${use_8_bit} -ne 0 ]
|
|
then
|
|
basecolor=$(( 8 + ${command_color} + ${background}*10 ))
|
|
next="${basecolor};5;${ext_8_bit}"
|
|
else
|
|
basecolor=$(( 8 + ${command_color} + ${background}*10 ))
|
|
next="${basecolor};2;${red_dec};${green_dec};${blue_dec}"
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
ansi_color()
|
|
{
|
|
if [ ${1} -gt 15 ]
|
|
then
|
|
echo "ERROR!" 1>&2 ; exit 255
|
|
elif [ ${1} -ge 8 ]
|
|
then
|
|
use_4_bit=1
|
|
intensity_1_bit=1
|
|
legacy_3_bit=$(( ${1} - 8 ))
|
|
else
|
|
use_3_bit=1
|
|
intensity_1_bit=0
|
|
legacy_3_bit=$(( ${1} ))
|
|
fi
|
|
}
|
|
|
|
ext_grey()
|
|
{
|
|
use_8_bit=1
|
|
grey_val=${1}
|
|
if [ ${grey_val} -le 0 - ${grey_val} -gt 23 ]
|
|
then
|
|
echo "ERROR!" 1>&2 ; exit 255
|
|
fi
|
|
ext_8_bit=$(( 232 + ${grey_val} ))
|
|
}
|
|
|
|
ext_rgb()
|
|
{
|
|
use_8_bit=1
|
|
red_ext_val="${1%??*}"
|
|
tmp="${1#?}"
|
|
green_ext_val=${tmp%?*}
|
|
blue_ext_val=${tmp#?*}
|
|
ext_8_bit=$(( 16 + 36 * ${red_ext_val} + 6 * ${green_ext_val} + ${blue_ext_val} ))
|
|
}
|
|
|
|
ext_color()
|
|
{
|
|
use_8_bit=1
|
|
if [ ${1} -gt 255 ]
|
|
then
|
|
echo "ERROR!" 1>&2 ; exit 255
|
|
else
|
|
ext_8_bit=${1}
|
|
fi
|
|
}
|
|
|
|
rgb_color()
|
|
{
|
|
color=$1
|
|
case $1 in
|
|
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
|
|
tmp="${color}"
|
|
color="${tmp%??*}${tmp%??*}"
|
|
tmp="${tmp#?}"
|
|
color="${color}${tmp%?*}${tmp%?*}${tmp#?}${tmp#?}"
|
|
;;
|
|
esac
|
|
# First split off the red, green, and blue components...
|
|
red_hex="0x${color%????}"
|
|
color="${color#??}"
|
|
green_hex="0x${color%??}"
|
|
blue_hex="0x${color#??}"
|
|
|
|
# Convert to decimal...
|
|
red_dec=$((${red_hex}))
|
|
green_dec=$((${green_hex}))
|
|
blue_dec=$((${blue_hex}))
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Red: $red_dec"
|
|
debug_print "Green: $green_dec"
|
|
debug_print "Blue: $blue_dec"
|
|
fi
|
|
|
|
# Now compute the rest of the stuff...
|
|
|
|
# We probably don't have to compute all the unused color states. We only have to compute
|
|
# the color state we're signed up for by CSHENV_TERMINAL_COLORS... but whatever...
|
|
|
|
# We round up by 128 points of colour, so that if we're above a certain intensity, we get the top
|
|
# bit set.
|
|
|
|
red_1_bit=$(( ( ( ${red_dec} + 128 ) >> 8 ) & 1 ))
|
|
green_1_bit=$(( ( ( ${green_dec} + 128 ) >> 8 ) & 1 ))
|
|
blue_1_bit=$(( ( ( ${blue_dec} + 128 ) >> 8 ) & 1 ))
|
|
intensity_1_bit=0
|
|
|
|
|
|
# If we can support an intensity bit, we'll turn that on too...
|
|
# But we're going to stop using bold to set the colour "intense"
|
|
# We'll use the 9x and 10x forms...
|
|
if [ ${red_dec} -ge 192 -o ${green_dec} -ge 192 -o ${blue_dec} -ge 192 ]
|
|
then
|
|
intensity_1_bit=1
|
|
fi
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Red bit: " $red_1_bit
|
|
debug_print "Green bit: " $green_1_bit
|
|
debug_print "Blue bit: " $blue_1_bit
|
|
debug_print "Intensity bit: " $intensity_1_bit
|
|
fi
|
|
|
|
|
|
# Because grey is tricky, I'm going to special case it:
|
|
if [ ${red_dec} -eq ${green_dec} -a ${green_dec} -eq ${blue_dec} -a ${red_dec} -lt 128 -a ${red_dec} -ge 80 ]
|
|
then
|
|
red_1_bit=0
|
|
green_1_bit=0
|
|
blue_1_bit=0
|
|
intensity_1_bit=1
|
|
fi
|
|
|
|
# Because brown is tricky, I'm going to special case it:
|
|
if [ ${red_dec} -gt 128 -a ${green_dec} -gt 80 -a ${blue_dec} -lt 50 ]
|
|
then
|
|
red_1_bit=1
|
|
green_1_bit=1
|
|
blue_1_bit=0
|
|
fi
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Red bit: " $red_1_bit
|
|
debug_print "Green bit: " $green_1_bit
|
|
debug_print "Blue bit: " $blue_1_bit
|
|
debug_print "Intensity bit: " $intensity_1_bit
|
|
fi
|
|
|
|
# This lets us combine them for a legacy colour value in the legacy colour space...
|
|
|
|
legacy_3_bit=$(( ( ${blue_1_bit} << 2 ) + ( ${green_1_bit} << 1 ) + ( ${red_1_bit} ) ))
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Legacy colour: " ${legacy_3_bit} " Intensity: " ${intensity_1_bit}
|
|
fi
|
|
|
|
# Now compute an extended colour cube placement (216 colours):
|
|
|
|
red_ext_val=$(( ${red_dec} * 6 / 256 ))
|
|
green_ext_val=$(( ${green_dec} * 6 / 256 ))
|
|
blue_ext_val=$(( ${blue_dec} * 6 / 256 ))
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Red ext: " ${red_ext_val}
|
|
debug_print "Green ext: " ${green_ext_val}
|
|
debug_print "Blue ext: " ${blue_ext_val}
|
|
fi
|
|
|
|
ext_8_bit=$(( 16 + 36 * ${red_ext_val} + 6 * ${green_ext_val} + ${blue_ext_val} ))
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Computed 216 color cube: " ${ext_8_bit}
|
|
fi
|
|
|
|
# Check for precise greyscale, which would replace the above:
|
|
# (Note that precise greyscale is implied by a user explicitly making ALL of RGB the
|
|
# same value, thus demanding greyscale, rather than a subtle tinting...)
|
|
|
|
if [ ${red_dec} -eq ${green_dec} -a ${green_dec} -eq ${blue_dec} ]
|
|
then
|
|
ext_8_bit=$(( 232 + ${red_dec} * 24 / 256 ))
|
|
fi
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Computed 256 color choice, after applying greyscale scan: " ${ext_8_bit}
|
|
fi
|
|
|
|
if [ ${enable_greyscale_cheat} -ne 0 ]
|
|
then
|
|
# Check for SPECIFIC white and black and grey:
|
|
# The half-saturation, no saturation, and full saturation values are mapped
|
|
# to specific points in the legacy set. (This may be ill-advised as a manually
|
|
# built reverse video terminal setting through colour mapping may make it so that
|
|
# we wind up in la-la land. Perhaps the better way is to find the head and tail
|
|
# points in the 6x6x6 colour cube.)
|
|
#
|
|
# TODO: Only map the head and tail of the colour cube.
|
|
if [ ${red_dec} -eq ${green_dec} -a ${green_dec} -eq ${blue_dec} ]
|
|
then
|
|
if [ ${red_dec} -eq 0 ]
|
|
then
|
|
ext_8_bit=0
|
|
elif [ ${red_dec} -eq 255 ]
|
|
then
|
|
ext_8_bit=15
|
|
elif [ ${red_dec} -eq 128 ]
|
|
then
|
|
ext_8_bit=8
|
|
elif [ ${red_dec} -eq 192 ]
|
|
then
|
|
ext_8_bit=7
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ ${debug} -ne 0 ]
|
|
then
|
|
debug_print "Computed 256 color choice, after applying white and black scan: " ${ext_8_bit}
|
|
fi
|
|
|
|
# We do NOT check for the base colours precisely. Since those 16 can be custom mapped by your terminal emulator, you just pass `ansi:0` thru
|
|
# `ansi:15` to select them by ID. Or use `ext:0` thru `ext:255` to select a specific extended colour.
|
|
}
|
|
|
|
make_color()
|
|
{
|
|
setup_color_depth
|
|
background=$1 # $1 is the background bit
|
|
$2 $3 # $2 is the color function
|
|
render_color
|
|
}
|
|
|
|
named_color()
|
|
{
|
|
name=$( echo ${1} | awk '{print tolower($1)}' )
|
|
mapped=$(map ${name})
|
|
debug_print "${name} -> ${mapped}"
|
|
if [ -z "${mapped}" ]
|
|
then
|
|
echo "ERROR!" 1>&2 ; exit 255
|
|
fi
|
|
|
|
rgb_color ${mapped}
|
|
}
|
|
|
|
build_sgr_code()
|
|
{
|
|
case $1 in
|
|
dump-colors) dump_colors ;;
|
|
color-chart) color_chart ;;
|
|
|
|
reset) next=0 ;;
|
|
bold) next=1 ;;
|
|
dim) next=2 ;;
|
|
italic) next=3 ;;
|
|
underline|under) next=4 ;;
|
|
blink) next=5 ;;
|
|
fastblink) next=6 ;;
|
|
reverse) next=7 ;;
|
|
hide|conceal) next=8 ;;
|
|
strike|strikethrough|strikethru) next=9 ;;
|
|
doubleunder) next=21 ;;
|
|
reveal) next=28 ;;
|
|
|
|
ansi:*) make_color 0 ansi_color ${1#?????} ;;
|
|
ext:grey*) make_color 0 ext_grey ${1#????????} ;;
|
|
ext:rgb*) make_color 0 ext_rgb ${1#???????} ;;
|
|
ext:*) make_color 0 ext_color ${1#????} ;;
|
|
# 12-bit color also supported
|
|
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 0 rgb_color $1 ;;
|
|
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 0 rgb_color $1 ;;
|
|
|
|
fg:ansi:*) make_color 0 ansi_color ${1#????????} ;;
|
|
fg:ext:grey*) make_color 0 ext_grey ${1#???????????} ;;
|
|
fg:ext:rgb*) make_color 0 ext_rgb ${1#??????????} ;;
|
|
fg:ext:*) make_color 0 ext_color ${1#???????} ;;
|
|
# 12-bit color also supported
|
|
fg:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 0 rgb_color ${1#???} ;;
|
|
fg:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 0 rgb_color ${1#???} ;;
|
|
|
|
bg:ansi:*) make_color 1 ansi_color ${1#????????} ;;
|
|
bg:ext:grey*) make_color 1 ext_grey ${1#???????????} ;;
|
|
bg:ext:rgb*) make_color 1 ext_rgb ${1#??????????} ;;
|
|
bg:ext:*) make_color 1 ext_color ${1#???????} ;;
|
|
# 12-bit color also supported
|
|
bg:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 1 rgb_color ${1#???} ;;
|
|
bg:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 1 rgb_color ${1#???} ;;
|
|
|
|
ul:ext:grey*) make_color 2 ext_grey ${1#???????????} ;;
|
|
ul:ext:rgb*) make_color 2 ext_rgb ${1#??????????} ;;
|
|
ul:ext:*) make_color 2 ext_color ${1#???????} ;;
|
|
# 12-bit color also supported
|
|
ul:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 2 rgb_color ${1#???} ;;
|
|
ul:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) make_color 2 rgb_color ${1#???} ;;
|
|
|
|
fg:*) make_color 0 named_color ${1#???} ;;
|
|
bg:*) make_color 1 named_color ${1#???} ;;
|
|
ul:*) make_color 2 named_color ${1#???} ;;
|
|
*) make_color 0 named_color $1 ;;
|
|
esac
|
|
if [ ! -z ${output} ]
|
|
then
|
|
output="${output};"
|
|
fi
|
|
output="${output}${next}"
|
|
}
|
|
|
|
output=""
|
|
while [ ! -z $1 ]
|
|
do
|
|
build_sgr_code $1
|
|
shift 1
|
|
done
|
|
|
|
if [ ! -z "${output}" ] && [ ${nocsi} -ne 1 ]
|
|
then
|
|
#echo "I AM PRINTING WITH CSI! (${nocsi} is nocsi, \`${output}\` is output)">&2
|
|
output="[${output}m"
|
|
fi
|
|
if [ ! -z "${output}" ]
|
|
then
|
|
printf ${output}
|
|
fi
|
|
|
|
|
|
exit 0
|