Linux Backspace/Delete mini-HOWTO

Sebastiano Vigna

vigna@acm.org
Revision History                                                             
Revision v1.6                  19 Jan 2002                                   
Included many comments from Alex Boldt and Chung-Rui Kao.                    
Revision v1.5                  3 May 2000                                    
Updated for new distros and the tput trick.                                  
Revision v1.4                  7 December 2000                               
Updated for Red Hat 7.0 and Helix Gnome conflicts.                           
Revision v1.3                  18 October 2000                               
Name change.                                                                 
Revision v1.2                  15 October 2000                               
Updated. Added "What If Nothing Works" section.                              
Revision v1.1                  13 September 2000                             
Added tcsh fixes                                                             
Revision v1.0                  5 September 2000                              
First release                                                                
-----------------------------------------------------------------------------

Table of Contents
1. Introduction
2. How Keys Are Turned Into Actions
3. Why It Doesn't (Always) Work
4. X
5. What You Should Do When Writing Applications
6. What You Should Do On Your System
    6.1. What Needs to Be Done
    6.2. How to Do It
    6.3. Fixing for tcsh
   
   
7. What If Nothing Works
8. More Hacking
9. Conclusions

1. Introduction

Every Linux user has been sooner or later trapped in a situation in which
having working Backspace and Delete keys on the console and on X seemed
impossible. This paper explains why this happens and suggests solutions. The
notions given here are essentially distribution-independent: due to the
widely different content of system configuration files in each distribution,
I will try to give the reader enough knowledge to think up his/her own fixes,
if necessary.

I assume that the Backspace key should go back one character and then erase
the character under the cursor. On the other hand, the Delete key should
delete the character under the cursor, without moving it. If you think that
the function of the two keys should be exchanged, in spite of the fact that
most keyboards feature an arrow pointing to the left (??) on the Backspace
key, then this paper will not give you immediate solutions, but certainly you
may find the explanations given here useful.

Another assumption is that the fixes should alter only local (user) files. No
standard part of the distribution should be altered. Finally, this document
discusses how to set up your system so that applications get the right
events. If an application decides to interpret such events in an
idiosyncratic way, the only possible fix is to reconfigure the application.

Note Since the first release of this Mini-HOWTO things have become even more 
     entangled. Different distributions of the same terminal emulator (e.g., 
     gnome-terminal as provided by Red Hat 7.0, Helix Code/Ximian or even Red
     Hat??7.1) generate different ASCII sequences. Due to this mismatch, now 
     the terminal databases correspond even less to the terminal emulators   
     they are supposed to describe. To set a firm ground for the following   
     discussion, we assume basically as correct settings the ones proposed in
     the Debian keyboard policy.                                             
-----------------------------------------------------------------------------

2. How Keys Are Turned Into Actions

When a key is pressed on the keyboard, a number of hardware and software
components cooperate so as to guarantee that the intended meaning of the key
(e.g., emitting a certain character) matches the actual behaviour of the key.
I will concentrate on the software side (as our control on the hardware part
is nonexistent), and in particular, for the time being, on the events related
to console output.

 1. Hitting a key causes raw keyboard scancodes to be generated; these
    scancodes are then transformed in a keycode. On an i386 system, usually
    the key Backspace emits 14 and the key Delete emits 111.
   
 2. The keycodes are translated by the keyboard library into a keyboard
    symbol (keysym) using the keyboard definition loaded by the user. If you
    look into your keyboard database (e.g., in /lib/kbd/), you'll discover
    several definitions for different computers, different layouts and
    possibly different interpretations of the same keys (e.g., one could
    desire that the two Alt keys really behave as distinct modifiers). The
    Linux console keyboard layout assigns keysym Delete to keycode 14 and
    keysym Remove to keycode 111. This may seem strange, but the Linux
    console emulates a VT100 terminal, and this is the way things work in
    that realm.[1]
   
 3. Our journey has still to come to an end. Console applications read ASCII
    sequences, not keysyms. So the console must read keysyms and translate
    them into ASCII sequences that suitably encode the keys. Of course, this
    operation must be performed in a way that is understandable by
    applications. For instance, on the Linux console the Delete keysym is
    mapped to the ASCII code 127 (DEL), the Remove keysym on a suitable
    escape sequence, and the BackSpace keysym to ASCII code 8 (BS).
   
 4. Finally, we must in a sense roll back to what we had before and translate
    the ASCII sequences generated by each key into a key capability. This
    goal is reached by a terminal database, which contains, for each kind of
    terminal, a reverse mapping from sequences of characters to key
    capabilities (which are essentially a subset of the keysyms).[2]
   
    Note Unfortunately, there are two "standard" terminal databases, 
         termcap and terminfo. Depending on your distribution, you   
         could be using either one of them, or the database could    
         even depend on the application. Our discussion will         
         concentrate on the more modern terminfo database, but the   
         suggested fixes take both into consideration.               
   
    For instance, on the Linux console F1 generates an escape followed by
    [[A, which can be translated to the capability key_f1 by looking into the
    terminal-database entry of the console (try infocmp linux if you want to
    have a look at the entry). A very good and thorough discussion of
    terminal databases can be found in GNU's termcap manual. Usually Linux
    applications use the newer terminfo database, contained in the ncurses
    package.
   
    Maybe at this point not surprisingly, the Linux console terminfo entry
    maps DEL to the kbs (backspace key) capability, and escape followed by
    [3~ to the kdch1 ("delete-one-char" key) capability. Even if you could
    find strange that the Backspace key emits a DEL, the terminal database
    puts everything back into its right place, and correctly behaving
    applications will interpret DEL as the capability kbs, thus deleting the
    character to the left of the cursor.
   

-----------------------------------------------------------------------------
3. Why It Doesn't (Always) Work

I hope the basic problem is clear at this point: there is a bottleneck
between the keyboard and console applications, that is, the fact that they
can only communicate by ASCII sequences. So special keys must be first
translated from keysyms to sequences, and then from sequences to key
capabilities. Since different consoles have different ideas about what this
translation can look like, we need a terminal database. The system would work
flawlessly, except for a small problem: it is not always set up correctly,
and not everyone uses it.

Applications must have a way to know which database entry to use: this is
accomplished by suitably setting the TERM environment variable. In some
cases, there is a mismatch between the terminal emulator and the content of
the database entry suggested by TERM.

Moreover, many applications do not use the terminal database (or at least not
all of it), and consider BS and DEL ASCII codes with an intended meaning:
thus, without looking at the database, they assign them semantics (usually,
of course, the semantics is removing the character before or under the
cursor). So now our beautiful scheme is completely broken (as every Linux
user is bitterly aware). For instance, the bash assumes that DEL should do a 
backward-delete-char, that is, backspace. Hence, on a fresh install the 
Backspace key works on the console as expected, but just because of two
twists in a row! Of course, the Delete key does not work. This happens
because the bash does not look into the terminal database for the kdch1
capability.

Just to illustrate how things have become entangled, consider the 
fix_bs_and_del script provided with the Red Hat distribution (and maybe
others). It assigns on-the-fly the BackSpace keysym to the Backspace key, and
the Delete keysym to the Delete key. Now the shell works! Unfortunately, all
programs relying on the correct coupling of keysym generation and terminal
database mappings are now not working at all, as the Delete keysym is mapped
to DEL, and the latter to the kbs key capability by the terminfo database, so
in such programs both keys produce backspacing.
-----------------------------------------------------------------------------

4. X

The situation under X is not really different. There is just a different
layer, that is, the X window system translates the scancodes into its own
keysyms, which are much more varied and precise than the console ones, and
feeds them into applications (by the way, this is the reason why XEmacs is
not plagued by the problem: X translates keycode 22 to keysym BackSpace and
keycode 107 to keysym Delete, and then the user can easily assign to those
keysyms the desired behaviour). Of course, a terminal emulator program
(usually a VT100 emulator in the X world) must translate the X keysyms into
ASCII sequences, so we are again in our sore business.

More in detail, usually xterm behaves exactly like the console (i.e., it
emits the same ASCII sequences), but, for instance, gnome-terminal in Red Hat
<7.0 or ??7.1 emits BS for Backspace and DEL for Delete. The real fun starts
when you realise that by default they use the same terminal-database entry,
so the fact that the kbs capability is associated to an ASCII DEL makes all
correctly behaving applications produce the same behaviour for the Backspace
and Delete keys in gnome-terminal. The simple statement
+---------------------------------------------------------------------------+
|bash$ export TERM=gnome                                                    |
+---------------------------------------------------------------------------+
can solve the problem in this case for correctly behaving applications. Well,
not always, because your system could lack an entry in the terminal database
named gnome, in particular if it is not very up-to-date.

In any case, this is not always a solution: if, for instance, you have a Red
Hat 7.0 distribution, your gnome-terminal behaves like a console. But beware:
if you upgraded your desktop using the Helix distribution, then your 
gnome-terminal behaves like a pre-7.0 Red Hat.

Just to make easier the following discussion, let us define standard a VT100
emulator behaving like the console, and deviant one that emits BS for 
Backspace and DEL for Delete.[3] Thus, for instance, xterm has always been
standard in the Debian distribution, while it switched a couple of times from
standard to deviant and viceversa in Red Hat; the behaviour of gnome-terminal
is even more erratic. See Section 8 for some information on how to turn a
deviant terminal into a standard one.
-----------------------------------------------------------------------------

5. What You Should Do When Writing Applications

When you write a console application, be kind to the user and try to
understand what comes from the standard input using the following fallback
chain:

 1. open the right terminfo entry, and try to process the sequence so as to
    discover whether it has a particular meaning on the current terminal; if
    so, use the terminfo semantics;
   
 2. use the ASCII intended meaning on line feeds, newlines, tab characters
    and, of course, BS and DEL. Crossing your finger could also be useful.
   

-----------------------------------------------------------------------------
6. What You Should Do On Your System

Note again that the main issue that confuses people trying to fix their
system is that usually they are fixing thing in the wrong place. Since the
parts that work often just work by chance, trying to fix the system assuming
something is broken will often lead to change correct settings into incorrect
settings.
-----------------------------------------------------------------------------

6.1. What Needs to Be Done

6.1.1. Detecting Deviance

The first step towards a clean solution is to know exactly which terminals
are deviant and which not. Usually they all behave like the console, and in
this case the modifications to get everything working are minimal. If,
however, you have some deviant terminal (e.g., a deviant version of 
gnome-terminal), you will have to treat it in a special way.

The following C one-liner
void main(void) {int c; while(c = getchar()) printf("%d 0x%02X\n", c, c);}   
may help you. Put the line into a file named ascii.c, compile it with gcc
ascii.c -o ascii, type ./ascii and press a key followed by RETURN. The
program will display the decimal and hexadecimal codes of the ASCII sequence
produced (you may want to do a stty erase ^- first to get really all the
codes). Now you can easily see what Backspace key does: if it emits a DEL
(127), you have a standard emulator, if it emits a BS (8) you have a deviant
one.
-----------------------------------------------------------------------------

6.1.2. Distinguishing Between Emulators

If you have some deviant terminal emulator, you must distinguish it from the
standard ones. Theoretically, this should not be a problem because there are
different entries in the terminal database for terminals with different
sequences (the entry used depends on the value of the TERM variable).

Here we take the approach that the gnome entry should be used for all deviant
VT100 emulators, and the xterm entry for the standard ones. This is in line
with several distributions (except a few cases like RedHat ??5.0, where the
xterm entry is deviant).

However, gnome-terminal uses by default the same entry as xterm, so if one is
deviant and the other one is not you will need to find a way to tell them
apart. The option termname of gnome-terminal allows the user to set the TERM
variable to a more sensible name. However, in older versions of 
gnome-terminal the option does not work. Moreover, sometimes it is not easy
to modify the way gnome-terminal is started.

A good idea here is to exploit the fact that gnome-terminal sets the
COLORTERM variable to gnome-terminal. Thus, by adding a simple test to the
shell configuration files we can fix the TERM variable.
-----------------------------------------------------------------------------

6.1.3. Fixing the Terminal Database

Our problem now is that the terminal database could lack a gnome entry for
deviant terminals (this happens on a number of termcap and terminfo
versions). Recent terminfo databases have an entry gnome, but, in any case,
since gnome-terminal behaves essentially like xterm modulo our famous two
keys, it is possible to automagically generate a brand new correct entry.
-----------------------------------------------------------------------------

6.1.4. Fixing the Shell Behaviour

The readline library used by the bash and by many other programs to read the
input line can be customized so to recognize specific sequences of
characters. The customization can also depend on the TERM variable, so once
we can distinguish terminals we can do fine tuning of the keyboard.

Moreover, if you want less and other application that do raw line input to
work correctly, you must convince the shell that under a deviant terminal
emulator the erase character is BS, and not DEL (in the other case the 
Backspace key is already emitting DEL, so we do not have to do anything).
This can be done using the command stty.
-----------------------------------------------------------------------------

6.2. How to Do It

Caution These fixes have some drawbacks. First, they work only for the       
        specified terminals. Second, in theory (but this is unlikely to      
        happen) they could confuse the readline library on other terminals.  
        Both limitations are however mostly harmless.                        

First of all, check with infocmp gnome whether you already have a gnome entry
in your terminfo database (we will fix termcap later). If the entry does not
exist, the following command
bash$ tic <(infocmp xterm |\                                                 
        sed 's/xterm|/gnome|/' |\                                            
        sed 's/kbs=\\177,/kbs=^H,/' |\                                       
        sed 's/kdch1=\\E\[3~,/kdch1=\\177,/')                                
will create a correct one in ~/.terminfo. If the same command is launched by
the root, it will generate the entry in the global database (you can override
this behaviour by setting TERMINFO to ~/.terminfo). Note that if your xterm
entry is already deviant (e.g., you have a Red Hat ??5.0) the script will
copy it unchanged, which is exactly what we want.

Now, add the following snippet to ~/.inputrc[4]:
"\e[3~": delete-char                                                         
This line teaches the readline library how to manage your standard Delete key
for standard emulators, and with a bit of luck it should not interfere with
other terminals. However, now we must also explain to the library the meaning
of the DEL character on deviant terminals, for instance by adding
$if term=gnome                                                               
DEL: delete-char                                                             
Meta-DEL: kill-word                                                          
"\M-\C-?": kill-word                                                         
$endif                                                                       
to ~/.inputrc. If xterm is deviant, too, you must add other three lines for
it. On the other hand, if no terminal emulator is deviant this part is not
needed. All these changes can be made global by altering the /etc/inputrc
file.

Note that the conditional assignments make deviant terminal emulators work 
given that the TERM variable is set correctly. To guarantee this, there are a
number of techniques. First of all, since the default value of the TERM
variable for gnome-terminal is xterm, if all terminals are not deviant then
we do nothing. If, however, a terminal that by default uses the xterm entry
is deviant you must find a way to set the TERM variable correctly; assume for
instance this is true of gnome-terminal.

The simplest way to obtain this effect is to start gnome-terminal with the
argument --termname=gnome, for instance by suitably setting the command line
in the launcher on the GNOME panel. If however you have an old version, and
this method does not work, you can add the lines
if [ "$COLORTERM" = "gnome-terminal" ]                                       
then                                                                         
    export TERM=gnome                                                        
fi                                                                           
to your ~/.bashrc configuration file[5]. The assignment is executed only
under gnome-terminal, and sets correctly the TERM variable.

Note Setting the terminal to gnome could prevent ls from using colours, as   
     many versions of ls do not know that gnome-terminal is colour capable.  
     To avoid this problem, create a configuration file ~/.dircolors with    
     dircolors --print-database >~/.dircolors, and add a line TERM=gnome to  
     the configuration file.                                                 

We will now generate on-the-fly a suitable termcap entry for deviant terminal
emulators; this can be done as follows, always in ~/.bashrc:
if [ "$TERM" = "gnome" ]                                                     
then                                                                         
    export TERMCAP=$(infocmp -C gnome | grep -v '^#' | \                     
                    tr '\n\t' '  ' | sed 's/\\  //g' | sed s/::/:/g)         
fi                                                                           

Finally, we must explain to the terminal device which character is generated
by the erase key. Since usually the erase key is expected to backspace, there
is a nice trick taken from the Red Hat /etc/bashrc that works: add this to ~
/.bashrc:
KBS=$(tput kbs)                                                              
if [ ${#KBS} -eq 1 ]; then stty erase $KBS; fi                               
It's a simple idea: we read from the terminal database the capability kbs,
and set the erase character to its value if it is a single character (which
happens in both standard and deviant terminals).

Note Certain distributions could have fixes already in place in the          
     system-wide /etc/inputrc configuration file. In this case you can       
     eliminate redundant lines from your ~/.inputrc.                         
-----------------------------------------------------------------------------

6.3. Fixing for tcsh

In the case of the tcsh, the fixes go all in ~/.tcshrc, and follow the same
rationale as the ones for the bash:
bindkey "^[[3~" delete-char                                                                  
                                                                                             
if ($?COLORTERM) then                                                                        
   if ($COLORTERM == "gnome-terminal") then                                                  
      setenv TERM gnome                                                                      
   endif                                                                                     
endif                                                                                        
                                                                                             
if ($?TERM) then                                                                             
   if ($TERM == "gnome") then                                                                
      setenv TERMCAP \                                                                       
       "`infocmp -C gnome | grep -v '^#' | tr '\n\t' '  ' | sed 's/\\  //g' | sed s/::/:/g`" 
      bindkey "^?" delete-char                                                               
      bindkey "^[^?" delete-word                                                             
      bindkey "\377" delete-word                                                             
   endif                                                                                     
endif                                                                                        
                                                                                             
set KBS=`tput kbs`                                                                           
if (${%KBS} == 1) then                                                                       
   stty erase $KBS                                                                           
endif                                                                                        
The second part must be replicated for every deviant terminal. Of course, if
a termcap entry already exists it is not necessary to generate it.
-----------------------------------------------------------------------------

7. What If Nothing Works

The first thing to do is understanding which ASCII codes are produced by a
certain key using the C one-liner.

Once you know which sequences are produced, you must check the current 
terminfo entry with infocmp (don't be scared by the amount of information
printed!) and be sure that the kbs and kdch1 capabilities correspond to the
right sequences (that is, the one produced by the respective keys). Moreover,
you must check with stty -a that the erase character is the one emitted by
the Backspace key (note that ^H represent BS whereas ^? represents DEL).

If there is a mismatch, there can be several different reason: wrong content
of the TERM variable, wrong entry of the terminal database, wrong terminal
emulation under X. I hope at this point you have enough information to dig
the solution autonomously.

Note If different applications behave in different ways, it is likely that   
     some of them are using the terminal database correctly, and some are    
     not. Remember that the fact that the keys produce the right behaviour in
     a certain application does not mean that the application is using       
     correctly the terminal database??they could work just by chance. If you 
     want to have an independent check, you can try whether the ne editor    
     works. ne uses all terminal capabilities, including kbs and kdch1, and  
     uses intended meaning only as a last resource.                          
-----------------------------------------------------------------------------

8. More Hacking

So, you're not happy with the information you got. In this case, there is
even more hacking you can do on the Backspace/Delete issue, using suitable
commands that get or set the way X and the console handle keys.

It could happen that, for some reason, what I said talking about X is not
true, that is, X does not translate keycode 22 to keysym BackSpace and
keycode 107 to keysym Delete (or even that, on your particular keyboard, the
keycodes associated to Backspace/Delete are not 22 and 107). To be sure of
that, you need to use xev, a simple X application that will display the
keycode and keysym associated to the key you press. If anything goes wrong,
there are several ways you can fix the problem: the easy, temporary way is to
use xmodmap, a command that lets you change many settings related to X
keyboard handling. For instance,
+---------------------------------------------------------------------------+
|xmodmap -e "keycode 22 = BackSpace"                                        |
|xmodmap -e "keycode 107 = Delete"                                          |
+---------------------------------------------------------------------------+
will set correctly the keysyms (assuming that 22 and 107 are the correct
keycodes for you). In case you want to do some changes permanently, you can
play with the resources vt100.backArrowKey, vt100.translations and ttyModes
of xterm (and similar terminal applications) in the configuration file ~
/.Xdefaults. One possibility, for instance, is
XTerm.VT100.Translations: \                                                  
        <Key>BackSpace: string(0x7F)\n\                                      
        <Key>Delete:    string("\033[3~")                                    
You should take a look at the xterm man page for more information.

The program that does for the console what xev does for X is showkeys: it
will dump the console keycodes of the keys you press. Combining showkeys with
dumpkeys, which will print on standard output the console keymap, you can
easily fix mismatches between keycodes and keysyms. Analogously to xmodmap, 
loadkeys can then fix single associations, or load entirely new console
keymaps. With it, you can even change the string associated to a given
keysym. If you want to record these changes, you will have to define a new
keymap for the console (you should have a look at the system keymaps, usually
located in /lib/kbd).
-----------------------------------------------------------------------------

9. Conclusions

The fixes suggested here should solve to a large extent the problem of
deleting text you wrote (however, they do not help in creating other text
:)).

There is a small bug in the whole setting: if you're using the COLORTERM
trick and you start xterm from gnome-terminal, the former will get TERM set
to gnome. This inconvenience is, of course, mostly harmless, and does not
occur if you simply started gnome-terminal with TERM suitably set.

Another nontrivial problem that essentially has no solution is the one
concerning remote connections: if you connect to a host whose terminal
database is incoherent with yours, you will have to set up things manually.

Finally, it should be noted that the fixes will not work for broken
applications (for instance, applications ignoring the kbs key capability).
There is little to do in this case, as fixing for one broken application will
likely break all well-behaving ones.

Notes

[1]  This claim has been asserted/disputed several times commenting this     
     document. If you have any definitive information on this subject, please
     write me.                                                               
[2]  Some programs rely on the terminal driver for input line editing, such  
     as deleting characters or words. With stty, you can tell the terminal   
     driver what character it should use to delete the character to the left 
     of the cursor (the erase character). You can check your current settings
     with stty -a and set them with stty erase character.                    
[3]  Also these definitions have been asserted/disputed several times        
     commenting this document. If you have any definitive information on this
     subject, please write me.                                               
[4]  On older version of the bash, you must remember to set INPUTRC suitably,
     for instance adding                                                     
     export INPUTRC=~/.inputrc                                               
     to your ~/.profile (or whichever file is read just by login shells).    
[5]  More precisely, to the shell configuration file that is read in every   
     shell, not only in login shells. The right file depend on startup       
     sequence of your bash.                                                  
