451c3912 (MoserMichael 2026-01-06 07:41:14 +0200   1)   #!/bin/sh
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   2) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   3) # Convert ANSI (terminal) colours and attributes to HTML
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   4) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   5) # Licence: LGPLv2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   6) # Author:
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   7) #    http://www.pixelbeat.org/docs/terminal_colours/
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   8) # Examples:
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200   9) #    ls -l --color=always | ansi2html.sh > ls.html
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  10) #    git show --color | ansi2html.sh > last_change.html
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  11) #    Generally one can use the `script` util to capture full terminal output.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  12) # Changes:
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  13) #    V0.1, 24 Apr 2008, Initial release
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  14) #    V0.2, 01 Jan 2009, Phil Harnish <philharnish@gmail.com>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  15) #                         Support `git diff --color` output by
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  16) #                         matching ANSI codes that specify only
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  17) #                         bold or background colour.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  18) #                       P@draigBrady.com
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  19) #                         Support `ls --color` output by stripping
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  20) #                         redundant leading 0s from ANSI codes.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  21) #                         Support `grep --color=always` by stripping
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  22) #                         unhandled ANSI codes (specifically ^[[K).
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  23) #    V0.3, 20 Mar 2009, http://eexpress.blog.ubuntu.org.cn/
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  24) #                         Remove cat -v usage which mangled non ascii input.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  25) #                         Cleanup regular expressions used.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  26) #                         Support other attributes like reverse, ...
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  27) #                       P@draigBrady.com
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  28) #                         Correctly nest <span> tags (even across lines).
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  29) #                         Add a command line option to use a dark background.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  30) #                         Strip more terminal control codes.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  31) #    V0.4, 17 Sep 2009, P@draigBrady.com
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  32) #                         Handle codes with combined attributes and color.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  33) #                         Handle isolated <bold> attributes with css.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  34) #                         Strip more terminal control codes.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  35) #    V0.26, 16 Nov 2019
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  36) #      http://github.com/pixelb/scripts/commits/master/scripts/ansi2html.sh
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  37) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  38) gawk --version >/dev/null || exit 1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  39) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  40) if [ "$1" = "--version" ]; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  41)     printf '0.26\n' && exit
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  42) fi
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  43) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  44) usage()
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  45) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  46) printf '%s\n' \
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  47) 'This utility converts ANSI codes in data passed to stdin
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  48) It has 4 optional parameters:
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  49) --bg=dark --palette=linux|solarized|tango|xterm --css-only|--body-only
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  50) E.g.: ls -l --color=always | ansi2html.sh --bg=dark > ls.html' >&2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  51)     exit
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  52) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  53) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  54) if [ "$1" = "--help" ]; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  55)     usage
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  56) fi
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  57) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  58) processArg()
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  59) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  60)     [ "$1" = "--bg=dark" ] && { dark_bg=yes; return; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  61)     [ "$1" = "--css-only" ] && { css_only=yes; return; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  62)     [ "$1" = "--body-only" ] && { body_only=yes; return; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  63)     if [ "$1" = "--palette=solarized" ]; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  64)        # See http://ethanschoonover.com/solarized
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  65)        P0=073642;  P1=D30102;  P2=859900;  P3=B58900;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  66)        P4=268BD2;  P5=D33682;  P6=2AA198;  P7=EEE8D5;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  67)        P8=002B36;  P9=CB4B16; P10=586E75; P11=657B83;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  68)       P12=839496; P13=6C71C4; P14=93A1A1; P15=FDF6E3;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  69)       return;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  70)     elif [ "$1" = "--palette=solarized-xterm" ]; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  71)        # Above mapped onto the xterm 256 color palette
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  72)        P0=262626;  P1=AF0000;  P2=5F8700;  P3=AF8700;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  73)        P4=0087FF;  P5=AF005F;  P6=00AFAF;  P7=E4E4E4;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  74)        P8=1C1C1C;  P9=D75F00; P10=585858; P11=626262;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  75)       P12=808080; P13=5F5FAF; P14=8A8A8A; P15=FFFFD7;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  76)       return;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  77)     elif [ "$1" = "--palette=tango" ]; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  78)        # Gnome default
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  79)        P0=000000;  P1=CC0000;  P2=4E9A06;  P3=C4A000;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  80)        P4=3465A4;  P5=75507B;  P6=06989A;  P7=D3D7CF;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  81)        P8=555753;  P9=EF2929; P10=8AE234; P11=FCE94F;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  82)       P12=729FCF; P13=AD7FA8; P14=34E2E2; P15=EEEEEC;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  83)       return;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  84)     elif [ "$1" = "--palette=xterm" ]; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  85)        P0=000000;  P1=CD0000;  P2=00CD00;  P3=CDCD00;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  86)        P4=0000EE;  P5=CD00CD;  P6=00CDCD;  P7=E5E5E5;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  87)        P8=7F7F7F;  P9=FF0000; P10=00FF00; P11=FFFF00;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  88)       P12=5C5CFF; P13=FF00FF; P14=00FFFF; P15=FFFFFF;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  89)       return;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  90)     else # linux console
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  91)        P0=000000;  P1=AA0000;  P2=00AA00;  P3=AA5500;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  92)        P4=0000AA;  P5=AA00AA;  P6=00AAAA;  P7=AAAAAA;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  93)        P8=555555;  P9=FF5555; P10=55FF55; P11=FFFF55;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  94)       P12=5555FF; P13=FF55FF; P14=55FFFF; P15=FFFFFF;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  95)       [ "$1" = "--palette=linux" ] && return;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  96)     fi
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  97) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  98) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200  99) processArg #defaults
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 100) for var in "$@"; do processArg $var; done
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 101) [ "$css_only" ] && [ "$body_only" ] && usage
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 102) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 103) # Mac OSX's GNU sed is installed as gsed
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 104) # use e.g. homebrew 'gnu-sed' to get it
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 105) if ! sed --version >/dev/null 2>&1; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 106)   if gsed --version >/dev/null 2>&1; then
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 107)     alias sed=gsed
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 108)   else
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 109)     echo "Error, can't find an acceptable GNU sed." >&2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 110)     exit 1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 111)   fi
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 112) fi
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 113) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 114) [ "$css_only" ] || [ "$body_only" ] || printf '%s' "<html>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 115) <head>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 116) <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 117) <style type=\"text/css\">
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 118) pre { white-space: pre-wrap; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 119) "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 120) [ "$body_only" ] || printf ".ef0,.f0 { color: #$P0; } .eb0,.b0 { background-color: #$P0; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 121) .ef1,.f1 { color: #$P1; } .eb1,.b1 { background-color: #$P1; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 122) .ef2,.f2 { color: #$P2; } .eb2,.b2 { background-color: #$P2; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 123) .ef3,.f3 { color: #$P3; } .eb3,.b3 { background-color: #$P3; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 124) .ef4,.f4 { color: #$P4; } .eb4,.b4 { background-color: #$P4; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 125) .ef5,.f5 { color: #$P5; } .eb5,.b5 { background-color: #$P5; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 126) .ef6,.f6 { color: #$P6; } .eb6,.b6 { background-color: #$P6; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 127) .ef7,.f7 { color: #$P7; } .eb7,.b7 { background-color: #$P7; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 128) .ef8, .f0 > .bold,.bold > .f0 { color: #$P8; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 129) .ef9, .f1 > .bold,.bold > .f1 { color: #$P9; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 130) .ef10,.f2 > .bold,.bold > .f2 { color: #$P10; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 131) .ef11,.f3 > .bold,.bold > .f3 { color: #$P11; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 132) .ef12,.f4 > .bold,.bold > .f4 { color: #$P12; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 133) .ef13,.f5 > .bold,.bold > .f5 { color: #$P13; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 134) .ef14,.f6 > .bold,.bold > .f6 { color: #$P14; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 135) .ef15,.f7 > .bold,.bold > .f7 { color: #$P15; font-weight: normal; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 136) .eb8  { background-color: #$P8; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 137) .eb9  { background-color: #$P9; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 138) .eb10 { background-color: #$P10; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 139) .eb11 { background-color: #$P11; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 140) .eb12 { background-color: #$P12; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 141) .eb13 { background-color: #$P13; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 142) .eb14 { background-color: #$P14; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 143) .eb15 { background-color: #$P15; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 144) "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 145) # The default xterm 256 colour palette
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 146) for red in 0 1 2 3 4 5 ; do
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 147)   for green in 0 1 2 3 4 5 ; do
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 148)     for blue in 0 1 2 3 4 5 ; do
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 149)       c=$((16 + ($red * 36) + ($green * 6) + $blue))
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 150)       r=$((($red * 40 + 55) * ($red > 0)))
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 151)       g=$((($green * 40 + 55) * ($green > 0)))
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 152)       b=$((($blue * 40 + 55) * ($blue > 0)))
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 153)       [ "$body_only" ] || printf ".ef%d { color: #%2.2x%2.2x%2.2x; } " $c $r $g $b
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 154)       [ "$body_only" ] || printf ".eb%d { background-color: #%2.2x%2.2x%2.2x; }\n" $c $r $g $b
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 155)     done
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 156)   done
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 157) done
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 158) for gray in $(seq 0 23); do
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 159)   c=$(($gray+232))
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 160)   l=$(($gray*10 + 8))
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 161)   [ "$body_only" ] || printf ".ef%d { color: #%2.2x%2.2x%2.2x; } " $c $l $l $l
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 162)   [ "$body_only" ] || printf ".eb%d { background-color: #%2.2x%2.2x%2.2x; }\n" $c $l $l $l
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 163) done
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 164) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 165) [ "$body_only" ] || printf '%s' '
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 166) .f9 { color: '`[ "$dark_bg" ] && printf "#$P7;" || printf "#$P0;"`' }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 167) .b9 { background-color: #'`[ "$dark_bg" ] && printf $P0 || printf $P15`'; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 168) .f9 > .bold,.bold > .f9, body.f9 > pre > .bold {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 169)   /* Bold is heavy black on white, or bright white
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 170)      depending on the default background */
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 171)   color: '`[ "$dark_bg" ] && printf "#$P15;" || printf "#$P0;"`'
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 172)   font-weight: '`[ "$dark_bg" ] && printf 'normal;' || printf 'bold;'`'
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 173) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 174) .reverse {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 175)   /* CSS does not support swapping fg and bg colours unfortunately,
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 176)      so just hardcode something that will look OK on all backgrounds. */
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 177)   '"color: #$P0; background-color: #$P7;"'
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 178) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 179) .underline { text-decoration: underline; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 180) .line-through { text-decoration: line-through; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 181) .blink { text-decoration: blink; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 182) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 183) /* Avoid pixels between adjacent span elements.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 184)    Note this only works for lines less than 80 chars
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 185)    where we close span elements on the same line.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 186) span { display: inline-block; }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 187) */
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 188) '
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 189) [ "$body_only" ] || [ "$css_only" ] && printf '%s\n' \
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 190) 'To use the css generated from --css-only, do: '\
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 191) '<head><link rel="stylesheet" type="text/css" href="style.css"></head>' >&2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 192) [ "$css_only" ] && exit
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 193) [ "$body_only" ] || printf '%s' '</style>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 194) </head>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 195) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 196) <body class="f9 b9">
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 197) <pre>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 198) '
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 199) [ "$body_only" ] && printf '%s\n' 'Be sure to use <body class="f9 b9"> and <pre>' >&2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 200) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 201) p='\x1b\['        #shortcut to match escape codes
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 202) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 203) # Handle various xterm control sequences.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 204) # See /usr/share/doc/xterm-*/ctlseqs.txt
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 205) sed "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 206) # escape ampersand and quote
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 207) s#&#\&amp;#g; s#\"#\&quot;#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 208) s#\x1b[^\x1b]*\x1b\\\##g  # strip anything between \e and ST
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 209) s#\x1b][0-9]*;[^\a]*\a##g # strip any OSC (xterm title etc.)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 210) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 211) s#\r\$## # strip trailing \r
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 212) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 213) # strip other non SGR escape sequences
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 214) s#[\x07]##g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 215) s#\x1b[]>=\][0-9;]*##g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 216) s#\x1bP+.\{5\}##g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 217) # Mark cursor positioning codes \"Jr;c;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 218) s#${p}\([0-9]\{1,2\}\)G#\"J;\1;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 219) s#${p}\([0-9]\{1,2\}\);\([0-9]\{1,2\}\)H#\"J\1;\2;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 220) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 221) # Mark clear as \"Cn where n=1 is screen and n=0 is to end-of-line
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 222) s#${p}H#\"C1;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 223) s#${p}K#\"C0;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 224) # Mark Cursor move columns as \"Mn where n is +ve for right, -ve for left
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 225) s#${p}C#\"M1;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 226) s#${p}\([0-9]\{1,\}\)C#\"M\1;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 227) s#${p}\([0-9]\{1,\}\)D#\"M-\1;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 228) s#${p}\([0-9]\{1,\}\)P#\"X\1;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 229) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 230) s#${p}[0-9;?]*[^0-9;?m]##g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 231) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 232) " |
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 233) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 234) # Normalize the input before transformation
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 235) sed "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 236) # escape HTML (ampersand and quote done above)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 237) s#>#\&gt;#g; s#<#\&lt;#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 238) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 239) # handle truecolor
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 240) s#${p}38;2;\([0-9]\{1,3\}\);\([0-9]\{1,3\}\);\([0-9]\{1,3\}\)m#\
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 241) <span style=\"color:rgb(\1\,\2\,\3\)\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 242) s#${p}48;2;\([0-9]\{1,3\}\);\([0-9]\{1,3\}\);\([0-9]\{1,3\}\)m#\
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 243) <span style=\"background-color:rgb(\1\,\2\,\3\)\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 244) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 245) # normalize SGR codes a little
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 246) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 247) # split 256 colors out and mark so that they're not
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 248) # recognised by the following 'split combined' line
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 249) :e
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 250) s#${p}\([0-9;]\{1,\}\);\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m${p}¬\2m#g; t e
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 251) s#${p}\([34]8;5;[0-9]\{1,3\}\)m#${p}¬\1m#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 252) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 253) :c
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 254) s#${p}\([0-9]\{1,\}\);\([0-9;]\{1,\}\)m#${p}\1m${p}\2m#g; t c   # split combined
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 255) s#${p}0\([0-7]\)#${p}\1#g                                 #strip leading 0
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 256) s#${p}1m\(\(${p}[4579]m\)*\)#\1${p}1m#g                   #bold last (with clr)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 257) s#${p}m#${p}0m#g                                          #add leading 0 to norm
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 258) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 259) # undo any 256 color marking
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 260) s#${p}¬\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 261) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 262) # map 16 color codes to color + bold
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 263) s#${p}9\([0-7]\)m#${p}3\1m${p}1m#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 264) s#${p}10\([0-7]\)m#${p}4\1m${p}1m#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 265) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 266) # change 'reset' code to \"R
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 267) s#${p}0m#\"R;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 268) " |
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 269) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 270) # Convert SGR sequences to HTML
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 271) sed "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 272) # common combinations to minimise html (optional)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 273) :f
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 274) s#${p}3[0-7]m${p}3\([0-7]\)m#${p}3\1m#g; t f
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 275) :b
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 276) s#${p}4[0-7]m${p}4\([0-7]\)m#${p}4\1m#g; t b
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 277) s#${p}3\([0-7]\)m${p}4\([0-7]\)m#<span class=\"f\1 b\2\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 278) s#${p}4\([0-7]\)m${p}3\([0-7]\)m#<span class=\"f\2 b\1\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 279) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 280) s#${p}1m#<span class=\"bold\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 281) s#${p}4m#<span class=\"underline\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 282) s#${p}5m#<span class=\"blink\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 283) s#${p}7m#<span class=\"reverse\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 284) s#${p}9m#<span class=\"line-through\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 285) s#${p}3\([0-9]\)m#<span class=\"f\1\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 286) s#${p}4\([0-9]\)m#<span class=\"b\1\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 287) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 288) s#${p}38;5;\([0-9]\{1,3\}\)m#<span class=\"ef\1\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 289) s#${p}48;5;\([0-9]\{1,3\}\)m#<span class=\"eb\1\">#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 290) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 291) s#${p}[0-9;]*m##g # strip unhandled codes
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 292) " |
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 293) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 294) # Convert alternative character set and handle cursor movement codes
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 295) # Note we convert here, as if we do at start we have to worry about avoiding
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 296) # conversion of SGR codes etc., whereas doing here we only have to
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 297) # avoid conversions of stuff between &...; or <...>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 298) #
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 299) # Note we could use sed to do this based around:
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 300) #   sed 'y/abcdefghijklmnopqrstuvwxyz{}`~/▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·/'
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 301) # However that would be very awkward as we need to only conv some input.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 302) # The basic scheme that we do in the awk script below is:
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 303) #  1. enable transliterate once "T1; is seen
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 304) #  2. disable once "T0; is seen (may be on diff line)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 305) #  3. never transliterate between &; or <> chars
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 306) #  4. track x,y movements and active display mode at each position
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 307) #  5. buffer line/screen and dump when required
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 308) sed "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 309) # change 'smacs' and 'rmacs' to \"T1 and \"T0 to simplify matching.
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 310) s#\x1b(0#\"T1;#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 311) s#\x0E#\"T1;#g;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 312) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 313) s#\x1b(B#\"T0;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 314) s#\x0F#\"T0;#g
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 315) " |
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 316) (
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 317) gawk '
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 318) function dump_line(l,del,c,blanks,ret) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 319)   for(c=1;c<maxX;c++) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 320)     if ((c SUBSEP l) in attr || alength(cur)) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 321)       ret = ret blanks fixas(cur,attr[c,l])
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 322)       if(del) delete attr[c,l]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 323)       blanks=""
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 324)     }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 325)     if ((c SUBSEP l) in dump) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 326)       ret=ret blanks dump[c,l]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 327)       if(del) delete dump[c,l]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 328)       blanks=""
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 329)     } else blanks=blanks " "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 330)   }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 331)   if(alength(cur)) ret=ret blanks
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 332)   return ret
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 333) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 334) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 335) function dump_screen(l,ret) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 336)   for(l=1;l<=maxY;l++)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 337)     ret=ret dump_line(l,0) "\n"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 338)   return ret fixas(cur, "")
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 339) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 340) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 341) function atos(a,i,ret) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 342)   for(i=1;i<=alength(a);i++) if(i in a) ret=ret a[i]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 343)   return ret
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 344) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 345) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 346) function alength(a, i, k) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 347)     k = 0
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 348)     for(i in a) k++
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 349)     return k
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 350) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 351) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 352) function fixas(a,s,spc,i,attr,rm,ret) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 353)   spc=alength(a)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 354)   l=split(s,attr,">")
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 355)   for(i=1;i<=spc;i++) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 356)     rm=rm?rm:(a[i]!=attr[i]">")
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 357)     if(rm) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 358)       ret=ret "</span>"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 359)       delete a[i];
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 360)     }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 361)   }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 362)   for(i=1;i<l;i++) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 363)     attr[i]=attr[i]">"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 364)     if(a[i]!=attr[i]) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 365)       a[i]=attr[i]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 366)       ret = ret attr[i]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 367)     }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 368)   }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 369)   return ret
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 370) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 371) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 372) function encode(string,start,end,i,ret,pos,sc,buf) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 373)    if(!end) end=length(string);
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 374)    if(!start) start=1;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 375)    state=3
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 376)    for(i=1;i<=length(string);i++) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 377)      c=substr(string,i,1)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 378)      if(state==2) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 379)        sc=sc c
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 380)        if(c==";") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 381)           c=sc
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 382)           state=last_mode
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 383)        } else continue
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 384)      } else {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 385)        if(c=="\r") { x=1; continue }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 386)        if(c=="<") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 387)          # Change attributes - store current active
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 388)          # attributes in span array
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 389)          split(substr(string,i),cord,">");
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 390)          i+=length(cord[1])
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 391)          span[++spc]=cord[1] ">"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 392)          continue
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 393)        }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 394)        else if(c=="&") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 395)          # All goes to single position till we see a semicolon
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 396)          sc=c
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 397)          state=2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 398)          continue
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 399)        }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 400)        else if(c=="\b") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 401)           # backspace move insertion point back 1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 402)           if(spc) attr[x,y]=atos(span)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 403)           x=x>1?x-1:1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 404)           continue
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 405)        }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 406)        else if(c=="\"") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 407)           split(substr(string,i+2),cord,";")
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 408)           cc=substr(string,i+1,1);
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 409)           if(cc=="T") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 410)               # Transliterate on/off
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 411)               if(cord[1]==1&&state==3) last_mode=state=4
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 412)               if(cord[1]==0&&state==4) last_mode=state=3
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 413)           }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 414)           else if(cc=="C") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 415)               # Clear
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 416)               if(cord[1]+0) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 417)                 # Screen - if Recording dump screen
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 418)                 if(dumpStatus==dsActive) ret=ret dump_screen()
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 419)                 dumpStatus=dsActive
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 420)                 delete dump
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 421)                 delete attr
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 422)                 x=y=1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 423)               } else {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 424)                 # To end of line
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 425)                 for(pos=x;pos<maxX;pos++) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 426)                   dump[pos,y]=" "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 427)                   if (!spc) delete attr[pos,y]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 428)                   else attr[pos,y]=atos(span)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 429)                 }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 430)               }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 431)           }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 432)           else if(cc=="J") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 433)               # Jump to x,y
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 434)               i+=length(cord[2])+1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 435)               # If line is higher - dump previous screen
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 436)               if(dumpStatus==dsActive&&cord[1]<y) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 437)                 ret=ret dump_screen();
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 438)                 dumpStatus=dsNew;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 439)               }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 440)               x=cord[2]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 441)               if(length(cord[1]) && y!=cord[1]){
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 442)                 y=cord[1]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 443)                 if(y>maxY) maxY=y
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 444)                 # Change y - start recording
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 445)                 dumpStatus=dumpStatus?dumpStatus:dsReset
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 446)               }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 447)           }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 448)           else if(cc=="M") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 449)               # Move left/right on current line
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 450)               x+=cord[1]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 451)           }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 452)           else if(cc=="X") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 453)               # delete on right
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 454)               for(pos=x;pos<=maxX;pos++) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 455)                 nx=pos+cord[1]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 456)                 if(nx<maxX) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 457)                   if((nx SUBSEP y) in attr) attr[pos,y] = attr[nx,y]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 458)                   else delete attr[pos,y]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 459)                   if((nx SUBSEP y) in dump) dump[pos,y] = dump[nx,y]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 460)                   else delete dump[pos,y]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 461)                 } else if(spc) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 462)                   attr[pos,y]=atos(span)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 463)                   dump[pos,y]=" "
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 464)                 }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 465)               }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 466)           }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 467)           else if(cc=="R") {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 468)               # Reset attributes
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 469)               while(spc) delete span[spc--]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 470)           }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 471)           i+=length(cord[1])+2
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 472)           continue
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 473)        }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 474)        else if(state==4&&i>=start&&i<=end&&c in Trans) c=Trans[c]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 475)      }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 476)      if(dumpStatus==dsReset) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 477)        delete dump
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 478)        delete attr
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 479)        ret=ret"\n"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 480)        dumpStatus=dsActive
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 481)      }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 482)      if(dumpStatus==dsNew) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 483)        # After moving/clearing we are now ready to write
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 484)        # somthing to the screen so start recording now
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 485)        ret=ret"\n"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 486)        dumpStatus=dsActive
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 487)      }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 488)      if(dumpStatus==dsActive||dumpStatus==dsOff) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 489)        dump[x,y] = c
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 490)        if(!spc) delete attr[x,y]
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 491)        else attr[x,y] = atos(span)
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 492)        if(++x>maxX) maxX=x;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 493)      }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 494)     }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 495)     # End of line if dumping increment y and set x back to first col
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 496)     x=1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 497)     if(!dumpStatus) return ret dump_line(y,1);
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 498)     else if(++y>maxY) maxY=y;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 499)     return ret
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 500) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 501) BEGIN{
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 502)   OFS=FS
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 503)   # dump screen status
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 504)   dsOff=0    # Not dumping screen contents just write output direct
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 505)   dsNew=1    # Just after move/clear waiting for activity to start recording
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 506)   dsReset=2  # Screen cleared build new empty buffer and record
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 507)   dsActive=3 # Currently recording
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 508)   F="abcdefghijklmnopqrstuvwxyz{}`~"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 509)   T="▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·"
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 510)   maxX=80
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 511)   delete cur;
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 512)   x=y=1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 513)   for(i=1;i<=length(F);i++)Trans[substr(F,i,1)]=substr(T,i,1);
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 514) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 515) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 516) { $0=encode($0) }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 517) 1
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 518) END {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 519)   if(dumpStatus) {
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 520)     print dump_screen();
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 521)   }
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 522) }'
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 523) )
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 524) 
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 525) [ "$body_only" ] || printf '</pre>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 526) </body>
160604b1 (MoserMichael 2025-11-20 07:10:43 +0200 527) </html>\n'