Como permitir backspaces no modo unbuffered / non-canonical?

1

Estou trabalhando em vários programas em C como um shell e um editor de texto que precisam ser executados sem os sinalizadores ECHO e ICANON. Desabilitei estes usando termios.h e consegui escrever minha própria função gets que pode retransmitir strings para o meu programa e fazer coisas especiais para caracteres de escape. O único pensamento que não posso fazer é imprimir um backspace. Se, por exemplo, eu uso este código:

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
        }
}

Tudo funciona muito bem, mas o programa simplesmente não responde quando eu retrocedo. se eu mudar minha declaração if um pouco ...

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

Mas ainda não responde. Eu sei que puts ("\ b \ b") funciona, então a única conclusão é que minha chave de backspace não está sendo detectada como '\ b'. O que eu posso fazer? Por favor ajude? Obrigado antecipadamente!

    
por GNU Geek 30.07.2017 / 20:50

2 respostas

2

Antes altere as configurações do termios, salve . Essa informação é a mesma que você pode ver com stty -a .

Por exemplo

$ 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

O valor mostrado após erase é o caractere a que você está se referindo como "backspace". Dependendo das convenções do sistema que podem ser ^H (8 ou backspace ASCII) ou ^? (127 ou ASCII DEL).

Nas configurações do termios, por exemplo,

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

então

data.c_cc[VERASE]

é o mesmo caracter erase (com algumas advertências quando os dados nunca foram definidos: stty mostrará "undef").

Ao contrário de algumas das outras configurações, o significado do caractere erase ainda é relevante nos modos raw / cbreak, já que é uma configuração que informa ao driver do terminal o que seu terminal deve enviar. Não faz mal testar as duas possibilidades, caso as configurações de stty estejam incorretas ...

Leitura adicional:

por 30.07.2017 / 22:52
1

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

Quais seqüências de controle ou escape as chaves (backspace) e (delete) geram varia.

Em terminais que tentam ser como DEC VTs:

  • A tecla emite uma seqüência de controle DECFNK.
  • O comportamento de é comutável entre os caracteres ASCII BS e DEL, com uma sequência de controle chamada DECBKM emitida para o terminal.

Mas nem todo terminal (emulador) tenta ser como um DEC VT, e muito menos implementa o mecanismo DECBKM. Você não pode apenas hardwire seqüências de entrada do teclado e esperar que eles funcionem em todas as circunstâncias.

A maneira que seu programa precisa para funcionar é esta: ele precisa pegar o tipo de terminal da variável de ambiente TERM , obter o registro correspondente do banco de dados terminfo (ou termcap ), e procurar lá por dois recursos:

  • kbs ( kb ) - a sequência enviada pela tecla
  • kdch1 ( kD ) - a sequência enviada pela tecla

Ele deve corresponder essas cadeias à entrada que lê. Para distinguir entre seqüências de controle reais e simples pressionamentos do Esc , seu programa precisa ser mais complexo que um loop fgetc() . Você deve definir o modo não canônico e definir um tempo limite de leitura (curto). Somente se read() retornar uma string inteira dentro de um período de tempo limite, deve ser tratado como uma seqüência de controle de entrada.

Note que os caracteres especiais definidos por stty são praticamente uma referência aqui. Eles não se aplicam no modo não canônico, e afetam apenas a disciplina de linha . O comportamento do terminal é inteiramente determinado pela forma como mapeia os eventos de chave de hardware para controlar as sequências que envia "down the wire" para a disciplina de linha. Os terminais são, como dito anteriormente, livres para fazer isso de várias maneiras, desde o uso de mapas carregáveis entre códigos de tecla e seqüências de controle (emulador de terminal de kernel embutido no FreeBSD) até recursos X.

Leitura adicional

por 30.07.2017 / 23:30