From 3ee62b50a28ffb5ab4a6bc38870ba09fd6d5c74a Mon Sep 17 00:00:00 2001 From: Blau Araujo Date: Thu, 1 May 2025 11:36:05 -0300 Subject: [PATCH] =?UTF-8?q?conte=C3=BAdo=20da=20aula=2013?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aulas/13-read/README.org | 523 +++++++++++++++++++++++---------------- 1 file changed, 311 insertions(+), 212 deletions(-) diff --git a/aulas/13-read/README.org b/aulas/13-read/README.org index 44625b1..5c6da64 100644 --- a/aulas/13-read/README.org +++ b/aulas/13-read/README.org @@ -11,28 +11,25 @@ A função =read= é um /wrapper/ (uma /"embalagem"/) para simplificar o uso da chamada de sistema =read=. Chamadas de sistema são funções internas que o kernel implementa para que os programas (executados no /espaço de usuário/) tenham acesso -a serviços e funcionalidades do sistema (disponíveis no /espaço do kernel/). +a serviços e funcionalidades do sistema disponíveis no /espaço do kernel/. -Entre essas funcionalidades, estão: +Entre essas funcionalidades, estão o acesso a arquivos para leitura e escrita, a +alocação de espaço em memória, a criação de processos, o término de programas e +muitas outras. Portanto, nossos programas em C não interagem diretamente com o +hardware ou com recursos protegidos do sistema. Em vez disso, eles fazem +/chamadas de sistema/ para "pedir" que o kernel realize essas operações por eles. -- Acesso a arquivos para leitura e escrita; -- Alocação de espaço em memória; -- Criação de processos; -- Término de programas. +Isso também significa que todas as funções da biblioteca C padrão que nós +utilizamos até agora, em algum momento, também fazem chamadas de sistema. Por +exemplo, a função =fgets= utiliza a chamada de sistema =read= para preencher o +buffer de entrada do programa e, em seguida, copiar parte desse conteúdo para um +determinado endereço na memória: o que também envolve algumas outras chamadas de +sistema. -Nossos programas em C não interagem diretamente com o hardware ou com recursos -protegidos do sistema. Em vez disso, eles fazem /chamadas de sistema/ para -"pedir" que o kernel realize essas operações por ele. Sendo assim, fica fácil -concluir que todas as funções da biblioteca C padrão que nós utilizamos até -agora, em algum momento, fazem chamadas de sistema. - -Por exemplo, a função =fgets= utiliza a chamada de sistema =read= para preencher o -buffer de entrada do processo e, em seguida, copiar parte desse conteúdo para o -buffer definido no programa para receber uma string. Se quisermos utilizar -a chamada de sistema =read= para escrever bytes diretamente no buffer de destino, -evitando o buffer de entrada do processo, nós podemos utilizar a função =read= -da =glibc=, que é uma interface (um /wrapper/, ou /"embalagem"/) para a chamada de -sistema propriamente dita. +Se quisermos utilizar a /chamada de sistema/ =read= para escrever bytes diretamente +no endereço de destino, evitando o buffer de entrada do processo, nós podemos +utilizar a /função/ =read= da =glibc=, que é uma interface para a chamada de sistema +=read= propriamente dita. #+begin_quote As /man pages/ das interfaces para as chamadas de sistema implementadas na =glibc= @@ -78,14 +75,9 @@ int main(void) { Isso fará com que até 10 bytes (=BUFMAX=) recebidos pela entrada padrão (descritor de arquivos =0=, valor expandido pela macro =STDIN_FILENO=) sejam escritos na memória a partir do endereço de =buf=. Mas, diferente de =fgets= e -=scanf=, não haverá a inclusão do terminador nulo (='\0'=). - -*** Incluindo o terminador nulo - -Se quisermos que o conteúdo do buffer seja uma string, nós teremos que -implementar uma forma de acrescentar o terminador nulo -- por exemplo, -utilizando a quantidade de bytes lidos, que é retornada pela função =read= -em caso de sucesso: +=scanf=, não haverá a inclusão do terminador nulo (='\0'=). Se quisermos +garantir um byte para receber o terminador, nós precisamos fazer como na +função =fgets=: ler =count - 1= bytes... #+begin_src c #include @@ -95,11 +87,37 @@ em caso de sucesso: int main(void) { char buf[BUFMAX]; - ssize_t count = 0; + read(STDIN_FILENO, buf, BUFMAX - 1); - if((count = read(STDIN_FILENO, buf, BUFMAX - 1)) > 0) { - buf[count] = '\0'; + ... + + return 0; +} +#+end_src + + +** Conversão para string incluindo o terminador nulo + +Para converter o conteúdo do buffer em uma string, nós temos que +implementar uma forma de acrescentar o terminador nulo -- por exemplo, +utilizando a quantidade de bytes efetivamente lidos, que é retornada +pela função =read= em caso de sucesso: + +#+begin_src c +#include + +#define BUFMAX 10 + +int main(void) { + + char buf[BUFMAX]; + ssize_t bytes = 0; + + if((bytes = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { + return 1; // Erro ou nada foi lido } + // Tratamento do terminador nulo... + buf[bytes] = '\0'; /* ... */ @@ -108,19 +126,19 @@ int main(void) { #+end_src #+begin_quote -O retorno =0= indica o fim do arquivo e =-1= indica um erro, cujo identificador é -atribuído à variável =errno= e pode ser exibido com a função =perror=. +O retorno =0= indica o fim do arquivo e =-1= indica um erro, cujo identificador +é atribuído à variável =errno= e pode ser exibido com a função =perror=. #+end_quote -A solução do exemplo faz com que =read= se comporte quase como a função -=fgets=, exceto por não criar um buffer de entrada no /heap/ do processo: +A solução do exemplo faz com que =read= tenha um comportamento semelhante ao +de =fgets=: - A leitura é feita até =BUFMAX - 1=; -- O caractere seguinte no buffer (=buf[count]=) recebe ='\0'=; +- O caractere seguinte no buffer (=buf[bytes]=) recebe ='\0'=; - O caractere ='\n'= será preservado se estiver no limite de leitura. Para demonstrar, digamos que o usuário digitou =12345= e teclou =Enter=. O valor -de =count= será =6= e os bytes em =buf= serão: +de =bytes= será =6= e os bytes em =buf= serão: #+begin_example buf[0] buf[1] buf[2] buf[3] buf[4] buf[5] buf[6] buf[7] buf[8] buf[9] @@ -129,7 +147,7 @@ buf[10] | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x0A | lixo | lixo | lixo | lixo | +------+------+------+------+------+------+------+------+------+------+ #+end_example -Depois de alterado o byte em =buf[count]= (=buf[6]=), nós teremos: +Depois de alterado o byte em =buf[bytes]= (=buf[6]=), nós teremos: #+begin_example buf[0] buf[1] buf[2] buf[3] buf[4] buf[5] buf[6] buf[7] buf[8] buf[9] @@ -147,7 +165,7 @@ buf[10] | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39 | lixo | +------+------+------+------+------+------+------+------+------+------+ #+end_example -Os caracteres =0x30= (=0=) e =0x0a= (='\n'=) não serão lidos, o valor de =count= será =9= +Os caracteres =0x30= (=0=) e =0x0a= (='\n'=) não serão lidos, o valor de =bytes= será =9= e =buf[9]= receberá o terminador nulo: #+begin_example @@ -169,20 +187,22 @@ estivermos lendo... - Dados para cálculos de /checksum/ e /hash/; - Dados para compressão ou criptografia... - Entre outras tantas situações. +Entre outras tantas situações. ** O buffer do terminal -Quando um programa com a função =read= é executado interativamente, os dados -digitados são inicialmente armazenados em um buffer gerenciado pelo terminal. -Ao chamar a função =read=, o programa consome parte desses dados da entrada padrão -e os bytes que não forem consumidos permanecerão no buffer do terminal. +Quando um programa com a função =read= é executado para receber dados digitados +em um terminal, a digitação é acumulada em um buffer gerenciado pelo terminal +e só será enviada para o processo do programa quando o usuário teclar =Enter=. +No programa, a função =read= consome parte desses dados e os bytes que não +forem consumidos permanecerão no buffer do terminal. -Esses dados residuais não são descartados e permanecem disponíveis para futuras -chamadas de leitura -- inclusive por processos subsequentes, caso o processo -atual termine. Isso pode causar efeitos inesperados em programas interativos -executados na sequência (como o próprio shell, por exemplo), pois os caracteres -digitados anteriormente, mas ainda não consumidos, serão lidos automaticamente. +Os dados residuais não são descartados e permanecem disponíveis para futuras +chamadas de leitura (inclusive por processos subsequentes, caso o processo +do programa termine) ou até o terminal ser fechado. Isso pode causar efeitos +inesperados em programas interativos executados na sequência (como o próprio +shell, por exemplo), pois o conteúdo remanescente no buffer do terminal será +lido automaticamente. Veja este exemplo: @@ -195,13 +215,13 @@ Veja este exemplo: int main(void) { char buf[BUFMAX]; - ssize_t count = 0; + ssize_t bytes = 0; - if((count = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { - return 1; + if((bytes = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { + return 1; // Erro ou nada foi lido } // Tratamento do terminador nulo... - buf[count] = '\0'; + buf[bytes] = '\0'; printf("%s\n", buf); @@ -216,45 +236,38 @@ Compilando e executando: :~$ ./a.out 123456789012345 123456789 -:~$ 012345 +:~$ 012345 <-- O Bash tentou interpretar e executar o conteúdo do buffer! bash: 012345: comando não encontrado #+end_example -A função =read= consumiu apenas os 9 primeiros caracteres digitados, os 6 -caracteres restantes continuaram no buffer do terminal e foram lidos pelo -processo do shell (interativo) que tentou executá-los como um comando. +A função =read= consumiu apenas os 9 primeiros caracteres digitados, mas os 6 +caracteres restantes continuaram no buffer do terminal e foram lidos e +interpretados pelo processo do shell (interativo) como a linha de um comando. -A solução para isso é simples (e já conhecida por nós): consumir todos os -bytes restantes na entrada padrão: +*** Descarga do buffer do terminal + +A solução para isso é simples e já é velha conhecida: consumir todos os +bytes restantes na entrada padrão, por exemplo, com a função que nós +criamos em outras aulas... #+begin_src c -#include -#include - -#define BUFMAX 10 - -int main(void) { - - char buf[BUFMAX]; - ssize_t count = 0; - - if((count = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { - return 1; - } - // Tratamento do terminador nulo... - buf[count] = '\0'; - - // Tratamento dos caracteres restantes no buffer do terminal... +void flush_stdin(void) { char c; while((c = getchar()) != '\n' && c != EOF); - - printf("%s\n", buf); - - return 0; } #+end_src -Compilando e executando novamente: +Mas esta função utiliza =getchar= que, assim como =fgets= e =scanf=, fará a +leitura do buffer de entrada criado no processo pela =glibc=. Certamente, +o conteúdo do buffer do terminal será copiado para o buffer de entrada do +programa. Porém, sem garantias de que haverá algo no buffer do terminal, +pode acontecer que a nossa função fique presa em =getchar= até que o usuário +tecle =Enter= novamente, enviando a quebra de linha que o loop =while= espera +para terminar. + +Por exemplo, utilizando a função =flush_stdin=, o nosso exemplo funcionaria +corretamente quando houvesse mais caracteres digitados do que o =read= +pode consumir: #+begin_example :~$ gcc -Wall teste.c @@ -264,9 +277,147 @@ Compilando e executando novamente: :~$ #+end_example -** Definindo um prompt +Contudo, digitando menos caracteres do que o limite, nós teríamos que +teclar =Enter= duas vezes: a primeira para a leitura de =read= e a segunda +para =getchar=... -O nosso programa precisa de um prompt, então vamos implementá-lo: +#+begin_example +:~$ gcc -Wall teste.c +:~$ ./a.out +12345 + <--- Aguardando um segundo ENTER +12345 + <--- Quebra de linha na string impressa +:~$ +#+end_example + +A segunda linha em branco era esperada porque nós não fizemos nada para +remover a quebra de linha lida por =read=, mas a primeira aconteceu porque +eu tive que teclar =Enter= mais uma vez para que =getchar= tivesse algo para +ler. + +*** Implementação de =flush_stdin= com a chamada =read= + +Antes de passarmos à solução da descarga do buffer do terminal, vamos +aproveitar que estamos lidando com chamadas de sistema para implementar +uma versão da função =flush_stdin= com =read=, em vez de =getchar=: + +#+begin_src c +void flush_tty_buffer(void) { + char c; + while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n'); +} +#+end_src + +#+begin_quote +*Importante!* Tanto =read= quanto =STDIN_FILENO= dependem da inclusão de =unistd.h=. +#+end_quote + +O princípio é o mesmo, só que, desta vez, o /loop/ vai continuar enquanto +=read= retornar que leu 1 byte e este byte for diferente de ='\n'=. A outra +diferença é que, em vez de ler o buffer do programa, a nossa nova função +lerá diretamente o buffer do terminal. + +#+begin_quote +Esta função não poderia ser utilizada se os bytes remanescentes estivessem +no buffer de entrada do programa! +#+end_quote + +Mesmo assim, o problema continua: se o buffer do terminal estiver vazio, +=read= ficará esperando o recebimento de uma linha, o que só acontecerá +quando nós digitarmos novamente um =Enter=. + +*** Descarga condicional do buffer do terminal + +Se a causa do problema é a possibilidade do buffer do terminal estar vazio, +basta condicionar a chamada de =flush_stdin=, ou de =flush_tty_buffer=, à +verificação desta condição, o que pode ser avaliado de, pelo menos, duas +formas: utilizando os bytes lidos e a sua quantidade ou verificando o +estado do buffer do terminal. + +*Solução 1: Teste da presença de ='\n'=* + +#+begin_example + buf[0] buf[1] buf[2] buf[3] buf[4] buf[5] buf[6] buf[7] buf[8] buf[9] + +------+------+------+------+------+------+------+------+------+------+ +buf[10] | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x0A | 0X00 | + +------+------+------+------+------+------+------+------+------+------+ +#+end_example + +Aqui, a digitação foi =12345678\n= (exatamente =BUFMAX - 1= bytes) e, mesmo que +fosse digitada qualquer quantidade de bytes menor que essa, o buffer do +terminal estaria vazio e o caractere ='\n'= estaria presente na string. Logo, +nós poderíamos utilizar a seguinte condição: + +#+begin_src c +if (buf[bytes - 1] != '\n') { + flush_stdout(); // Ou flush_tty_buffer +} +#+end_src + +É uma solução interessante porque, além de simples, ela já verifica a +existência da quebra de linha na string e nós poderíamos aproveitar, se +fosse o caso, para removê-la: + +#+begin_src c +if (buf[bytes - 1] == '\n') { + buf[bytes - 1] = '\0'; // Remoção da quabra de linha +} else { + flush_stdout(); // Ou flush_tty_buffer +} +#+end_src + +*Solução 2: verificação do estado do buffer do terminal* + +A chamada de sistema =ioctl=, no cabeçalho =sys/ioctl.h=, manipula parâmetros +de dispositivos especiais (como terminais, por exemplo). Seus argumentos +são: + +- O descritor de arquivos do dispositivo (ex.: =0= ou =STDIN_FILENO=); +- O número da operação com o dispositivo (ex.: =FIONREAD=, quantidade de bytes + disponíveis para leitura imediata); +- Um ponteiro para o endereço que vai receber a informação obtida. + +Sendo assim, nós poderíamos criar uma função para retornar =0=, no caso de +buffer vazio, ou a quantidade de bytes remanescentes no buffer do terminal: + +#+begin_src c +#include + +... + +int is_tty_empty(void) { + int r = 0; + return (ioctl(STDIN_FILENO, FIONREAD, &r) == 0); +} +#+end_src + +Portanto, nossa função retornaria =1= (verdadeiro), se o buffer do terminal +estivesse vazio, ou =0= (falso), se não estivesse. + +#+begin_quote +A ideia de criar uma função, em vez de utilizar =ioctl= diretamente, tem a +ver com a atribuição de um valor semântico ao código. +#+end_quote + +No nosso exemplo, ela poderia ser utilizada desta maneira: + +#+begin_src c +if (!is_tty_empty()) { + flush_stdout(); // Ou flush_tty_buffer +} +#+end_src + +*Qual solução utilizar?* + +Não existe uma resposta certa neste caso: tudo depende do contexto do +programa. Se eu estivesse buscando uma solução mais robusta e aplicável +em diversas situações, eu escolheria a segunda (=ioctl=), mas escolheria +a primeira em contextos menos rigorosos, onde a quebra de linha tivesse +que ser detectada e removida da string de qualquer forma. + +Eu vou optar pela primeira solução desta vez, então o nosso código ficará +assim: #+begin_src c #include @@ -274,23 +425,30 @@ O nosso programa precisa de um prompt, então vamos implementá-lo: #define BUFMAX 10 +void flush_tty_buffer(void) { + char c; + while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n'); +} + int main(void) { char buf[BUFMAX]; - ssize_t count = 0; - - // Prompt... - printf("Digite algo: "); - - if((count = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { - return 1; + ssize_t bytes = 0; + + if((bytes = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { + return 1; // Erro ou nada foi lido } + // Tratamento do terminador nulo... - buf[count] = '\0'; - - // Tratamento dos caracteres restantes no buffer do terminal... - char c; - while((c = getchar()) != '\n' && c != EOF); + buf[bytes] = '\0'; + + if (buf[bytes - 1] == '\n') { + // Remoção condicional da quebra de linha... + buf[bytes - 1] = '\0'; + } else { + // Esvaziamento condicional do buffer do terminal... + flush_tty_buffer(); // Ou flush_stdin + } printf("%s\n", buf); @@ -298,6 +456,41 @@ int main(void) { } #+end_src +Compilando e testando... + +#+begin_example +:~$ gcc -Wall teste.c +:~$ ./a.out +12345 <-- 6 bytes digitados (buffer tty vazio) +12345 <-- Quebra de linha removida +:~$ ./a.out +12345678 <-- 9 bytes digitados (buffer vazio) +12345678 <-- Quebra de linha removida +:~$ ./a.out +123456789012345 <-- 16 bytes digitados (7 bytes no buffer tty) +123456789 <-- Não havia quebra de linha para remover +:~$ +#+end_example + +** Definindo um prompt + +O nosso programa precisa de um prompt, então vamos implementá-lo: + +#+begin_src c +... + +char buf[BUFMAX]; +ssize_t bytes = 0; + +printf("Digite algo: "); // Nosso prompt! + +if((bytes = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { + return 1; // Erro ou nada foi lido +} + +... +#+end_src + Agora, nós temos a impressão da string ="Digite algo: "= antes da leitura do terminal. Entretanto, ao compilar e executar o programa, nada é exibido até nós teclarmos =Enter=: @@ -306,55 +499,42 @@ até nós teclarmos =Enter=: :~$ gcc -Wall teste.c :~$ ./a.out 1234567890 -Digite algo: 123456789 +Digite algo: 123456789 <-- O prompt só foi impresso aqui! :~$ #+end_example Isso acontece porque =printf= utiliza o buffer de saída do programa (criado pela biblioteca padrão) e, por padrão, os dados na saída podem ser: -- Totalmente bufferizados: quando o programa escreve em arquivos ou pipes; -- Bufferizados por linha: quando a escrita é em um terminal. +- /Totalmente bufferizados/: quando o programa escreve em arquivos ou pipes; +- /Bufferizados por linha/: quando a escrita é em um terminal. -Como estamos escrevendo no terminal, a saída de =printf= é bufferizada por -linha e só é liberada quando o buffer de saída fica cheio (~BUFSIZ = 8192 bytes~), -quando recebe o caractere de quebra de linha (='\n'=) ou quando é esvaziada -explicitamente (por exemplo, com =fflush(stdout)=). +Como estamos escrevendo no terminal, a saída de =printf= é /bufferizada por +linha/ e só é liberada quando o buffer de saída fica cheio (geralmente, +=8192 bytes=), quando recebe o caractere de quebra de linha (='\n'=) ou quando +é esvaziada explicitamente. No caso do exemplo, a saída não foi descarregada porque a string na chamada de =printf= não tem uma quebra de linha (exatamente como nós queremos). Uma -solução simples, porém, é descarregar o buffer explicitamente: +solução simples, porém, é descarregar o buffer explicitamente com a função +=fflush=: #+begin_src c -#include -#include +... -#define BUFMAX 10 +char buf[BUFMAX]; +ssize_t count = 0; -int main(void) { +// Prompt... +printf("Digite algo: "); +// Descarga do buffer de saída... +fflush(stdout); - char buf[BUFMAX]; - ssize_t count = 0; - - // Prompt... - printf("Digite algo: "); - // Descarga do buffer de saída... - fflush(stdout); - - if((count = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { - return 1; - } - // Tratamento do terminador nulo... - buf[count] = '\0'; - - // Tratamento dos caracteres restantes no buffer do terminal... - char c; - while((c = getchar()) != '\n' && c != EOF); - - printf("%s\n", buf); - - return 0; +if((count = read(STDIN_FILENO, buf, BUFMAX - 1)) <= 0) { + return 1; } + +... #+end_src Compilando e testando: @@ -367,91 +547,10 @@ Digite algo: 1234567890 :~$ #+end_example -** Ainda temos um problema! - -Acontece, porém, que os nossos testes estão mascarando outro problema. -Observe o que acontece quando digitamos menos caracteres do que a função -=read= espera ler: - -#+begin_example -:~$ gcc -Wall teste.c -:~$ ./a.out -Digite algo: 12345 - -12345 - -:~$ -#+end_example - -A segunda linha em branco era esperada porque a quebra de linha da minha -digitação foi preservada, mas a primeira linha em branco aconteceu -porque eu tive que teclar =Enter= duas vezes! - -Isso aconteceu por causa da forma como estamos descarregando o restante do -buffer de entrada do terminal: - -#+begin_src c -// Tratamento dos caracteres restantes no buffer do terminal... -char c; -while((c = getchar()) != '\n' && c != EOF); -#+end_src - -Quando digitamos menos caracteres do que o =read= espera ler, não há sobras -no buffer do terminal e não há quebras de linha nem uma indicação de fim de -arquivo (a menos que o usuário tecle =Ctrl+D=). Por isso, a limpeza do buffer -deve ser condicionada de uma das seguintes formas: - -- Ou verificamos se o penúltimo caractere no buffer (=buf[count - 1]=) é - diferente de ='\n'=, o que indicaria que o buffer não foi consumido - completamente; -- Ou verificamos se o buffer está vazio ou não com a chamada de sistema - =ioctl=. - #+begin_quote -Experimente as duas soluções e escolha a sua preferida. +O problema da /bufferização por linha/ não é exatamente causado pelo uso da +chamada de sistema =read=. Na verdade, as funções de mais alto nível (como +=fgets= e =scanf=) é que são implementadas com mecanismos para descargar o buffer +de saída automaticamente. #+end_quote -*** Solução com o conteúdo de =buf[count - 1]= - -#+begin_src c -// Verificar se '\n' faz parte da string... -if (buf[count - 1] != '\n') { - // Tratamento dos caracteres restantes no buffer do terminal... - char c; - while((c = getchar()) != '\n' && c != EOF); -} -#+end_src - -*** Solução com =ioctl= - -A chamada de sistema =ioctl=, no cabeçalho =sys/ioctl.h=, manipula parâmetros -de dispositivos especiais (como terminais, por exemplo). Seus argumentos -são: - -- O descritor de arquivos do dispositivo (ex.: =0= ou =STDIN_FILENO=); -- O número da operação com o dispositivo (ex.: =FIONREAD=, quantidade de bytes - disponíveis para leitura imediata); -- Um ponteiro para o endereço que vai receber a informação obtida. - -Sendo assim, nós poderíamos utilizá-la desta forma para determinar se -o buffer do terminal contém alguma coisa (=0= indica buffer vazio): - -#+begin_src c -#include - -... - -int main(void) { - ... - - // Verificar se o buffer está vazio... - int r = 0; - if (ioctl(STDIN_FILENO, FIONREAD, &r) != 0) { - // Tratamento dos caracteres restantes no buffer do terminal... - char c; - while((c = getchar()) != '\n' && c != EOF); - } - ... -} -#+end_src -