This Space for Rent

Trivial hack of the day

One of the minorly annoying featurettes in Linux (and, for that matter, in every version of Unix) is the nonstop battle between ^H and ^? for who's going to be the official backspace character. Years ago, I worked around it on pell by the simple expedient of only using the console (and then, after I put pell into a co-lo [where it's been for about 11(!) years now] by including "set-backspace-to-del-damnit" in my standard putty setup.

This is a bit of a kludge, and it only works (most of the time) because I remember that I have to do it.

At work, there's much the same problem, except that at work we have massively multiuser Linux boxes scattered all over North America, which 10s of thousands of people logging into them from a variety of different terminals and terminal emulators. The standard login drops people into a all-singing-all-dancing menuing interface which understands both ^H and ^?, so 99% of the time it's not a problem. The other 1% is a problem, though -- when telnet forks off /bin/login, it's handing off erase handling to the kernel, which, as is traditional for Unix, only allows one character erase key, and, because it's traditional for Linux, that key is ^?. And if your terminal or terminal emulator has a backspace key that does backspace, a typo is forever.

This makes some people very unhappy, and they complain -- for good reason -- bitterly about having to do hand workarounds to deal with it. And because this Linux is based off a modern one, all of the input for logging in comes from deep in the bowels of PAM (which is, at least as far as I've seen the grand prize winner for unreadable code outside of the deliberately obfuscated that makes up the bulk of the FSF's catalogue) so my old kludge of hand-tweaking getty and login won't work.

So what to do? Patch the kernel, of course:

--- ./drivers/char/~n_tty.c	Thu Dec 13 08:49:02 2007
+++ ./drivers/char/n_tty.c	Thu Dec 13 08:52:33 2007
@@ -422,12 +422,22 @@
 	enum { ERASE, WERASE, KILL } kill_type;
 	int head, seen_alnums, cnt;
 	unsigned long flags;
+	unsigned char erase1, erase2;
 
 	if (tty->read_head == tty->canon_head) {
 		/* opost('\a', tty); */		/* what do you think? */
 		return;
 	}
-	if (c == ERASE_CHAR(tty))
+
+	if ( erase1 = ERASE_CHAR(tty) )
+	    erase2 = erase1;
+	else {
+	    /* default ^H & DEL are both erase character keys */
+	    erase1 = '\010';
+	    erase2 = '\177';
+	}
+
+	if (c == erase1 || c == erase2 )
 		kill_type = ERASE;
 	else if (c == WERASE_CHAR(tty))
 		kill_type = WERASE;
@@ -506,7 +506,7 @@
 					put_char(tty->read_buf[head], tty);
 				}
 			} else if (kill_type == ERASE && !L_ECHOE(tty)) {
-				echo_char(ERASE_CHAR(tty), tty);
+				echo_char(ERASE_CHAR(tty) ? ERASE_CHAR(tty) : '\010', tty);
 			} else if (c == '\t') {
 				unsigned int col = tty->canon_column;
 				unsigned long tail = tty->canon_head;
@@ -672,6 +672,15 @@
 static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
 {
 	unsigned long flags;
+	unsigned char erase1, erase2;
+
+	if ( ERASE_CHAR(tty) ) {
+		erase1 = erase2 = ERASE_CHAR(tty);
+	}
+	else {
+		erase1 = '\010';
+		erase2 = '\177';
+	}
 
 	if (tty->raw) {
 		put_tty_queue(c, tty);
@@ -757,7 +766,7 @@
 		}
 	}
 	if (tty->icanon) {
-		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
+		if (c == erase1 || c == erase2 || c == KILL_CHAR(tty) ||
 		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
 			eraser(c, tty);
 			return;
@@ -1038,7 +1047,12 @@
 			set_bit('\n', tty->process_char_map);
 
 		if (L_ICANON(tty)) {
-			set_bit(ERASE_CHAR(tty), tty->process_char_map);
+			if ( ERASE_CHAR(tty) )
+				set_bit(ERASE_CHAR(tty), tty->process_char_map);
+			else {
+				set_bit('\010', tty->process_char_map);
+				set_bit('\177', tty->process_char_map);
+			}
 			set_bit(KILL_CHAR(tty), tty->process_char_map);
 			set_bit(EOF_CHAR(tty), tty->process_char_map);
 			set_bit('\n', tty->process_char_map);
--- ./include/asm-i386/~termios.h	Wed Dec 12 09:56:33 2007
+++ ./include/asm-i386/termios.h	Thu Dec 13 08:53:23 2007
@@ -60,13 +60,13 @@
 #ifdef __KERNEL__
 #include 
 
-/*	intr=^C		quit=^\		erase=del	kill=^U
+/*	intr=^C		quit=^\		erase=0		kill=^U
 	eof=^D		vtime=\0	vmin=\1		sxtc=\0
 	start=^Q	stop=^S		susp=^Z		eol=\0
 	reprint=^R	discard=^U	werase=^W	lnext=^V
 	eol2=\0
 */
-#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#define INIT_C_CC "\003\034\0\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
 
 /*
  * Translate a "termio" structure into a "termios". Ugh.

This little hack tweaks n_tty.c so that if ERASE_CHAR(tty) == 0, ^H AND ^? become erase characters and everything will work happily up until the point where the shell runs your .profile and tsets ERASE_CHAR() to the wrong thing. Which is not a problem if you're kept away from the icky shell.

Comments


gtrcghdfhfvhyeryhchyhydrgyav3hrvesGUCZuvzgufruecguffgbgcrgeuccudufctuduftuctftvttcfbtdfftcttsdtvtvgdvttctdsfrsetvfrtctetdfzvtftseurtutfrtgbrhy546seghyrtbhtytbyrgzgbghcfxgtfrxdtgbgtv gvxbrtasfgvfxrgdgv dfvvdxrgrbvgfcgyt5gfsdbgh fgvbbxbdgvgxfgndfv fgdgtvtv ddvfgv fverffgnxddc c fvgb cvtvfgc fdvgrstfgtr vtrdserftc daefsrtc fvafgdgcfbddcbfrdgszbcfdf fvgfzxcdvgx mfxdnvhzdcgxgv dcvsfszhgftfhgsaxdxshfhdtsztx

fftfbvfgb vhnfhchcf Sat Dec 22 11:57:54 2007

tybehtxbhddhnbngxg

cfmmvjnvjnjy ghhnh Sat Dec 22 11:58:59 2007

t67cr6fk57djrb ctx ny ncf ntx78y7mnyymy76vxesxdrsy4dydsgyy fyfydefrt ffydryr46dr

2 Sat Dec 22 11:59:50 2007

Comments are closed