2

I'm working on multiple C programs like a shell and a text editor that require to be run without the ECHO and ICANON flags. I disabled these using termios.h and managed to write my own gets function that can relay returned strings to my program and do special things for escape characters. The only think I can't do is print a backspace. If, for example, I use this code:

void mgets(char *str)
{
    int c, i = 0;

    while((c = getchar()) != '\n')
        if(c == 27)
            // the user hit ESC, ignore it for now
        else if(c == '\b')
            puts("\b \b") // this is where it SHOULD backspace
        // else if it's a regular character:
        else {
            str+i = c; i++; // write that to the string...
            putchar(c); // ...and echo it to the screen
        }
}

It all works great but the program just doesn't respond when I backspace. if I change my if statement a bit...

if(c == '\b')
    printf("You hit a backspace!");

But it's still unresponsive. I know that puts("\b \b") works so the only conclusion is that my backspace key isn't being detected as a '\b'. What can I do? Please help? Thanks in advance!

GNU Geek
  • 77
  • 1
  • 7

2 Answers2

2

Before you change the termios settings, save them. That information is the "same" as what you can see with stty -a.

For example

$ stty -a
speed 38400 baud; rows 40; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

The value shown after erase is the character that you're referring to as "backspace". Depending on the system conventions that might be ^H (8, or ASCII backspace) or ^? (127, or ASCII DEL).

In the termios settings, e.g.,

#include <termios.h>
...
struct termios data;
tcgetattr(0, &data);

then

data.c_cc[VERASE]

is the same erase character (with some caveats when the data has never been set: stty would show "undef").

Unlike some of the other settings, the meaning of the erase character is still relevant in raw/cbreak modes, since it is a setting that tells the terminal driver what your terminal is expected to send. It doesn't hurt to test for both possibilities, in case your stty settings were incorrect...

Further reading:

Thomas Dickey
  • 76,765
1

my backspace key isn't being detected as a '\b'. What can I do?

What control or escape sequences the (backspace) and (delete) keys generate varies.

On terminals that try to be like DEC VTs:

  • The key emits a DECFNK control sequence.
  • The behaviour of is switchable between the ASCII BS and DEL characters, with a control sequence named DECBKM emitted to the terminal.

But not every terminal (emulator) even tries to be like a DEC VT, let alone implements the DECBKM mechanism. You cannot just hardwire keyboard input sequences and expect them to work in all circumstances.

The way that your program needs to work is this: It needs to take the terminal type from the TERM environment variable, obtain the corresponding record from the terminfo (or termcap) database, and look there for two capabilities:

  • kbs (kb) — the sequence sent by the key
  • kdch1 (kD) — the sequence sent by the key

It must match those strings against the input that it reads. To distinguish between actual control sequences and simple presses of the Esc your program needs to be more complex than a fgetc() loop. You must set non-canonical mode and set a (short) read timeout. Only if read() returns an entire string within a timeout period should it be treated as an input control sequence.

Note that the special characters set by stty are pretty much a red herring here. They do not apply in non-canonical mode, and they only affect the line discipline anyway. The terminal's behaviour is entirely determined by how it maps the hardware key events to control sequences that it sends "down the wire" to the line discipline. Terminals are, as said previously, free to do that in a variety of ways, from employing loadable maps between keycodes and control sequences (FreeBSD built-in kernel terminal emulator) to X resources.

Further reading

JdeBP
  • 68,745