What do these strings, 'M^?' and '^M?', represent in zsh/ZLE?
In the documentation for the Zsh Line Editor, there is a section that says:
For either in-string or out-string, the following escape sequences are recognised:
a
bell character
b
backspace
e, E
escape
f
form feed
n
linefeed (newline)
r
carriage return
t
horizontal tab
v
vertical tab
NNN
character code in octal
xNN
character code in hexadecimal
uNNNN
unicode character code in hexadecimal
UNNNNNNNN
unicode character code in hexadecimal
M[-]X
character with meta bit set
C[-]X
control character
^X
control character
In all other cases, ‘’ escapes the following character. Delete is written as ‘^?’. Note that ‘M^?’ and ‘^M?’ are not the same...
How should those last two sequences be interpreted? My guess is:
M^? - delete with the meta bit set?
^M? - control + question mark with the meta bit set
Is this correct?
^?
is the byte 127 = 0x7f, which is commonly sent by the Backspace key (unless it’s set to send ^H
and the Delete key is set to ^?
).
M^?
or M-^?
is the same but with the upper bit set, i.e. 255 = 0xff. On modern systems, non-ASCII characters are encoded in UTF-8. On some ancient systems, or on modern systems with some backward compatibility settings designed for ASCII-only input, typing an ASCII character while holding Meta sends the corresponding byte with the upper bit set. If your terminal does that and it sends ^?
for Ctrl+?, you should be able to input this byte with Meta+Ctrl+?.
% bindkey '^M?' wibble
% bindkey | grep wibble
"M-^_" wibble
^M?
is parsed as controlifying M?
, which is metaifying ?
, i.e. setting the upper bit (bit 7) in ?
. ?
is 0x3f = 0b00111111 so M?
is the byte 0xcf = 0b10111111. Controlifying apparently sets bits 5 and 6 to 0 for every character except ?
, for which it changes the value to 0x7f. Thus ^M?
ends up being 0x9f = 0b10011111, which is normally written M^_
(set the upper bit of ^_
). That’s not useful behavior, it’s just an edge case in the implementation.