chiark / gitweb /
Add Vivek's ctlseq script verbatim.
authorColin Watson <cjwatson@chiark.greenend.org.uk>
Fri, 17 Oct 2003 01:49:53 +0000 (01:49 +0000)
committerColin Watson <cjwatson@chiark.greenend.org.uk>
Fri, 17 Oct 2003 01:49:53 +0000 (01:49 +0000)
ctlseq [new file with mode: 0755]

diff --git a/ctlseq b/ctlseq
new file mode 100755 (executable)
index 0000000..267f794
--- /dev/null
+++ b/ctlseq
@@ -0,0 +1,334 @@
+#!/bin/sh
+
+# Last modified: Fri 2003-03-28 15:25:04 +0000
+# Copyright (C) 2003, Vivek Dasmohapatra <vivek@etla.org>
+
+declare paridx=0;
+declare -a OPT_p;
+
+# convert "^X" to a control char (A-_ supported):
+_ctrl ()
+{
+    local char="${1:1:1}";
+    #echo _ctrl "$char";
+    case $char in
+        A) asc=$'\x01' ;;
+        B) asc=$'\x02' ;;
+        C) asc=$'\x03' ;;
+        D) asc=$'\x04' ;;
+        E) asc=$'\x05' ;;
+        F) asc=$'\x06' ;;
+        G) asc=$'\x07' ;;
+        H) asc=$'\x08' ;;
+        I) asc=$'\x09' ;;
+        J) asc=$'\x0a' ;;
+        K) asc=$'\x0b' ;;
+        L) asc=$'\x0c' ;;
+        M) asc=$'\x0d' ;;
+        N) asc=$'\x0e' ;;
+        O) asc=$'\x0f' ;;
+        P) asc=$'\x10' ;;
+        Q) asc=$'\x11' ;;
+        R) asc=$'\x12' ;;
+        S) asc=$'\x13' ;;
+        T) asc=$'\x14' ;;
+        U) asc=$'\x15' ;;
+        V) asc=$'\x16' ;;
+        W) asc=$'\x17' ;;
+        X) asc=$'\x18' ;;
+        Y) asc=$'\x19' ;;
+        Z) asc=$'\x1a' ;;
+        [) asc=$'\x1b' ;;
+       \\) asc=$'\x1c' ;;
+        ]) asc=$'\x1d' ;;
+        ^) asc=$'\x1e' ;;
+        _) asc=$'\x1f' ;;
+        *) asc=""      ;;
+    esac;
+}
+
+# decode a symbol, or just return unaltered if no match found:
+_decode ()
+{
+    local \
+        asc=""    \
+        symbol="" \
+        ctlbuf="" ;
+
+    ctlstr="";
+    #echo _decode "'$*'" 1>&2;
+
+    for symbol in "$@";
+    do
+      case "$symbol" in
+          S7C1T)  symbol="ESC SP F" ;; # 7 bit controls
+          S8C1T)  symbol="ESC SP G" ;; # 8 bit controls
+          DECDHLU)symbol="ESC # 3"  ;; # double height, upper half
+          DECDHLL)symbol="ESC # 4"  ;; # double height, lower half
+          DECSWL) symbol="ESC # 5"  ;; # single width
+          DECDWL) symbol="ESC # 6"  ;; # double width
+          DECALN) symbol="ESC # 8"  ;; # alignment test
+          DECSC)  symbol="ESC 7"    ;; # save cursor
+          DECRC)  symbol="ESC 8"    ;; # restore cursor
+          DECPAM) symbol="ESC ="    ;; # app keypad
+          DECPNM) symbol="ESC >"    ;; # normal keypad
+          RIS)    symbol="ESC c"    ;; # full reset
+          ESC)    symbol="^["       ;; # esc
+          IND)    symbol="ESC D"    ;; # index
+          NEL)    symbol="ESC E"    ;; # next line
+          HTS)    symbol="ESC H"    ;; # set tab stop
+          RI)     symbol="ESC M"    ;; # Reverse Index
+          SS2)    symbol="ESC N"    ;; # Single Shift G2 charset next char only
+          SS3)    symbol="ESC O"    ;; # Single Shift G3 charset next char only
+          DCS)    symbol="ESC P"    ;; # Device Control String
+          SPA)    symbol="ESC V"    ;; # Start Protected Area
+          EPA)    symbol="ESC W"    ;; # End Protected Area
+          SOS)    symbol="ESC X"    ;; # Start Of String
+          DECID)  symbol="ESC Z"    ;; # terminal id (obs, use CSI Ps c)
+          CSI)    symbol="ESC ["    ;; # Control Sequence Introduction
+          ST)     symbol="ESC \\"   ;;# String Terminator (new, was BEL)
+          OSC)    symbol="ESC ]"    ;; # Operating System Command
+          PM)     symbol="ESC ^"    ;; # Privacy Message
+          APC)    symbol="ESC _"    ;; # Application Program Command
+          BEL)    symbol="^G"       ;; # beep
+          BS)     symbol="^H"       ;; # backspace
+          CR)     symbol="^M"       ;; # Carriage Return (beginning of line)
+          ENQ)    symbol="^E"       ;; # terminal stats. usu responds "xterm"
+          FF)     symbol="^L"       ;; # Form Feed (clear/redraw screen)
+          LF)     symbol="^J"       ;; # Line Feed
+          SO)     symbol="^N"       ;; # Shift Out : G1 character set
+          TAB)    symbol="^I"       ;; # Tab
+          VT)     symbol="^K"       ;; # same as LF
+          SI)     symbol="^O"       ;; # Shift In : G0 (default) character set
+          ^?)     _ctrl "$symbol"; symbol="$asc" ;; # ^X ctrl notation
+         SET_WTITLE) symbol="OSC 2 ; %p ST" ;;
+          SET_ITITLE) symbol="OSC 1 ; %p ST" ;;
+          SET_TITLES) symbol="OSC 0 ; %p ST" ;;
+          SET_XPROP)  symbol="OSC 3 ; %p ST" ;;
+          GET_WTITLE) symbol="CSI 2 1 t"     ;;
+          GET_ITITLE) symbol="CSI 2 0 t"     ;;
+      esac;
+      ctlbuf="${ctlbuf}${ctlbuf:+ }${symbol}";
+    done;
+
+    ctlstr="${ctlbuf}";
+}
+
+usage ()
+{
+    
+    cat <<EOF | ${PAGER:-cat}
+usage:
+  $0 [-r] [-R] [-p arg0 [-p arg1]...] <SYMBOLIC SEQUENCE...>
+
+  This tool translates a sequence of symbols and strings into a
+  corresponding terminal escape sequence, which it then prints
+  on stdout.
+
+  If the -r flag is supplied, then it sends the escape sequence
+  to the tty ( via '/dev/tty' ), reads the terminal's response,
+  trims some leading and trailing marker characters, and prints
+  the result on stdout instead (unless -R was specified, in which
+  case you get the raw response sequence).
+
+  Note that the raw response generally begins with a control string
+  that renders it invisible when printed, so it's a good idea to
+  capture it with a RSP=\$($0 -r -R SEQUENCE) type construct.
+
+  The sequence will have all the spaces stripped out of it, except
+  those explicitly passed as the 'SP' symbol.
+
+  $0 knows about the following symbols:
+  
+      S7C1T      7 bit control sequences
+      S8C1T      8 bit control sequences
+      DECDHLU    double height line, upper half
+      DECDHLL    double height line, lower half
+      DECSWL     single width
+      DECDWL     double width
+      DECALN     alignment test
+      DECSC      save cursor
+      DECRC      restore cursor
+      DECPAM     app keypad
+      DECPNM     normal keypad
+      RIS        full reset
+      ESC        esc
+      IND        index
+      NEL        next line
+      HTS        set tab stop
+      RI         Reverse Index
+      SS2        Single Shift G2 charset: next char only
+      SS3        Single Shift G3 charset: next char only
+      DCS        Device Control String
+      SPA        Start Protected Area
+      EPA        End Protected Area
+      SOS        Start Of String
+      DECID      terminal id (obsolete: use CSI Ps c)
+      CSI        Control Sequence Introduction
+      ST         String Terminator
+      OSC        Operating System Command
+      PM         Privacy Message
+      APC        Application Program Command
+      BEL        beep
+      BS         backspace
+      CR         Carriage Return (beginning of line)
+      ENQ        terminal status
+      FF         Form Feed (clear/redraw screen)
+      LF         Line Feed
+      SO         Shift Out : G1 character set
+      SP         space
+      TAB        Tab
+      VT         same as LF
+      SI         Shift In : G0 (default) character set
+      ^A ... ^_  control characters 0x01 through 0x1f
+
+  The following high-level sequences have been defined for convenience:
+
+      GET_WTITLE    fetch the window title
+      GET_ITITLE    fetch the icon title
+
+  Additionally, we support parametrised symbolic sequences:
+  Each '%p' in a sequence will be replaced with successive 
+  values of the -p flag.
+
+  The following parametrised sequences are built in:
+
+      SET_WTITLE    (1 arg)    Set the window title
+      SET_ITITLE    (1 arg)    Set the icon title
+      SET_TITLES    (1 arg)    Set both the above
+      SET_XPROP     (1 arg)    Set (prop=value) or zap (prop) an X property
+
+  The escape sequences are detailed here:
+
+      http://rtfm.etla.org/xterm/ctlseq.html
+
+  Example:
+
+      # sets the title bar to "Xterm Title"
+      $0 OSC 0 \; Xterm SP Title ST
+
+      # or:
+      $0 -p "Xterm SP Title" SET_WTITLE
+      
+      # stores the (sanitised) title response in \$TSTR
+      TSTR=\$($0 CSI 2 1 t)
+
+      # or:
+      TSTR=\$($0 GET_WTITLE)
+EOF
+}
+
+_param ()
+{
+    local curidx=0;
+
+    while [ 1 -eq 1 ];
+    do
+      if [ $paridx -eq $curidx ];
+      then
+         par=${OPT_p[$curidx]};
+         paridx=$(($paridx + 1));
+         #echo "retrieving p[$curidx] = $par";
+         break;
+      else
+         curidx=$(($curidx + 1));
+      fi;
+    done;
+}
+
+decode ()
+{
+    local\
+        ctlstr="" \
+        sym=""    \
+        seqbuf="" ;
+    
+    for sym in "$@";
+    do
+      while [ "1" = "1" ];
+      do
+        _decode $sym; 
+        if [ "X$ctlstr" = "X$sym" ]; then break; else sym="$ctlstr"; fi;
+      done;
+      sequence="${sequence}${sequence:+ }${sym}";
+      #echo -n sequence=                               1>&2; 
+      #echo    "$sequence" | sed -e 's@[^A-Z0-9_]@.@g' 1>&2; 
+    done;
+
+    for sym in $sequence;
+    do
+      if [ "X$sym" = "XSP" ]; then sym=" ";             fi;
+      if [ "X$sym" = "X%p" ]; then _param ; sym="$par"; fi;
+      seqbuf="${seqbuf}${sym}";
+    done;
+
+    sequence="$seqbuf";
+}
+
+ctl_read ()
+{
+    local \
+       old=""      \
+        cseq="$*"   \
+        response="" \
+        sequence="" ;
+
+    exec </dev/tty;              # make sure stdin is a tty
+    old="$(stty -g)";            # store the stty settings
+    stty raw -echo min 0 time 1; # black magic
+    echo -n "$cseq" > /dev/tty;  # feed the control sequence to the tty
+    IFS='' read -r response;     # read the response
+    stty "$old";                 # reset th tty to its saved state
+
+    if [ -z "$OPT_R" ] && [ -n "$response" ];
+    then
+        local \
+            OSC=""                \
+            CSI=""                \
+            rlen=${#response}     \
+            start=${response:0:2} ;
+        
+        decode "OSC"; OSC="$sequence";
+        decode "CSI"; CSI="$sequence";
+        
+        if   [ "$start" = "$OSC" ]; then response=${response:3:$(($rlen-5))};
+        elif [ "$start" = "$CSI" ]; then response=${response:3:$(($rlen-4))};
+        fi;
+    fi;
+    
+    echo -n "$response";         # echo the response string
+}
+
+main ()
+{
+    local \
+        name=""   \
+        setarg="" ;
+    while getopts rhp: n;
+    do
+      case $n in
+         p)
+             OPT_p[$paridx]="${OPTARG:-$paridx}";
+             paridx=$(($paridx + 1));
+             ;;
+         *)
+             setarg="OPT_${n}=\"\${OPT_${n}}\${OPT_$n:+ }${OPTARG:-1}\"";
+             #echo $setarg 1>&2;
+             eval $setarg;
+             ;;
+      esac;
+    done;
+    paridx=0;
+    shift $(($OPTIND-1));
+
+    #echo "\$OPT_p='$OPT_p'";
+
+    if [ $# -eq 0 ] || [ -n "$OPT_h" ]; then usage; exit 0; fi;
+    
+    decode "$@";
+    
+    if [ -n "$OPT_r" ]; then ctl_read $sequence; else echo -n $sequence; fi;
+}
+
+main "$@";
+