paulo1205
(usa Ubuntu)
Enviado em 29/08/2017 - 05:01h
Dentro do programa, o que a acontece é apenas o que está escrito: os caracteres especificados, incluindo as conversões de inteiro para string, são enviados para a saída. Ainda que o programador o saiba, o programa em C, por si próprio, não tem conhecimento de que existe uma sequência de controle no meio dos caracteres enviados à saída.
Contudo, em algum momento os caracteres atravessam a barreira entre o programa em C e seu ambiente de execução e são entregues ao sistema operacional, que, em condições normais, vai encaminhá-los a algum dispositivo. Essa etapa já está fora do controle do programa em C.
O dispositivo final pode ser um arquivo em disco, um canal de comunicação pela rede, um canal de comunicação local entre processos (como
pipes), dispositivos físicos (como uma porta serial, à qual pode estar ligado um terminal), um dispositivo virtual (como
/dev/null ou um pseudoterminal) etc.
No caso do seu programa, você provavelmente está mandando a saída para um terminal. Provavelmente é um terminal virtual ou emulado, mas lá pelos anos de 1980 e 1990, possivelmente seria um terminal físico, através de uma porta serial. Ambos trabalham de modo parecido, sendo que um terminal virtual ou emulado, como indica o nome, emula o comportamento de um terminal físico. Então eu vou descrever como um terminal físico funcionava.
O terminal físico recebe caracteres brutos. A maioria desses caracteres geralmente produz saída na tela de um símbolo correspondente ao código do caráter enviado (por exemplo: se eu enviar o byte de valor 65 para um terminal que use um conjunto de caracteres compatível com ASCII, a letra “A” aparecerá na tela). Porém, alguns códigos correspondem a caracteres de controle, que permitem passar comandos ao terminal. Dependendo das características do terminal, alguns desses comandos podem incluir ações como reposicionar o cursor, limpar a tela, alterar a cor do texto, acionar/desligar atributos especiais (e.g. texto invertido, piscante, enfatizado, sublinhado etc.), imprimir símbolos especiais fora do conjunto de caracteres principal, acionar modo gráfico ou de desenhos etc.
A interpretação dos comandos enviados ao terminal é uma atribuição do terminal. O envio de comandos ao terminal é responsabilidade de cada aplicação. O sistema operacional interfere muito pouco entre o que a aplicação manda e aquilo que efetivamente chega ao terminal (no caso do UNIX, o SO se limita a mapeamentos simples entre caracteres e a questões de temporização entre caracteres e
padding; nenhum esforço de reconhecer comandos ou de gerá-los por conta própria ou à revelia da aplicação).
Um terminal virtual ou emulado funciona de modo parecido, com a diferença que em lugar de enviar caracteres a um outro dispositivo físico, o SO os envia para outro processo. É esse processo que vai decidir o que fazer com os caracteres recebidos. Contudo, como geralmente esse processo estará ligado a um programa emulador de terminal, é de se esperar que ele funcione dando um jeito de exibir caracteres numa tela, e eventualmente modificar o texto exibido através de comandos que ele possa vir a reconhecer.
Historicamente, havia dezenas de tipos diferentes de terminais, cada um deles podendo aceitar um conjunto de comandos diferente para realizar ações análogas. Essas diferenças tinham de ser refletidas em cada aplicação que quisesse permitir o uso por mais de um tipo de terminal diferente. Isso era uma chateação de mais de uma maneira:
- cada vez que um tipo novo de terminal aparecia, se a aplicação quisesse trabalhar com esse terminal, ela tinha de ser adaptada de modo a incorporar suporte a tal tipo de terminal;
- o código da aplicação acabava crescendo por ter de embutir código que suportasse múltiplos terminais diferentes;
- nem todo terminal suportava os mesmos recursos, então os desenvolvedores da aplicação tinham de escolher se ela trabalharia com um mínimo de recursos comuns à maioria dos tipos de terminal, ou se funcionaria de modo melhor num determinado tipo de terminal e pior em outros.
Para tentar minimizar esses problemas, houve dois tipos de iniciativa: do lado da implementação de terminais, criar um padrão de comandos a ser universalmente aceito por todos os terminais, e, do lado do software, criar bibliotecas que permitissem tratar de modo uniforme, dentro da aplicações, a forma de solicitar ações ao terminal, permitindo conhecer o tipo do terminal apenas durante a execução do programa, com a própria biblioteca se encarregando de encontrar os comandos adequados ao tipo de terminal selecionado.
O padrão mais difundido de conjunto de comandos para terminais é o ANSI X3.64 (posteriormente incorporado como padrão ISO, ISO/IEC 6429). Vários modelos de terminais adotaram essa especificação (ou um subconjunto dela), sendo os mais famosos os das famílias DEC VT-100 e DEC VT-220. Entre os emuladores de terminais, a adoção do ANSI X3.64 foi ainda mais comum. Mesmo assim, ainda havia fabricantes que preferiram seguir com suas próprias linhas, e alguns que deliberadamente embutiam em seus produtos algumas discrepâncias em relação ao X3.64, como forma de criar um
lock-in. E havia também muitas instalações de equipamento legados, anteriores ao X3.64, que tinham de continuar sendo suportadas.
Essa não-universalização imediata (e, aliás, nunca alcançada) justificava a outra inciativa, de criar bibliotecas que permitissem aos desenvolvedores não se preocupar com o tipo de terminal, nem de sofrer as consequências de presumir um dado tipo de terminal, e acabar tendo a surpresa de a aplicação falhar porque o terminal escolhido não é o que o usuário tem em mãos.
No mundo UNIX prevalecem dois tipos de bibliotecas para acesso ao terminal: um de operações básicas, conhecido como
termcap (mais antiga) ou
terminfo (mais moderna, basicamente uma evolução da
termcap), que define uma especificação de “capacidades” que um terminal pode ter mapeia, para vários tipos de terminais, como ter acesso a cada uma dessas capacidades (se o terminal as suportar); e o outro tipo, encarnado sobretudo na biblioteca
Curses, que se preocupa com facilitar a criação de interfaces de texto, permitindo ao programador pensar em termos de janelas, regiões de tela/janela, painéis, menus, e até mesmo interação com mouse, sem ter de descer ao nível operações individuais de
termcap.
O seu programa assume que o terminal em que será executado é compatível com ANSI X3.64, pois usa uma sequência de escape fixa. Eu fico um pouco desconfortável com isso porque isso é pouco legível, e também porque quando eu mexi com terminais ligados a computadores com UNIX na faculdade, esses terminais eram compatíveis com um monte de padrões, MENOS ANSI X3.64 ou um de seus derivados (nós acabávamos usando o conjunto de comandos WYSE-50). É bem verdade que eu hoje não uso mais WYSE-50, mas mesmo assim eu prefiro escrever meus programas de modo a estar pronto para rodar em qualquer lugar, até porque vai dar praticamente o mesmo trabalho escrever código portável ou código não-portável.
Uma vez eu publiquei neste fórum um exemplo de programa usando
termcap/
terminfo (
https://www.vivaolinux.com.br/topico/C-C++/gotoxy-em-C). Dê uma olhada.