Capítulo 12. Programação

# Capítulo 12. Programação

Disponibilizo algumas dicas para as pessoas aprenderem programação no sistema Debian o suficiente para rastrear o código fonte do pacote. Aqui estão pacotes notáveis e pacotes de documentação correspondentes para programação.

Estão disponíveis referências online ao escrever "`man nome`" após instalar os pacotes `manpages` e `manpages-dev`. As referências online ás ferramentas GNU está disponíveis ao escrever "`info nome_do_programa`" após instalar os pacotes de documentação pertinentes. Poderá ter de incluir os arquivos `contrib` e `non-free` adicionalmente ao arquivo `main` pois algumas documentações GFDL não são consideradas compatíveis com DFSG.

Considere a possibilidade de utilizar ferramentas do sistema de controlo de versões. Ver Seção 10.5, “Git”.

| Atenção
Não use "`test`" como o nome de um ficheiro de teste executável. "`test`" é um comando embutido na shell.

| Cuidado
Deve instalar os programas compilados directamente a partir da fonte em "`/usr/local`" ou "`/opt`" para evitar colisões com os programas do sistema.

| Dica
Os Exemplos de código da criação de "Song 99 Bottles of Beer" devem dar-lhe uma boa ideia de praticamente todas as linguagens de programação.

## 12.1. O script de shell

O script de shell é um ficheiro de texto com o bit de execução definido e contém os comandos no seguinte formato.

    #!/bin/sh
     ... command lines

A primeira linha especifica o interpretador shell que lê e executa o conteúdo deste ficheiro.

Ler scripts de shell é a melhor maneira de compreender como um sistema tipo Unix funciona. Aqui, Dou alguns apontamentos e lembranças para programação de shell. Veja "Erros de Shell" (https://www.greenend.org.uk/rjk/2001/04/shell.html) para aprender a partir de erros.

Ao contrário do modo interativo de shell (veja Seção 1.5, “O comando simples da shell” e Seção 1.6, “Processamento de texto estilo Unix”), os scripts de shell usam frequentemente parâmetros, condicionais e ciclos.

### 12.1.1. Compatibilidade da shell do POSIX

Muitos scripts de sistema podem ser interpretados por qualquer uma das shells POSIX (ver Tabela 1.13, “Lista de programas da shell”).

- A shell POSIX não-interactiva predefinida "`/usr/bin/sh`" é uma ligação simbólica que aponta para `/usr/bin/dash` e é utilizada por muitos programas de sistema.

- A shell POSIX interactiva predefinida é `/usr/bin/bash`.

Evite escrever um script de shell com bashisms ou zshisms para fazê-loportável entre todas as shells do POSIX. Pode verificar isto a usar o checkbashisms(1).

Tabela 12.1. Lista dos 'bashisms' típicos

Bom: POSIX | Evitar: 'bashism'
--- | ---
`if [ "$foo" = "$bar" ] ; then …` | `if [ "$foo" == "$bar" ] ; then …`
`diff -u file.c.orig file.c` | `diff -u file.c{.orig,}`
`mkdir /foobar /foobaz` | `mkdir /foo{bar,baz}`
`funcname() { … }` | `function funcname() { … }`
formato octal: "`\377`" | formato hexadecimal: "`\xff`"

O comando "`echo`" tem de ser usado com os seguintes cuidados porque a implementação dele difere entre o integrado na shell e os comandos externos.

- Evite usar quaisquer opções de comando excepto "`-n`".

- Evite usar sequências de escape na cadeia porque o manuseamento dele varia.

| Nota
Apesar da opção "`-n`" não ser realmente sintaxe POSIX, geralmente é aceite.

| Dica
Use o comando "`printf`" em vez do comando "`echo`" se precisar de embeber sequências de escape na cadeia de saída.

### 12.1.2. Parâmetros da shell

Parâmetros de shell especiais são frequentemente usados no script shell.

Tabela 12.2. Lista de parâmetros da shell

parâmetro da shell | valor
--- | ---
`$0` | nome da shell ou script de shell
`$1` | primeiro(1) argumento shell
`$9` | nono(9) argumento shell
`$#` | quantidade de parâmetros de posição
`"$*"` | `"$1 $2 $3 $4 … "`
`"$@"` | `"$1" "$2" "$3" "$4" …`
`$?` | estado de saída do comando mais recente
`$$` | PID deste script shell
`$!` | PID da tarefa de fundo iniciada mais recentemente

As expansões de parâmetro básicas a lembrar são as seguintes.

Tabela 12.3. Lista de expansões de parâmetros de shell

formato da expressão do parâmetro | valor se `var` estiver definido | valor se `var` não estiver definido
--- | --- | ---
`${var:-string}` | "`$var`" | "`cadeia`"
`${var:+string}` | "`cadeia`" | "`null`"
`${var:=string}` | "`$var`" | "`cadeia`" (e corra "`var=cadeia`")
`${var:?string}` | "`$var`" | echo "`cadeia`" para stderr (e termina com erro)

Aqui, o símbolo ortográfico dois pontos "`:`" em todas estas operações é na realidade opcional.

- com "`:`" = teste de operador para existe e não nulo

- sem "`:`" = teste de operador para apenas existe

Tabela 12.4. Lista de substituições de parâmetros de shell chave

formato de substituição de parâmetro | resultado
--- | ---
`${var%suffix}` | remover o modelo de sufixo menor
`${var%%suffix}` | remover o modelo de sufixo maior
`${var#prefix}` | remover o modelo de prefixo menor
`${var##prefix}` | remover o modelo de prefixo maior

### 12.1.3. Condicionais da shell

Cada comando retorna um estado de saída que pode ser usado para expressões condicionais.

- Sucesso: 0 ("True")

- Erro: não 0 ("False")

| Nota
"0" no contexto condicional da shell significa "Verdadeiro", enquanto "0" no contexto condicional de C significa "Falso".

| Nota
"`[`" é o equivalente do comando `test`, o qual avalia os seus argumentos até ao "`]`" como uma expressão condicional.

Os idiomas condicionais básicos a lembrar são os seguintes.

- "`comando && se_sucesso_corre_também_este_comando || true`"

- "`comando || se_não_sucesso_corre_também_este_comando || true`"

- Um fragmento de script de multi-linhas como o seguinte

    if [ conditional_expression ]; then
     if_success_run_this_command
    else
     if_not_success_run_this_command
    fi

Aqui o "`|| true`" final foi necessário para assegurar que estes script de shell não termina acidentalmente nesta linha quando a shell é invocada com a flag "`-e`".

Tabela 12.5. Lista de operadores de comparação de ficheiros na expressão condicional

equação | condição para retornar o verdadeiro lógico
--- | ---
`-e file` | ficheiro existe
`-d file` | ficheiro existe e é um diretório
`-f file` | ficheiro existe e é um ficheiro normal
`-w file` | ficheiro existe e pode-se escrever nele
`-x file` | ficheiro existe e é executável
`file1 -nt file2` | ficheiro1 é mais recente que ficheiro2 (modificação)
`file1 -ot file2` | ficheiro1 é mais antigo que ficheiro2 (modificação)
`file1 -ef file2` | ficheiro1 e ficheiro2 estão no mesmo aparelho e no mesmo número de inode

Tabela 12.6. Lista de operadores de comparação de cadeias na expressão condicional

equação | condição para retornar o verdadeiro lógico
--- | ---
`-z str` | o comprimento de str é zero
`-n str` | o comprimento de str não é zero
`str1 = str2` | str1 and str2 são iguais
`str1 != str2` | str1 and str2 não são iguais
`str1 < str2` | str1 ordena antes de str2 (dependente do locale)
`str1 > str2` | str1 ordena após str2 (dependente do locale)

Os operadores de comparação Aritmética de inteiros na expressão regular são "`-eq`", "`-ne`", "`-lt`", "`-le`", "`-gt`" e "`-ge`".

### 12.1.4. Ciclos (loops) da shell

Existem vários idiomas de ciclo para usar na shell POSIX.

- "`for x in foo1 foo2 … ; do command ; done`" faz ciclos ao atribuir itens da lista "`foo1 foo2 …`" à variável "`x`" e a executar o "`comando`".

- "`while condition ; do command ; done`" repete o "`comando`" enquanto a "`condição`" for verdadeira.

- "`until condition ; do command ; done`" repete o "`comando`" enquanto a "`condição`" não for verdadeira.

- "`break`" permite sair do ciclo.

- "`continue`" permite resumir a próxima interacção do ciclo.

| Dica
A interacção numérica tipo linguagem C pode ser realizada a usar seq(1) como o gerador de "`foo1 foo2 …`".

| Dica
Veja Seção 9.4.9, “Repetir um ciclo de comandos sobre ficheiros”.

### 12.1.5. Variáveis de ambiente do shell

Algumas variáveis de ambiente populares para a linha de comandos normal da shell podem não estar disponíveis no ambiente de execução do seu script.

- Para "`$USER`", use "`$(id -un)`"

- Para "`$UID`", use "`$(id -u)`"

- Para "`$HOME`", use "`$(getent passwd "$(id -u)"|cut -d ":" -f 6)`" (isto também funciona em Seção 4.5.2, “O moderno sistema de gestão centralizado”)

### 12.1.6. A sequência de processamento da linha de comandos da shell

A shell processa um script rudemente como a seguinte sequência.

- A shell lê uma linha.

- A shell agrupa uma parte de uma linha como um testemunho se estiver dentro de `"…"` ou `'…'`.

- A shell divide a outra parte de uma linha em testemunhos como o seguinte.

  - Espaços em branco: `espaço tab nova-linha`

  - Meta-caracteres: `< > | ; & ( )`

- A shell verifica a palavra reservada para cada testemunho para ajustar o comportamento dele se não dentro de `"…"` ou `'…'`.

  - palavra reservada: `if then elif else fi for in while unless do done case esac`

- A shell expande o alias se não estiver dentro de `"…"` ou `'…'`.

- A shell expande o til se não dentro de `"…"` ou `'…'`.

  - "`~`" → diretório home do utilizador atual

  - "`~utilizador`" → diretório home do `utilizador`

- A shell expande o parâmetro ao seu valor se não dentro de `'…'`.

  - parâmetro: "`$PARAMETER`" ou "`${PARAMETER}`"

- A shell expande a substituição do comando se não dentro de `'…'`.

  - "`$( comando )`" → o resultado do "`comando`"

  - "`` comando ``" → o resultado do "`comando`"

- A shell expande o glob nome_de-caminho aos nomes de ficheiros correspondentes se não dentro de `"…"` ou `'…'`.

  - `*` → quaisquer caracteres

  - `?` → um caractere

  - `[…]` → qualquer um dos caracteres em "`…`"

- A shell procura o comando a partir do seguinte e executa-o.

  - definição de função

  - comando builtin

  - ficheiro executável em "`$PATH`"

- A shell vai à próxima linha e repete este processo outra vez a partir do topo desta sequência.

Citações singulares (') dentro de aspas não têm efeito.

Executar "`set -x`" na shell ou invocar a shell com a opção "`-x`" faz a shell escrever todos os comandos executados. Isto é muito útil para depuração.

### 12.1.7. Programas utilitários para script de shell

De modo a tornar o seu programa de shell o mais portável possível entre os sistemas Debian, é uma boa ideia limitar os programas utilitários àqueles disponibilizados pelos pacotes essenciais.

- "`aptitude search ~E`" lista os pacotes essenciais.

- "`dpkg -L nome_do-pacote |grep '/man/man.*/'`" lista as manpages (manuais) para comandos oferecidos pelo pacote `nome_do_pacote`.

Tabela 12.7. Lista de pacotes que contém programas utilitários pequenos para scripts de shell

pacote | popcon | tamanho | descrição
--- | --- | --- | ---
`dash` | V:928, I:998 | 207 | shell pequeno e rápido compatível com POSIX para `sh`
`coreutils` | V:910, I:1000 | 17994 | utilitários de núcleo GNU
`grep` | V:779, I:1000 | 1297 | GNU `grep`, `egrep` e `fgrep`
`sed` | V:762, I:1000 | 987 | GNU `sed`
`mawk` | V:485, I:998 | 295 | `awk` pequeno e rápido
`debianutils` | V:936, I:997 | 225 | utilitários variados específicos do Debian
`bsdutils` | V:445, I:1000 | 338 | utilitários básicos do 4.4BSD-Lite
`bsdextrautils` | V:754, I:863 | 382 | utilitários extras do 4.4BSD-Lite
`moreutils` | V:18, I:39 | 232 | utilitários Unix adicionais

| Dica
Apesar de `moreutils` poder não existir fora de Debian, oferece pequenos programas interessantes. O mais notável é o sponge(8) que é bastante útil quando desejar sobrescrever o ficheiro original.

Ver Seção 1.6, “Processamento de texto estilo Unix” exemplos.

## 12.2. Programação em linguagens interpretadas

Tabela 12.8. Lista de pacotes relacionados com o interpretador

pacote | popcon | tamanho | documentação
--- | --- | --- | ---
`dash` | V:928, I:998 | 207 | sh: shell pequena e rápida compatível com POSIX para `sh`
`bash` | V:893, I:1000 | 7309 | sh: "`info bash`" fornecido por `bash-doc`
`mawk` | V:485, I:998 | 295 | AWK: `awk` pequeno e rápido
`gawk` | V:250, I:301 | 3289 | AWK: "`info gawk`" proporcionado por `gawk-doc`
`perl` | V:673, I:992 | 836 | Perl: perl(1) e páginas html fornecidas por `perl-doc` e `perl-doc-html`
`libterm-readline-gnu-perl` | V:2, I:27 | 439 | Extensão Perl para a Biblioteca GNU ReadLine/History: perlsh(1)
`libreply-perl` | V:0.01, I:0.10 | 171 | REPL para Perl: reply(1)
`libdevel-repl-perl` | V:0.03, I:0.57 | 237 | REPL para Perl: re.pl(1)
`python3` | V:717, I:974 | 80 | Python: python3(1) e páginas html fornecidas por `python3-doc`
`tcl` | V:23, I:176 | 20 | Tcl: tcl(3) e páginas de manual detalhadas fornecidas por `tcl-doc`
`tk` | V:17, I:169 | 20 | Tk: tk(3) e páginas de manual detalhadas fornecidas por `tk-doc`
`ruby` | V:71, I:157 | 32 | Ruby: ruby(1), erb(1), irb(1), rdoc(1), ri(1)

Quando deseja automatizar uma tarefa em Debian, deve primeiro fazer o script com uma linguagem interpretada. A linha de orientação para a escolha da linguagem interpretada é:

- Utilize `dash`, se a tarefa for simples e combinar programas CLI com um programa shell.

- Utilize `python3`, se a tarefa não for simples e se a estiver a escrever de raiz.

- Use `perl`, `tcl`, `ruby`, ... se houver um código existente usando uma dessas linguagens no Debian que precisa ser retocado para fazer a tarefa.

Se o código resultante for demasiado lento, pode reescrever apenas a parte crítica para a velocidade de execução numa linguagem compilada e chamá-la a partir da linguagem interpretada.

### 12.2.1. Depuração de códigos de linguagem interpretada

A maioria dos intérpretes oferece funcionalidades básicas de verificação da sintaxe e de rastreio do código.

- "dash -n script.sh" - Verificação da sintaxe de um script Shell

- "dash -x script.sh" - Rastreia um script de shell

- "python -m py_compile script.py" - Verificação da sintaxe de um script Python

- "python -mtrace --trace script.py" - Rastreia um script Python

- "perl -I ../libpath -c script.pl" - Verificação da sintaxe de um script Perl

- "perl -d:Trace script.pl" - Traça um script Perl

Para testar o código para `dash`, tente Seção 9.1.4, “Revestimento da linha de leitura” o que acomoda o ambiente interativo semelhante ao `bash`.

Para testar código para `perl`, tente o ambiente REPL para Perl que acomoda um ambiente REPL (=READ + EVAL + PRINT + LOOP) tipo Python para o Perl.

### 12.2.2. Programa GUI com o script de shell

O script shell pode ser melhorado para criar um programa GUI atrativo. O truque é usar um dos chamados programas de diálogo em vez de uma interação monótona usando comandos `echo` e `read`.

Tabela 12.9. Lista de programas de diálogo

pacote | popcon | tamanho | descrição
--- | --- | --- | ---
`x11-utils` | V:222, I:551 | 651 | xmessage(1): mostra uma mensagem ou questão numa janela (X)
`whiptail` | V:303, I:997 | 61 | mostra caixas de diálogo amigas do utilizador a partir de scripts de shell (newt)
`dialog` | V:8, I:77 | 520 | mostra caixas de diálogo amigas do utilizador a partir de scripts de shell (ncurses)
`zenity` | V:74, I:338 | 193 | exibir caixas de diálogo gráficas a partir de scripts de shell (GTK)
`ssft` | V:0.01, I:0.16 | 80 | Ferramenta Frontend de Scripts de Shell (wrapper para o zenity, kdialog e dialog com o gettext)
`gettext` | V:51, I:216 | 20468 | "`/usr/bin/gettext.sh`": traduz mensagem

Aqui está um exemplo de um programa GUI para demonstrar como é fácil apenas com um script de shell.

Este script usa `zenity` para selecionar um ficheiro (por defeito `/etc/motd`) e mostrá-lo.

O lançador GUI para este script pode ser criado da seguinte forma Seção 9.4.10, “Arrancar um programa a partir da GUI”.

    #!/bin/sh -e
    # Copyright (C) 2021 Osamu Aoki <osamu@debian.org>, Public Domain
    # vim:set sw=2 sts=2 et:
    DATA_FILE=$(zenity --file-selection --filename="/etc/motd" --title="Select a file to check") || \
      ( echo "E: File selection error" >&2 ; exit 1 )
    # Check size of archive
    if ( file -ib "$DATA_FILE" | grep -qe '^text/' ) ; then
      zenity --info --title="Check file: $DATA_FILE" --width 640  --height 400 \
        --text="$(head -n 20 "$DATA_FILE")"
    else
      zenity --info --title="Check file: $DATA_FILE" --width 640  --height 400 \
        --text="The data is MIME=$(file -ib "$DATA_FILE")"
    fi

Este tipo de abordagem ao programa GUI com o script de shell é útil apenas para casos de escolha simples. Se for escrever qualquer programa com complexidades, por favor considere escrevê-lo numa plataforma mais capaz.

### 12.2.3. Ações personalizadas para o arquivador GUI

Os programas de arquivamento GUI podem ser alargados para executar algumas ações populares em ficheiros selecionados utilizando pacotes de extensão adicionais. Também podem ser feitos para executar ações personalizadas muito específicas, adicionando os seus scripts específicos.

- Para o GNOME, consulte NautilusScriptsHowto.

- Para o KDE, veja Criando menus de serviço do Dolphin.

- Para o Xfce, consulte Thunar - Acções personalizadas e https://help.ubuntu.com/community/ThunarCustomActions.

- Para LXDE, consulte Ações personalizadas.

### 12.2.4. A loucura dos scripts curtos de Perl

Para processar dados, `sh` precisa gerar sub-processos executando `cut`, `grep`, `sed`, etc., e é lento. Por outro lado, o `perl` tem capacidades internas para processar dados, e é rápido. Por isso muitos scripts de manutenção do sistema na Debian usam `perl`.

Vamos pensar no seguinte trecho de script AWK de uma linha e seus equivalentes em Perl.

    awk '($2=="1957") { print $3 }' |

Isto é equivalente a qualquer uma das seguintes linhas.

    perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |

    perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |

    perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |

    perl -lane 'print $F[2] if $F[1] eq "1957"' |

    perl -lane 'print$F[2]if$F[1]eq+1957' |

Este último é um enigma. Aproveitei-me das seguintes funcionalidades do Perl.

- O espaço em branco é opcional.

- Existe a conversão automática de números para cadeia.

- Truques de execução do Perl através de opções de linha de comando: perlrun(1)

- Variáveis especiais do Perl: perlvar(1)

Esta flexibilidade é o ponto forte do Perl. Ao mesmo tempo, isto permite-nos criar códigos crípticos e emaranhados. Por isso, é preciso ter cuidado.

## 12.3. Codificação em linguagens compiladas

Tabela 12.10. Lista de pacotes relacionados com o compilador

pacote | popcon | tamanho | descrição
--- | --- | --- | ---
`gcc` | V:155, I:564 | 36 | Compilador GNU C
`libc6-dev` | V:269, I:584 | 12861 | Biblioteca GNU C: Bibliotecas de desenvolvimento e ficheiros de cabeçalho
`g++` | V:57, I:531 | 13 | Compilador GNU C++
`libstdc++-16-dev` | V:0.7, I:4.3 | 24382 | GNU Standard C++ Library v3 (ficheiros de desenvolvimento)
`cpp` | V:332, I:719 | 18 | Pré-processador GNU C
`gettext` | V:51, I:216 | 20468 | Utilitários de internacionalização GNU
`glade` | V:0.7, I:2.7 | 1613 | Construtor de interfaces de utilizador GTK
`valac` | V:0.2, I:3.2 | 533 | Linguagem semelhante ao C# para o sistema GObject
`flex` | V:7, I:68 | 1247 | Gerador de analisador léxico rápido compatível com LEX
`bison` | V:7, I:73 | 3122 | YACC-compatible gerador de análise
`susv2` | I:0.03 | 16 | buscar "The Single UNIX Specifications v2"
`susv3` | I:0.06 | 16 | buscar "The Single UNIX Specifications v3"
`susv4` | I:0.04 | 16 | baixar "As especificações únicas UNIX v4"
`golang` | I:22 | 12 | Compilador de linguagem de programação Go
`rustc` | V:5, I:20 | 13061 | Linguagem de programação de sistemas Rust
`gfortran` | V:4, I:50 | 15 | Compilador GNU Fortran 95
`fpc` | I:2.3 | 101 | Pascal livre

Aqui, Seção 12.3.3, “Flex — um Lex melhor” e Seção 12.3.4, “Bison — um Yacc melhor” são incluídos para indicar como um programa semelhante a um compilador pode ser escrito em linguagem C, compilando uma descrição de nível superior em linguagem C.

### 12.3.1. C

Pode configurar um ambiente apropriado para compilar programas escritos na linguagem de programação C com o seguinte.

    # apt-get install glibc-doc manpages-dev libc6-dev gcc build-essential

O pacote `libc6-dev`, isto é, a biblioteca C GNU, disponibiliza uma biblioteca standard C a qual é uma colecção de ficheiros cabeçalho e rotinas de biblioteca usadas pela linguagem de programação C.

Veja referências para C nos seguintes.

- "`info libc`" (Referência de funções da biblioteca C)

- gcc(1) e "`info gcc`"

- `cada-nome_de_função_da_biblioteca_C`(3)

- Kernighan & Ritchie, "A Linguagem de Programação C", 2ª edição (Prentice Hall)

### 12.3.2. Programa C simples (gcc)

Um simples exemplo, "`example.c`" pode ser compilado com uma biblioteca "`libm`" num executável "`run_example`" com o seguinte.

    $ cat > example.c << EOF
    #include <stdio.h>
    #include <math.h>
    #include <string.h>

    int main(int argc, char **argv, char **envp){
            double x;
            char y[11];
            x=sqrt(argc+7.5);
            strncpy(y, argv[0], 10); /* prevent buffer overflow */
            y[10] = '\0'; /* fill to make sure string ends with '\0' */
            printf("%5i, %5.3f, %10s, %10s\n", argc, x, y, argv[1]);
            return 0;
    }
    EOF
    $ gcc -Wall -g -o run_example example.c -lm
    $ ./run_example
            1, 2.915, ./run_exam,     (null)
    $ ./run_example 1234567890qwerty
            2, 3.082, ./run_exam, 1234567890qwerty

Aqui, o "`-lm`" é necessário para ligar a biblioteca "`/usr/lib/libm.so`" do pacote `libc6` para o sqrt(3). A biblioteca real está em "`/lib/`" com o nome de ficheiro "`libm.so.6`", o qual é uma ligação simbólica para "`libm-2.7.so`".

Olhe ao último parâmetro no texto resultante. Existem mais de 10 caracteres mesmo com "`%10s`" especificado.

O uso de funções de operação de memória de ponteiro sem verificações de limites como em sprintf(3) e strcpy(3), está descontinuado para prevenir exploits de sobrelotação do buffer que influenciam os efeitos de transbordo em cima. Em vez disso, use snprintf(3) e strncpy(3).

### 12.3.3. Flex — um Lex melhor

O Flex é um gerador rápido de análise léxica compatível com o Lex.

O tutorial do flex(1) encontra-se em "`info flex`".

Muitos exemplos simples podem ser encontrados em "`/usr/share/doc/flex/examples/`". [7]

### 12.3.4. Bison — um Yacc melhor

Vários pacotes disponibilizam um gerador LR parser ou LALR parser compatível em frente com o Yacc em Debian.

Tabela 12.11. Lista de geradores de análise LALR compatíveis com Yacc

pacote | popcon | tamanho | descrição
--- | --- | --- | ---
`bison` | V:7, I:73 | 3122 | gerador de análise GNU LALR
`byacc` | V:0.1, I:3.0 | 263 | Gerador de análise Berkeley LALR
`btyacc` | V:0.01, I:0.06 | 247 | gerador de análises de retrocesso baseado no `byacc`

O tutorial para o bison(1) encontra-se em "`info bison`".

Tem de disponibilizar as suas próprias chamadas "`main()`" e "`yyerror()`". "`main()`" chama "`yyparse()`" que chama "`yylex()`", geralmente criada com Flex.

Aqui está um exemplo para criar um programa simples de calculadora de terminal.

Vamos criar `example.y`:

    /* calculator source for bison */
    %{
    #include <stdio.h>
    extern int yylex(void);
    extern int yyerror(char *);
    %}

    /* declare tokens */
    %token NUMBER
    %token OP_ADD OP_SUB OP_MUL OP_RGT OP_LFT OP_EQU

    %%
    calc:
     | calc exp OP_EQU    { printf("Y: RESULT = %d\n", $2); }
     ;

    exp: factor
     | exp OP_ADD factor  { $$ = $1 + $3; }
     | exp OP_SUB factor  { $$ = $1 - $3; }
     ;

    factor: term
     | factor OP_MUL term { $$ = $1 * $3; }
     ;

    term: NUMBER
     | OP_LFT exp OP_RGT  { $$ = $2; }
      ;
    %%

    int main(int argc, char **argv)
    {
      yyparse();
    }

    int yyerror(char *s)
    {
      fprintf(stderr, "error: '%s'\n", s);
    }

Vamos criar, `example.l`:

    /* calculator source for flex */
    %{
    #include "example.tab.h"
    %}

    %%
    [0-9]+ { printf("L: NUMBER = %s\n", yytext); yylval = atoi(yytext); return NUMBER; }
    "+"    { printf("L: OP_ADD\n"); return OP_ADD; }
    "-"    { printf("L: OP_SUB\n"); return OP_SUB; }
    "*"    { printf("L: OP_MUL\n"); return OP_MUL; }
    "("    { printf("L: OP_LFT\n"); return OP_LFT; }
    ")"    { printf("L: OP_RGT\n"); return OP_RGT; }
    "="    { printf("L: OP_EQU\n"); return OP_EQU; }
    "exit" { printf("L: exit\n");   return YYEOF; } /* YYEOF = 0 */
    .      { /* ignore all other */ }
    %%

Em seguida, execute o seguinte a partir do prompt do shell para tentar isso:

    $ bison -d example.y
    $ flex example.l
    $ gcc -lfl example.tab.c lex.yy.c -o example
    $ ./example
    1 + 2 * ( 3 + 1 ) =
    L: NUMBER = 1
    L: OP_ADD
    L: NUMBER = 2
    L: OP_MUL
    L: OP_LFT
    L: NUMBER = 3
    L: OP_ADD
    L: NUMBER = 1
    L: OP_RGT
    L: OP_EQU
    Y: RESULT = 9

    exit
    L: exit

## 12.4. Ferramentas de análise de código estático

As ferramentas do tipo Lint podem ajudar automaticamente a análise de código estático.

As ferramentas do tipo indentação podem ajudar as revisões de código humano, reformatando os códigos-fonte de forma consistente.

As ferramentas do tipo Ctags podem ajudar nas revisões humanas de código, gerando um ficheiro de índice (ou tag) de nomes encontrados nos códigos fonte.

| Dica
Configurar o seu editor favorito (`emacs` ou `vim`) para usar plugins assíncronos do motor lint, ajuda-o a escrever o seu código. Estes plugins estão a ficar muito poderosos tirando partido do Protocolo do Servidor de Idiomas. Como eles estão evoluindo rapidamente, usar o seu código original ao invés do pacote Debian pode ser uma boa opção.

Tabela 12.12. Lista de ferramentas para análise de código estático

pacote | popcon | tamanho | descrição
--- | --- | --- | ---
`vim-ale` | I:0.80 | 2833 | Mecanismo assíncrono Lint para Vim 8 e NeoVim
`vim-syntastic` | I:2.0 | 1379 | Truques de verificação de sintaxe para o vim
`elpa-flycheck` | V:0.1, I:1.7 | 1130 | verificação de sintaxe moderna em tempo real para o Emacs
`elpa-relint` | I:0.06 | 150 | Localizador de erros de regexp do Emacs Lisp
`cppcheck-gui` | V:0.1, I:1.0 | 7697 | ferramenta para análise estática de código C/C++ (GUI)
`shellcheck` | V:3, I:16 | 22860 | ferramenta lint para scripts shell
`pyflakes3` | V:1, I:12 | 20 | verificador passivo de programas Python 3
`pylint` | V:3, I:17 | 2093 | Verificador estático de código Python
`perl` | V:673, I:992 | 836 | interpretador com verificador de código estático interno: `B::Lint`(3perl)
`rubocop` | V:0.1, I:1.1 | 4192 | Analisador de código estático Ruby
`clang-tidy` | V:2, I:10 | 21 | ferramenta de verificação de formato de regras C++ baseada em Clang
`splint` | V:0.06, I:0.92 | 2325 | ferramenta para verificação estática de programas C por bugs
`flawfinder` | V:0.07, I:0.49 | 187 | ferramenta para examinar código fonte C/C++ e procurar por fraquezas na segurança
`black` | V:2, I:13 | 10057 | formatador de código Python sem compromissos
`perltidy` | V:0.4, I:3.1 | 3086 | Indentador e reformatador de scripts Perl
`indent` | V:0.8, I:5.1 | 438 | Programa de formatação de código fonte em linguagem C
`astyle` | V:0.2, I:2.7 | 770 | Indentador de código-fonte para C, C++, Objective-C, C# e Java
`bcpp` | V:0.02, I:0.26 | 114 | Embelezador C(++)
`xmlindent` | V:0.05, I:0.79 | 52 | Reformatador de fluxo XML
`global` | V:0.2, I:1.6 | 1923 | Ferramentas de pesquisa e navegação de código-fonte
`exuberant-ctags` | V:2, I:13 | 341 | criar índices de ficheiros de etiquetas de definições de código-fonte
`universal-ctags` | V:2, I:10 | 4238 | criar índices de ficheiros de etiquetas de definições de código-fonte

## 12.5. Depuração

A depuração é uma parte importante das atividades de programação. Saber como depurar programas faz de si um bom utilizador de Debian capaz de produzir relatórios de bugs significantes.

Tabela 12.13. Lista de pacotes de depuração

pacote | popcon | tamanho | documentação
--- | --- | --- | ---
`gdb` | V:88, I:161 | 12453 | "`info gdb`" disponibilizado por `gdb-doc`
`ddd` | V:0.7, I:5.2 | 4210 | "`info ddd`" disponibilizado por `ddd-doc`

### 12.5.1. Execução gdb básica

O depurador principal em Debian é o gdb(1) que lhe permite inspeccionar um programa enquanto ele é executado.

Vamos instalar o `gdb` e programas relacionados com o seguinte.

    # apt-get install gdb gdb-doc build-essential devscripts

Pode ser encontrado um bom tutorial do `gdb`:

- “`info gdb`”

- "Depurando com GDB" em `/usr/share/doc/gdb-doc/html/gdb/index.html`

- "tutorial na web"

Aqui está um exemplo simples de utilização do gdb(1) num "`program`" compilado com a opção "`-g`" para produzir informação de depuração.

    $ gdb program
    (gdb) b 1                # set break point at line 1
    (gdb) run args           # run program with args
    (gdb) next               # next line
    ...
    (gdb) step               # step forward
    ...
    (gdb) p parm             # print parm
    ...
    (gdb) p parm=12          # set value to 12
    ...
    (gdb) quit

| Dica
Muitos comandos do gdb(1) podem ser abreviados. A expansão da Tab funciona como na shell.

### 12.5.2. Depurar o pacote Debian

Uma vez que todos os binários instalados devem ser despojados no sistema Debian por defeito, a maioria dos símbolos de depuração são removidos no pacote normal. De modo a depurar pacotes Debian com gdb(1), os pacotes `*-dbgsym` precisam de ser instalados (e.g. `coreutils-dbgsym` no caso do `coreutils`). Os pacotes fonte geram pacotes `*-dbgsym` automaticamente juntamente com os pacotes binários normais e esses pacotes de depuração são colocados separadamente no arquivo debian-debug. Por favor, consulte os artigos no Debian Wiki para mais informações.

Se um pacote a ser depurado não fornecer o seu pacote `*-dbgsym`, é necessário instalá-lo depois de o reconstruir através do seguinte.

    $ mkdir /path/new ; cd /path/new
    $ sudo apt-get update
    $ sudo apt-get dist-upgrade
    $ sudo apt-get install fakeroot devscripts build-essential
    $ apt-get source package_name
    $ cd package_name*
    $ sudo apt-get build-dep ./

Corrigir bugs se necessário.

Mude a versão de pacote para uma que não colida com as versões oficiais de Debian, por exemplo, uma adicionada com "`+debug1`" quando se recompila uma versão de pacote existente, ou uma adicionada com "`~pre1`" quando se compila uma versão de pacote ainda não lançada com o seguinte.

    $ dch -i

Compilar e instalar pacotes com símbolos de depuração com o seguinte.

    $ export DEB_BUILD_OPTIONS="nostrip noopt"
    $ debuild
    $ cd ..
    $ sudo debi package_name*.changes

Necessita verificar os scripts de construção do pacote e assegurar o uso de "`CFLAGS=-g -Wall`" para compilar binários.

### 12.5.3. Obter um backtrace

Quando encontrar um crash num programa, é uma boa ideia enviar um relatório de bug com informação de backtrace copiada-e-colada.

O backtrace pode ser obtido pelo gdb(1) utilizando uma das seguintes abordagens:

- Abordagem às falhas em GDB:

  - Execute o programa a partir do GDB.

  - Falha no programa.

  - Digite "`bt`" no prompt da GDB.

- Abordagem falhas primeiro:

  - Actualize o ficheiro "/etc/security/limits.conf" para incluir o seguinte:

        * soft core unlimited

  - Escreva "`ulimit -c unlimited`" na prompt da shell.

  - Execute o programa a partir desta janela de comandos.

  - Colapsa o programa para produzir um ficheiro de despejo do núcleo.

  - Carregar o ficheiro core dump para GDB como "`gdb gdb ./program_binary core`" .

  - Digite "`bt`" no prompt da GDB.

Para uma situação de loop infinito ou de teclado congelado, pode forçar o encerramento do programa premindo `Ctrl-\` ou `Ctrl-C` ou executando "`kill -ABRT PID`". (Ver Seção 9.4.12, “Matar um processo”)

| Dica

Muitas vezes, vê um backtrace onde uma ou mais linhas do topo estão em "`malloc()`" ou "`g_malloc()`". Quando isto acontece, há grandes hipóteses do seu backtrace não ser muito útil. O modo mais fácil de encontrar alguma informação útil é definir a variável de ambiente "`$MALLOC_CHECK_`" para um valor de 2 (malloc(3)). Pode fazer isto enquanto corre o `gdb` ao fazer o seguinte.

     $ MALLOC_CHECK_=2 gdb hello

### 12.5.4. Comandos gdb avançados

Tabela 12.14. Lista de comandos gdb avançados

comando | descrição dos objetivos do comando
--- | ---
`(gdb) thread apply all bt` | obter um backtrace para todos os processos de um programa de multi-processo
`(gdb) bt full` | obter parâmetros que vêm na pilha das chamadas de função
`(gdb) thread apply all bt full` | obtém um backtrace e parâmetros como a combinação das opções precedentes
`(gdb) thread apply all bt full 10` | obter um backtrace e parâmetros para as 10 chamadas do topo para cortar resultados irrelevantes
`(gdb) set logging on` | escreve um relatório dos resultados do `gdb` para um ficheiro (a predefinição é "`gdb.txt`")

### 12.5.5. Verificar a dependência em bibliotecas

Use o ldd(1) para encontrar uma dependência de um programa em bibliotecas com o seguinte.

    $ ldd /usr/bin/ls
            librt.so.1 => /lib/librt.so.1 (0x4001e000)
            libc.so.6 => /lib/libc.so.6 (0x40030000)
            libpthread.so.0 => /lib/libpthread.so.0 (0x40153000)
            /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Para que o ls(1) funcione num ambiente 'chroot', as bibliotecas em cima têm de estar disponíveis no seu ambiente 'chroot'.

Veja Seção 9.4.6, “Rastear as atividades de programas”.

### 12.5.6. Ferramentas dinâmicas de rastreio de chamadas

Existem várias ferramentas de rastreio de chamadas dinâmicas disponíveis em Debian. Veja Seção 9.4, “Monitorizar, controlar e iniciar as atividades de programas”.

### 12.5.7. Depurar Erros do X

Se o programa do GNOME `preview1` recebeu um erro do X, deverá ver a mensagem que a seguir.

    The program 'preview1' received an X Window System error.

Neste caso, pode tentar correr o programa com "`--sync`" e quebrar a função "`gdk_x_error`" de modo a obter um backtrace.

### 12.5.8. Ferramentas de detecção de fugas de memória

Aqui estão várias ferramentas de detecção de fugas de memória em Debian.

Tabela 12.15. Lista de ferramentas de detecção de fugas de memória

pacote | popcon | tamanho | descrição
--- | --- | --- | ---
`libc6-dev` | V:269, I:584 | 12861 | mtrace(1): funcionalidades de depuração do malloc em glibc
`valgrind` | V:4, I:31 | 89318 | depurador e perfilador de memória
`electric-fence` | V:0.2, I:2.3 | 69 | o depurador malloc(3)
`libdmalloc5` | V:0.01, I:0.61 | 373 | biblioteca de depuração de alocação de memória
`duma` | V:0.00, I:0.06 | 297 | biblioteca para detetar "overruns" e "under-runs" de buffer em programas C e C++
`leaktracer` | V:0.01, I:0.33 | 56 | rastreador de fugas de memória para programas C++

### 12.5.9. Desassemblar binário

Pode desassemblar código binário com o objdump(1) com o seguinte.

    $  objdump -m i386 -b binary -D /usr/lib/grub/x86_64-pc/stage1

| Nota
O gdb(1) pode ser usado para desmontar (desassemblar) código interativamente.

## 12.6. Ferramentas de construção

Tabela 12.16. Lista de pacotes de ferramentas de compilação

pacote | popcon | tamanho | documentação
--- | --- | --- | ---
`make` | V:150, I:566 | 1762 | "`info make`" disponibilizado por `make-doc`
`autoconf` | V:29, I:200 | 2244 | "`info autoconf`" disponibilizado por `autoconf-doc`
`automake` | V:28, I:199 | 1932 | "`info automake`" disponibilizado por `automake1.10-doc`
`libtool` | V:24, I:183 | 1245 | "`info libtool`" fornecido por `libtool-doc`
`cmake` | V:21, I:123 | 59735 | cmake(1) sistema make multiplataforma e de código aberto
`ninja-build` | V:9, I:55 | 456 | ninja(1) pequeno sistema de construção mais próximo em espírito do Make
`meson` | V:6, I:29 | 4278 | "`info bison`" disponibilizado por `bison-doc`
`xutils-dev` | V:0.9, I:7.0 | 1495 | imake(1), xmkmf(1), etc.

### 12.6.1. Make

O Make é um utilitário para manutenção de grupos de programas Após a execução do make(1), o `make` lê o ficheiro de regras, "`Makefile`" e atualiza um alvo se depender de ficheiros pré-requisitados que foram modificados desde que o alvo foi modificado por último, ou se o alvo não existir. A execução destas atualizações pode ocorrer concorrentemente.

A regra de sintaxe do ficheiro é a seguinte.

    target: [ prerequisites ... ]
     [TAB]  command1
     [TAB]  -command2 # ignore errors
     [TAB]  @command3 # suppress echoing

Aqui "`[TAB]`" temos um código TAB. Cada linha é interpretada pelo Shell após fazer a substituição da variável. Use "`\`" no final de uma linha para continuar o script. Use "`$$`" para inserir "`$`" para valores de ambiente para um script de shell.

Podem ser escritas regras implícitas para o destino e pré-requisitos, por exemplo, com o seguinte.

    %.o: %.c header.h

Aqui, o alvo contém o caractere "`%`" (exatamente um deles). O "`%`" pode corresponder a qualquer subcadeia não vazia nos nomes de ficheiros do próprio alvo. Os pré-requisitos usam igualmente "`%`" para mostrar como os seus nomes estão relacionados ao próprio nome do alvo.

Tabela 12.17. Lista de variáveis automáticas do make

variável automática | valor
--- | ---
`$@` | alvo
`$<` | primeiro pré-requisito
`$?` | todos os novos pré-requisitos
`$^` | todos os pré-requisitos
`$*` | "`%`" estaminal correspondente no modelo de destino

Tabela 12.18. Lista de expansões da variável do make

expansão da variável | descrição
--- | ---
`foo1 := bar` | expansão de uma vez
`foo2 = bar` | expansão recursiva
`foo3 += bar` | acrescentar

Corra "`make -p -f/dev/null`" para ver as regras internas automáticas.

### 12.6.2. Autotools

O Autotools é um conjunto de ferramentas de programação concebido para ajudar a tornar os pacotes de código-fonte portáveis para muitos sistemas do tipo Unix.

- O Autoconf é uma ferramenta para produzir um script shell "`configure`" a partir de "`configure.ac`".

  - O "`configure`" é utilizado mais tarde para produzir o"`Makefile`" a partir do modelo "`Makefile.in`".

- Automake é uma ferramenta para produzir "`Makefile.in`" a partir de "`Makefile.am`".

- Libtool é um script shell para resolver o problema de portabilidade de software ao compilar bibliotecas partilhadas a partir do código fonte.

#### 12.6.2.1. Compilar e instalar um programa

| Atenção
Não substitua ficheiros do sistema com os seus programas compilados quando os instalar.

Debian não toca nos ficheiros em "`/usr/local/`" ou em "`/opt`". Portanto se compilar um programa a partir do código-fonte, instale-o em "`/usr/local/`" para que não interfira com o Debian.

    $ cd src
    $ ./configure --prefix=/usr/local
    $ make # this compiles program
    $ sudo make install # this installs the files in the system

#### 12.6.2.2. Desinstalar um programa

Se tiver o código original, se ele utiliza autoconf(1)/automake(1) e se você lembrar como o configurou, execute como segue para desinstalar o programa.

    $ ./configure all-of-the-options-you-gave-it
    $ sudo make uninstall

Em alternativa, se tiver a absoluta certeza que o processo de instalação apenas coloca ficheiros sob "`/usr/local/`" e não há lá nada importante, pode apagar todos os seus conteúdos com o seguinte.

    # find /usr/local -type f -print0 | xargs -0 rm -f

Se não tiver a certeza de onde os ficheiros estão instalados, deve considerar usar o checkinstall(8) do pacote `checkinstall`, que disponibiliza um caminho limpo para a desinstalação. Agora suporta criar um pacote Debian com a opção "`-D`".

### 12.6.3. Meson

O sistema de construção de software tem vindo a evoluir:

- Autotools no topo do Make tem sido o padrão de facto para a infraestrutura de compilação portátil desde os anos 1990. Isso é extremamente lento.

- O CMake, inicialmente lançado em 2000, melhorou significativamente a velocidade, mas foi originalmente construído sobre o inerentemente lento Make. (Agora Ninja pode ser seu backend.)

- O Ninja, lançado inicialmente em 2012, destina-se a substituir o Make para melhorar a velocidade de construção e foi concebido para que os seus ficheiros de entrada sejam gerados por um sistema de construção de nível superior.

- O Meson, lançado inicialmente em 2013, é o novo sistema de construção de nível superior popular e rápido que usa o Ninja como backend.

Ver documentos encontrados em "O sistema de construção Meson" e "O sistema de construção Ninja".

## 12.7. Web

Páginas web dinâmicas interactivas podem ser feitas conforme a seguir.

- As questões são apresentadas ao explorador do utilizador a usar formulários HTML.

- Preencher e clicar nas entradas do formulário envia uma das seguintes cadeias de URL com parâmetros codificados do explorador para o servidor web.

  - "`https://www.foo.dom/cgi-bin/program.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3`"

  - "`https://www.foo.dom/cgi-bin/program.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3`"

  - "`https://www.foo.dom/program.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3`"

- O "`%nn`" no URL é substituído por um caractere com valor hexadecimal `nn`.

- A variável de ambiente está definida como: "`QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3"`".

- O programa CGI (qualquer um de "`program.*`") no servidor web executa-se a si próprio com a variável de ambiente "`$QUERY_STRING`".

- O `stdout` do programa CGI é enviado ao explorador web e é apresentado como uma página web dinâmica e interactiva.

Por razões de segurança é melhor não embarcar em novos hacks para analisar parâmetros CGI. Existem módulos definidos para eles em Perl e Python. O PHP vem com estas funcionalidades. Quando é necessário o armazenamento de dados no cliente, usam-se cookies HTTP. Quando é necessário o processamento de dados no lado do cliente, usa-se frequentemente Javascript.

Para mais, veja Common Gateway Interface, The Apache Software Foundation e JavaScript.

Procurar "CGI tutorial" no Google ao escrever directamente o URL codificado https://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial no endereço do explorador é uma boa maneira de ver o script CGI em acção no servidor da Google.

## 12.8. A tradução do código-fonte

Existem programas para converter código-fonte.

Tabela 12.19. Lista de ferramentas de tradução de código-fonte

pacote | popcon | tamanho | palavra chave | descrição
--- | --- | --- | --- | ---
`perl` | V:673, I:992 | 836 | AWK→PERL | converter código-fonte de AWK para PERL: a2p(1)
`f2c` | V:0.1, I:2.0 | 443 | FORTRAN→C | converter código-fonte de FORTRAN 77 para C/C++: f2c(1)
`intel2gas` | V:0.02, I:0.19 | 178 | intel→gas | conversor de NASM (formato Intel) ao GNU Assembler (GAS)

## 12.9. Criar um pacote Debian

Se desejar criar um pacote Debian, leia o seguinte.

- Capítulo 2, Gestão de pacotes Debian para compreender o sistema básico de pacotes

- Seção 2.7.13, “Portar um pacote ao sistema stable” para compreender o processo básico de portar

- Seção 9.11.4, “Sistema chroot” para compreender as técnicas de chroot básicas

- debuild(1), e sbuild(1)

- Seção 12.5.2, “Depurar o pacote Debian” para recompilar para depuração

- Guia dos Novos Maintainers da Debian (o pacote `debmake-doc`)

- Referência de Programadores da Debian (o pacote `developers-reference`)

- Manual de Políticas Debian (o pacote `debian-policy`)

Existem pacotes como os `debmake`, `dh-make`, `dh-make-perl`, etc., que auxiliam no processo em empacotamento.

[7] Poderão ser necessárias algumas adaptações para que funcionem com o sistema atual.

Capítulo 11. Conversão de dados |  | Apêndice A. Apêndice