You are currently browsing Claudio Matsuoka’s articles.

Palavras-chave: C, stdin, stdout, descritor, descriptor, inode, comparar, compare

Ao escrever filtros (programas que processam dados provenientes da entrada padrão e os escrevem na saída padrão) pode-se querer evitar que o arquivo de entrada seja o mesmo arquivo utilizado para saída. Um exemplo de um programa assim seria um conversor de formato: o que fazer caso o filtro seja invocado como

        $ gif2png < tempfile > tempfile

ou ainda

        $ sig2uns tempfile > tempfile

potencialmente corrompendo dados quando eles são lidos e processados em blocos?

Uma solução simples em sistemas que utilizam sistemas de arquivo similares aos do UNIX é comparar o número do inode e o dispositivo dos descritores de entrada e saída, sejam eles arquivos ou stdin/stdout. Isso pode ser implementado em C como:

        fstat(ifd, &st);
        st.st_mode &= S_IFMT;
        if (st.st_mode != S_IFCHR && st.st_mode != S_IFBLK) {
                dev = st.st_dev;
                ino = st.st_ino;
        }

        fstat(ofd, &st);
        st.st_mode &= S_IFMT;
        if (st.st_dev == dev && st.st_ino == ino) {
                /* entrada e saída são iguais */
                return 1;
        }

Isso é exatamente o que o programa “cat” faz. Se você experimentar fazer cat de um arquivo para ele mesmo, o resultado é:

        $ cat a.out > a.out
        cat: a.out: input file is output file

Finalmente, note que isso não vale para dispositivos de caractere ou bloco, como tratado no trecho de código acima.

Palavras-chave: C, macro, multi-statement, cpp, pré-processador, preprocessor

Com alguma freqüência é necessário de definir macros com múltiplos comandos (statements) que possam ser usados de forma sintaticamente equivalente a chamadas de função. Nesse caso, não basta definir uma seqüência como:

#define foo(x) a1(x); a2(x)

Isso seria desastroso após um if, por exemplo. Tampouco adiantaria delimitar o bloco com chaves, o que não funcionaria numa situação como:

if (y) foo(z); else return;

Como, então, podemos definir tais macros? A resposta é: utilizando expressões “do {…} while(0)”, cuja utilidade em outras situações é bastante duvidosa. Veja o exemplo:

#define foo(x) do { a1(x); a2(x); } while (0)

Definindo macros dessa forma, cumprimos nosso objetivo: apesar da declaração aparentemente estranha, seu uso é equivalente a uma chamada de função em qualquer caso, incluindo loops e condicionais.

É comum em utilitários que rodam em um terminal e escrevem na saída padrão (stdout) a existência de um parâmetro que permite redirecionar a saída para um arquivo (geralmente -o).

Uma maneira simples de implementar essa redireção é copiando o descritor do arquivo especificado para o descritor 1 (do stdout). Pode-se implementar isso da seguinte maneira, dentro do tratamento dos parâmetros de linha de comando:

    case 'o':
        ofd = open(optarg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
        if (ofd < 0) {
            perror("open");
            exit(1);
        }
        dup2(ofd, STDOUT_FILENO);
        break;

onde optarg é o parâmetro passado para -o. Assim, o restante do código pode ser escrito sem se preocupar com o arquivo de saída, usando printf() normalmente para escrever em stdout.

Add to Technorati Favorites

Assuntos