chiark / gitweb /
hacky LP bug support
[bin.git] / ctlseq
1 #!/bin/sh
2
3 # Last modified: Fri 2003-03-28 15:25:04 +0000
4 # Copyright (C) 2003, Vivek Dasmohapatra <vivek@etla.org>
5
6 declare paridx=0;
7 declare -a OPT_p;
8
9 # convert "^X" to a control char (A-_ supported):
10 _ctrl ()
11 {
12     local char="${1:1:1}";
13     #echo _ctrl "$char";
14     case $char in
15         A) asc=$'\x01' ;;
16         B) asc=$'\x02' ;;
17         C) asc=$'\x03' ;;
18         D) asc=$'\x04' ;;
19         E) asc=$'\x05' ;;
20         F) asc=$'\x06' ;;
21         G) asc=$'\x07' ;;
22         H) asc=$'\x08' ;;
23         I) asc=$'\x09' ;;
24         J) asc=$'\x0a' ;;
25         K) asc=$'\x0b' ;;
26         L) asc=$'\x0c' ;;
27         M) asc=$'\x0d' ;;
28         N) asc=$'\x0e' ;;
29         O) asc=$'\x0f' ;;
30         P) asc=$'\x10' ;;
31         Q) asc=$'\x11' ;;
32         R) asc=$'\x12' ;;
33         S) asc=$'\x13' ;;
34         T) asc=$'\x14' ;;
35         U) asc=$'\x15' ;;
36         V) asc=$'\x16' ;;
37         W) asc=$'\x17' ;;
38         X) asc=$'\x18' ;;
39         Y) asc=$'\x19' ;;
40         Z) asc=$'\x1a' ;;
41         [) asc=$'\x1b' ;;
42        \\) asc=$'\x1c' ;;
43         ]) asc=$'\x1d' ;;
44         ^) asc=$'\x1e' ;;
45         _) asc=$'\x1f' ;;
46         *) asc=""      ;;
47     esac;
48 }
49
50 # decode a symbol, or just return unaltered if no match found:
51 _decode ()
52 {
53     local \
54         asc=""    \
55         symbol="" \
56         ctlbuf="" ;
57
58     ctlstr="";
59     #echo _decode "'$*'" 1>&2;
60
61     for symbol in "$@";
62     do
63       case "$symbol" in
64           S7C1T)  symbol="ESC SP F" ;; # 7 bit controls
65           S8C1T)  symbol="ESC SP G" ;; # 8 bit controls
66           DECDHLU)symbol="ESC # 3"  ;; # double height, upper half
67           DECDHLL)symbol="ESC # 4"  ;; # double height, lower half
68           DECSWL) symbol="ESC # 5"  ;; # single width
69           DECDWL) symbol="ESC # 6"  ;; # double width
70           DECALN) symbol="ESC # 8"  ;; # alignment test
71           DECSC)  symbol="ESC 7"    ;; # save cursor
72           DECRC)  symbol="ESC 8"    ;; # restore cursor
73           DECPAM) symbol="ESC ="    ;; # app keypad
74           DECPNM) symbol="ESC >"    ;; # normal keypad
75           RIS)    symbol="ESC c"    ;; # full reset
76           ESC)    symbol="^["       ;; # esc
77           IND)    symbol="ESC D"    ;; # index
78           NEL)    symbol="ESC E"    ;; # next line
79           HTS)    symbol="ESC H"    ;; # set tab stop
80           RI)     symbol="ESC M"    ;; # Reverse Index
81           SS2)    symbol="ESC N"    ;; # Single Shift G2 charset next char only
82           SS3)    symbol="ESC O"    ;; # Single Shift G3 charset next char only
83           DCS)    symbol="ESC P"    ;; # Device Control String
84           SPA)    symbol="ESC V"    ;; # Start Protected Area
85           EPA)    symbol="ESC W"    ;; # End Protected Area
86           SOS)    symbol="ESC X"    ;; # Start Of String
87           DECID)  symbol="ESC Z"    ;; # terminal id (obs, use CSI Ps c)
88           CSI)    symbol="ESC ["    ;; # Control Sequence Introduction
89           ST)     symbol="ESC \\"   ;;# String Terminator (new, was BEL)
90           OSC)    symbol="ESC ]"    ;; # Operating System Command
91           PM)     symbol="ESC ^"    ;; # Privacy Message
92           APC)    symbol="ESC _"    ;; # Application Program Command
93           BEL)    symbol="^G"       ;; # beep
94           BS)     symbol="^H"       ;; # backspace
95           CR)     symbol="^M"       ;; # Carriage Return (beginning of line)
96           ENQ)    symbol="^E"       ;; # terminal stats. usu responds "xterm"
97           FF)     symbol="^L"       ;; # Form Feed (clear/redraw screen)
98           LF)     symbol="^J"       ;; # Line Feed
99           SO)     symbol="^N"       ;; # Shift Out : G1 character set
100           TAB)    symbol="^I"       ;; # Tab
101           VT)     symbol="^K"       ;; # same as LF
102           SI)     symbol="^O"       ;; # Shift In : G0 (default) character set
103           ^?)     _ctrl "$symbol"; symbol="$asc" ;; # ^X ctrl notation
104           SET_WTITLE) symbol="OSC 2 ; %p ST" ;;
105           SET_ITITLE) symbol="OSC 1 ; %p ST" ;;
106           SET_TITLES) symbol="OSC 0 ; %p ST" ;;
107           SET_XPROP)  symbol="OSC 3 ; %p ST" ;;
108           GET_WTITLE) symbol="CSI 2 1 t"     ;;
109           GET_ITITLE) symbol="CSI 2 0 t"     ;;
110       esac;
111       ctlbuf="${ctlbuf}${ctlbuf:+ }${symbol}";
112     done;
113
114     ctlstr="${ctlbuf}";
115 }
116
117 usage ()
118 {
119     
120     cat <<EOF | ${PAGER:-cat}
121 usage:
122   $0 [-r] [-R] [-p arg0 [-p arg1]...] <SYMBOLIC SEQUENCE...>
123
124   This tool translates a sequence of symbols and strings into a
125   corresponding terminal escape sequence, which it then prints
126   on stdout.
127
128   If the -r flag is supplied, then it sends the escape sequence
129   to the tty ( via '/dev/tty' ), reads the terminal's response,
130   trims some leading and trailing marker characters, and prints
131   the result on stdout instead (unless -R was specified, in which
132   case you get the raw response sequence).
133
134   Note that the raw response generally begins with a control string
135   that renders it invisible when printed, so it's a good idea to
136   capture it with a RSP=\$($0 -r -R SEQUENCE) type construct.
137
138   The sequence will have all the spaces stripped out of it, except
139   those explicitly passed as the 'SP' symbol.
140
141   $0 knows about the following symbols:
142   
143       S7C1T      7 bit control sequences
144       S8C1T      8 bit control sequences
145       DECDHLU    double height line, upper half
146       DECDHLL    double height line, lower half
147       DECSWL     single width
148       DECDWL     double width
149       DECALN     alignment test
150       DECSC      save cursor
151       DECRC      restore cursor
152       DECPAM     app keypad
153       DECPNM     normal keypad
154       RIS        full reset
155       ESC        esc
156       IND        index
157       NEL        next line
158       HTS        set tab stop
159       RI         Reverse Index
160       SS2        Single Shift G2 charset: next char only
161       SS3        Single Shift G3 charset: next char only
162       DCS        Device Control String
163       SPA        Start Protected Area
164       EPA        End Protected Area
165       SOS        Start Of String
166       DECID      terminal id (obsolete: use CSI Ps c)
167       CSI        Control Sequence Introduction
168       ST         String Terminator
169       OSC        Operating System Command
170       PM         Privacy Message
171       APC        Application Program Command
172       BEL        beep
173       BS         backspace
174       CR         Carriage Return (beginning of line)
175       ENQ        terminal status
176       FF         Form Feed (clear/redraw screen)
177       LF         Line Feed
178       SO         Shift Out : G1 character set
179       SP         space
180       TAB        Tab
181       VT         same as LF
182       SI         Shift In : G0 (default) character set
183       ^A ... ^_  control characters 0x01 through 0x1f
184
185   The following high-level sequences have been defined for convenience:
186
187       GET_WTITLE    fetch the window title
188       GET_ITITLE    fetch the icon title
189
190   Additionally, we support parametrised symbolic sequences:
191   Each '%p' in a sequence will be replaced with successive 
192   values of the -p flag.
193
194   The following parametrised sequences are built in:
195
196       SET_WTITLE    (1 arg)    Set the window title
197       SET_ITITLE    (1 arg)    Set the icon title
198       SET_TITLES    (1 arg)    Set both the above
199       SET_XPROP     (1 arg)    Set (prop=value) or zap (prop) an X property
200
201   The escape sequences are detailed here:
202
203       http://rtfm.etla.org/xterm/ctlseq.html
204
205   Example:
206
207       # sets the title bar to "Xterm Title"
208       $0 OSC 0 \; Xterm SP Title ST
209
210       # or:
211       $0 -p "Xterm SP Title" SET_WTITLE
212       
213       # stores the (sanitised) title response in \$TSTR
214       TSTR=\$($0 CSI 2 1 t)
215
216       # or:
217       TSTR=\$($0 GET_WTITLE)
218 EOF
219 }
220
221 _param ()
222 {
223     local curidx=0;
224
225     while [ 1 -eq 1 ];
226     do
227       if [ $paridx -eq $curidx ];
228       then
229           par=${OPT_p[$curidx]};
230           paridx=$(($paridx + 1));
231           #echo "retrieving p[$curidx] = $par";
232           break;
233       else
234           curidx=$(($curidx + 1));
235       fi;
236     done;
237 }
238
239 decode ()
240 {
241     local\
242         ctlstr="" \
243         sym=""    \
244         seqbuf="" ;
245     
246     for sym in "$@";
247     do
248       while [ "1" = "1" ];
249       do
250         _decode $sym; 
251         if [ "X$ctlstr" = "X$sym" ]; then break; else sym="$ctlstr"; fi;
252       done;
253       sequence="${sequence}${sequence:+ }${sym}";
254       #echo -n sequence=                               1>&2; 
255       #echo    "$sequence" | sed -e 's@[^A-Z0-9_]@.@g' 1>&2; 
256     done;
257
258     for sym in $sequence;
259     do
260       if [ "X$sym" = "XSP" ]; then sym=" ";             fi;
261       if [ "X$sym" = "X%p" ]; then _param ; sym="$par"; fi;
262       seqbuf="${seqbuf}${sym}";
263     done;
264
265     sequence="$seqbuf";
266 }
267
268 ctl_read ()
269 {
270     local \
271         old=""      \
272         cseq="$*"   \
273         response="" \
274         sequence="" ;
275
276     exec </dev/tty;              # make sure stdin is a tty
277     old="$(stty -g)";            # store the stty settings
278     stty raw -echo min 0 time 1; # black magic
279     echo -n "$cseq" > /dev/tty;  # feed the control sequence to the tty
280     IFS='' read -r response;     # read the response
281     stty "$old";                 # reset th tty to its saved state
282
283     if [ -z "$OPT_R" ] && [ -n "$response" ];
284     then
285         local \
286             OSC=""                \
287             CSI=""                \
288             rlen=${#response}     \
289             start=${response:0:2} ;
290         
291         decode "OSC"; OSC="$sequence";
292         decode "CSI"; CSI="$sequence";
293         
294         if   [ "$start" = "$OSC" ]; then response=${response:3:$(($rlen-5))};
295         elif [ "$start" = "$CSI" ]; then response=${response:3:$(($rlen-4))};
296         fi;
297     fi;
298     
299     echo -n "$response";         # echo the response string
300 }
301
302 main ()
303 {
304     local \
305         name=""   \
306         setarg="" ;
307     while getopts rRhp: n;
308     do
309       case $n in
310           p)
311               OPT_p[$paridx]="${OPTARG:-$paridx}";
312               paridx=$(($paridx + 1));
313               ;;
314           *)
315               setarg="OPT_${n}=\"\${OPT_${n}}\${OPT_$n:+ }${OPTARG:-1}\"";
316               #echo $setarg 1>&2;
317               eval $setarg;
318               ;;
319       esac;
320     done;
321     paridx=0;
322     shift $(($OPTIND-1));
323
324     #echo "\$OPT_p='$OPT_p'";
325
326     if [ $# -eq 0 ] || [ -n "$OPT_h" ]; then usage; exit 0; fi;
327     
328     decode "$@";
329     
330     if [ -n "$OPT_r" ]; then ctl_read $sequence; else echo -n $sequence; fi;
331 }
332
333 main "$@";
334