Executando verificação de segurança...
4

[Code Review - C] Procuro pessoas para fazer uma review de uma implementação do utilitário do Unix seq

Eu estava estudando algumas coisas, descobri um sistema operacional antigo chamado Unix e lá tinha esse utilitário chamado seq. Eu pensei que valeria apena tentar fazer uma versão, e eu fiz: acabou que demorou um pouco mas foi feito (ainda dá alguns segfaults).

Repositório: https://github.com/JoaoPauloFerrariSantAna/SeqUtilImplementation
Link para clonar (token HTTP): https://github.com/JoaoPauloFerrariSantAna/SeqUtilImplementation.git

Att.te JV.

Carregando publicação patrocinada...
4

Seguem alguns comentários:

Por que a função print_usage manda a saída para o stderr? Esta é a saída de erro padrão, mas a meu ver o usage não é um erro, e sim uma mensagem informativa.

Outro detalhe é que vc trocou a ordem dos parâmetros. Veja que na documentação do seq a ordem é:

seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST

Ou seja, se passar apenas um valor, é o último. Se passar 2 valores, são o primeiro e último, e se passar 3, serão o primeiro, o incremento e o último.

Mas no seu programa vc trocou a ordem deles:

    -./myseq [ENDNUMBER] [STARTNUMBER]
    -./myseq [ENDNUMBER]
    -./myseq [ENDNUMBER] [STARTNUMBER] [STEP]

O único caso que está certo é quando passa apenas 1 valor. Já nos outros casos a ordem dos valores está diferente.

Além disso, o código não funciona se passar apenas 2 parâmetros. Isso porque vc tenta sempre pegar o terceiro (argv[3]), quando na verdade deveria verificar o argc antes para saber se existe o terceiro valor. É isso que está dando o segmentation fault.

Outro detalhe é que o seq, por padrão, imprime um número por linha, mas vc está imprimindo todos na mesma linha.

Enfim, para ficar mais parecido, poderia ser assim:

#include <stdio.h>
#include <stdlib.h>

#define PROGRAM_NAME "myseq"

void print_usage() {
    printf("Usage: %s LAST\n", PROGRAM_NAME);
    printf("  or:  %s FIRST LAST\n", PROGRAM_NAME);
    printf("  or:  %s FIRST INCREMENT LAST\n", PROGRAM_NAME);
    printf("Print numbers from FIRST to LAST, in steps of INCREMENT.\n");
}

int main(int argc, char *argv[]) {
    int first = 1, last, increment = 1;
    switch (argc) {
        case 1:
            print_usage();
            return 0;
        case 2:
            last = atoi(argv[1]);
            break;
        case 3:
            first = atoi(argv[1]);
            last = atoi(argv[2]);
            break;
        case 4:
            first = atoi(argv[1]);
            increment = atoi(argv[2]);
            last = atoi(argv[3]);
            break;
        default:
            /* Aqui sim faz sentido mandar pro stderr, pois é um erro mesmo */
            fprintf(stderr, "Extra operand: %s\n", argv[4]);
            return 1;
    }

    while (increment > 0 ? first <= last : first >= last) {
        printf("%d\n", first);
        first += increment;
    }

    return 0;
}

Ou seja, se apenas um valor é passado, é o último. Se 2 valores são passados, são o primeiro e o último, e se 3 são passados, são o primeiro, o incremento e o último. Se forem passados mais valores, mostra uma mensagem de erro (e aqui sim faz sentido mandar a saída para o stderr).

E caso o primeiro ou o incremento não sejam informados, o valor destes será 1.

Por fim, não precisa ficar verificando se o primeiro é maior que o último, pois neste caso, ele não imprime nada: seq 4 1 não imprime nenhum número, a menos que o incremento seja negativo: seq 4 -1 1 por exemplo, imprime 4, 3, 2 e 1 nesta ordem.

A condição increment > 0 ? first <= last : first >= last usa o operador condicional (também conhecido como "ternário"), e verifica se o incremento é positivo. Caso seja, verifica se o primeiro é menor ou igual ao último, caso contrário, verifica se o primeiro é maior ou igual ao último. Assim não precisa de funções separadas para cada caso, pois este loop serve para ambos.


Melhorias futuras

Para ficar igual mesmo, teria que implementar as opções (veja no já citado manual), como --help para mostrar o usage, além de opções de formatação e suporte a float (pois o seq não funciona só com números inteiros: teste seq 1.5 0.4 5.8 por exemplo), mudar o separador, entre outros.

Sem contar outras validações, como o incremento não pode ser zero, dar erro se passar um valor que não é um número (seq xyz), etc.

1

Primeiramente: Obrigado por sua code review.

Eu pensei em utilizar a stderr em fprintf justamente pelo fato de que: se não passar nenhum argumento o programa vai dar erro (segfault por não ter uma entrada correspondente em argv para usar, já que não passou pela minha cabeça que eu na verdade tinha que utilizar argc para a contagem de argumentos facepalms).

sim, pelo o incrivel que pareça não passou pela minha cabeça utilizar utilizar o argument count; eu sou novo nesse negócio de fazer scripts

Mas tirando isso, novamente, obrigado pela sua revisão de meu código.

3

Meus 2 cents,

Vendo sua publicacao, me inspirou a falar um pouco do Unix:

https://www.tabnews.com.br/Oletros/arvore-genealogica-resumida-dos-sistemas-operacionais-unix-linux-bsd-macos-ios-android-windows

Apesar de antigo, o UNIX eh o avo (direto ou conceitual) dos sistemas operacionais atuais.

O aplicativo SEQ esta disponivel nos UNIX-Like (sistemas derivados do UNIX) e no projeto GNU (implementacao propria de aplicativos disponiveis no UNIX).

Como curiosidade, no LINUX o SEQ eh fornecido pela biblioteca 'coreutils' - do projeto GNU.

Este codigo fonte pode ser visto aqui:

https://github.com/coreutils/coreutils/blob/master/src/seq.c

Parabens pela iniciativa - estudar de onde veio e tentar criar versoes de aplicativos eh uma forma bastante efetiva de aprendizado !

Obrigado por compartilhar - Saude e Sucesso !!!

1

Amiguinho, você está complicando demais as coisas, o seq é algo muito mais simples que o que você está fazendo, é basicamente a sintaxe do for-printf de qualquer hello world,

Dado o laço:

for (int s;f;(increment(&s,passo)) {
printf(pattern ||"%d");
printf(separador || "\n");
}

Seu trabalho é apenas popular as variáveis