Skip to content

Instantly share code, notes, and snippets.

@gnachman
Created May 10, 2020 06:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gnachman/cd82b2c4d47970f4e62ff7d56feae86a to your computer and use it in GitHub Desktop.
Save gnachman/cd82b2c4d47970f4e62ff7d56feae86a to your computer and use it in GitHub Desktop.
Prototype of pure POSIX terminal features decoder
#!/bin/dash
__tf_decode() {
local TABLE="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local COUNT=0
local ACCUMULATOR=0
local SKIP=0
local ENCODED="$1"
local REMAINING="$ENCODED"
local FEATURES="8 VERSION 1 24BIT_BASIC 1 24BIT_FULL 1 CLIPBOARD_WRITE 1 DECSLRM 1 MOUSE_BASIC 1 MOUSE_ALT_SCROLL 1 UTF8 1 DECSCUSR_BASIC 1 DECSCUSR_BAR 1 AMBIGUOUS_FULLWIDTH 4 UNICODE_VERSION 1 RTL_LEVEL1 1 RTL_LEVEL2 1 TITLE_SETTING 1 BRACKETED_PASTE 1 FOCUS_REPORTING 1 CWD 1 BCE 1 UNDERLINE_STYLING 1 UNDERLINE_STYLE_DOUBLE 1 UNDERLINE_STYLE_CURLY 1 UNDERLINE_STYLE_DOTTED 1 UNDERLINE_STYLE_DASHED 1 STRIKETHROUGH 1 OVERLINE 1 SYNC 1 HYPERLINKS 1 NOTIFICATIONS 1 SIXEL 1 ITALICS 1 INVERSE_7 0 EOF"
local BITS_NEEDED="${FEATURES%% *}"; FEATURES="${FEATURES#* }"
local FEATURE_NAME="${FEATURES%% *}"; FEATURES="${FEATURES#* }"
local VALUE=0
while [ -n "$REMAINING" ]; do
# Read the next byte from the base64-encoded input.
local REST="${REMAINING#?}"
CHAR="${REMAINING%"$REST"}"
REMAINING="$REST"
if [ "$CHAR" = $'\n' ]; then
continue
fi
if [ "$CHAR" = '=' ]; then
ACCUMULATOR=$(( ACCUMULATOR << 6 ))
SKIP=$(( SKIP + 1 ))
continue
fi
# Look up the byte in our table, setting CHAR to its 1-based index.
CHAR=${TABLE#*$CHAR}
CHAR=$(( ${#TABLE}-${#CHAR} ))
if [ "$CHAR" = 0 ]; then
continue
fi
# Accrue six bits from CHAR into the accumulator.
ACCUMULATOR=$(( ACCUMULATOR << 6 | (CHAR - 1) ))
COUNT=$(( COUNT + 1 ))
if [ "$COUNT" -ne "4" ]; then
continue
fi
# We have decoded 24 bits of data. Process it.
LIMIT=$(( 8 * SKIP ))
local SHIFT=16
while [ $SHIFT -ge $LIMIT ]; do
# Place the most significant unused byte from accumulator in CHAR.
CHAR=$(( ACCUMULATOR >> SHIFT & 255 ))
local BIT=0
local UNSHIFT=0
# Iterate over each bit in CHAR.
while [ $BIT -lt 8 ]; do
# OR the bit's value into VALUE.
local IS_SET=$(( (1 << BIT) & CHAR ))
VALUE=$(( VALUE + (IS_SET >> UNSHIFT) ))
BITS_NEEDED=$(( BITS_NEEDED - 1 ))
if [ $BITS_NEEDED = 0 ]; then
# We have read enough bits for the next feature. Output it.
export TF_"$FEATURE_NAME=$VALUE"
# Shift in the next feature.
BITS_NEEDED="${FEATURES%% *}"; FEATURES="${FEATURES#* }"
FEATURE_NAME="${FEATURES%% *}"; FEATURES="${FEATURES#* }"
VALUE=0
UNSHIFT=$((BIT + 1))
if [ $BITS_NEEDED = 0 ]; then
# 0 bits signals no more features are expected.
return
fi
fi
BIT=$(( BIT + 1 ))
done
SHIFT=$(( SHIFT - 8 ))
done
# Prepare to handle four more bytes of input.
ACCUMULATOR=0
COUNT=0
done
}
# Version 1. All features set to 1.
TEST1=$(echo -n 01FFFFFF FF03 | xxd -r -p - | base64)
__tf_decode $TEST1
env | grep TF_
# Version 255. All features set to 0.
TEST2=$(echo -n FF000000 0000 | xxd -r -p - | base64)
__tf_decode $TEST2
env | grep TF_
# Version 170, unicode=10, every other feature set to 1
TEST3=$(echo -n AAAAAAAA AA02 | xxd -r -p - | base64)
__tf_decode $TEST3
env | grep TF_
# Version 85, unicode=5, every other feature set to 1
TEST4=$(echo -n 55555555 5501 | xxd -r -p - | base64)
__tf_decode $TEST4
env | grep TF_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment