Ziirish's Home :: Blog

Ziirish's Pub

 
 

Désolé pour ce titre sorti de nulle part... on va mettre ça sur le dos du dimanche.

Pour continuer sur cette petite session "code" commencée hier avec une rapide présentation de shelldone, je vais vous présenter aujourd'hui mon implémentation de la commande cd.

Je ne vous ai pas encore rappelé le principe de fonctionnement d'un Shell en mode intéractif, je vais peut-être le faire plus tard. Mais très rapidement, voici ce que l'on peut dire :

  • le shell attend que l'utilisateur entre une ligne de commande et valide avec la toucher "entrée".
  • une ligne de commande peut être composée de plusieurs commandes séparées par des opérateurs logiques ou des séparateurs
  • une commande est généralement un programme externe lancé par le Shell

Mais voilà, il existe également des commandes internes au Shell. La commande cd en est une. C'est la raison pour laquelle j'ai trouvé amusé de la réimplémenter (attention, work in progress) et je vais vous présenter l'implémentation que j'ai choisie.


void 
sd_cd (int argc, char **argv)
{
    char *target;
    if (argc > 1)
    {
        fprintf (stderr, "ERROR: too many arguments\n");
        fprintf (stderr, "usage: cd [directory]\n");
        return;
    }
    if (argc == 0 || xstrcmp (argv[0], "~") == 0)
    {
        target = xstrdup (getenv ("HOME"));
    }
    else
    {
        if (xstrcmp (argv[0], "-") == 0)
        {
            target = xstrdup (getenv ("OLDPWD"));
        }
        else if (*argv[0] == '~')
        {
            char *home = getenv ("HOME");
            char *tmp = xmalloc (xstrlen (argv[0]));
            size_t len;
            int i = 0, j = 1;
            for (; argv[0][j] != '\0'; tmp[i] = argv[0][j],i++,j++);
            tmp[i] = '\0';
            len = xstrlen (home) + xstrlen (tmp) + 1;
            target = xmalloc (len);
            snprintf (target, len, "%s%s", home, tmp);
            xfree (tmp);
        }
        else
        {
            target = xstrdup (argv[0]);
        }
    }
    if (chdir (target) == -1)
    {
        xfree (target);
        perror ("cd");
        return;
    }
    else
    {
        setenv ("OLDPWD", getenv ("PWD"), 1);
        if (*target == '/')
        {
            setenv ("PWD", target, 1);
        }
        else if (xstrcmp (target, ".") != 0)
        {
            char *oldpwd = getenv ("OLDPWD");
            size_t len = xstrlen (oldpwd) + xstrlen (target) + 2;
            char *tmp = xmalloc (len);
            snprintf (tmp, len, "%s/%s", oldpwd, target);
            setenv ("PWD", tmp, 1);
            xfree (tmp);
        }
    }
    xfree (target);
}

Cette commande est vraiment super simple dans son fonctionnement puisqu'elle n'a besoin grosso-modo que de deux appels système : chdir et setenv. D'ailleurs, l'implémentation est détaillée dans la manpage. Bien entendu, je n'ai pas suivi tous les conseils de la manpage (ne sachant pas lire) et mon implémentation est encore incomplète (je ne gère pas encore le cas des '..'), mais je suis content du résultat :)


ziirish@pluto:~/Documents/dev/shelldone$ valgrind ./shelldone
==12701== Memcheck, a memory error detector
==12701== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==12701== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==12701== Command: ./shelldone
==12701== 
ziirish@pluto:~/Documents/dev/shelldone$ cd ~/Documents
nb commands: 1
=== Dump cmd n°1 ===
cmd: cd
argc: 1
argv[0]: ~/Documents
ziirish@pluto:~/Documents$ cd -
nb commands: 1
=== Dump cmd n°1 ===
cmd: cd
argc: 1
argv[0]: -
ziirish@pluto:~/Documents/dev/shelldone$ cd .
nb commands: 1
=== Dump cmd n°1 ===
cmd: cd
argc: 1
argv[0]: .
ziirish@pluto:~/Documents/dev/shelldone$ cd
nb commands: 1
=== Dump cmd n°1 ===
cmd: cd
argc: 0
ziirish@pluto:~$ quit
==12701== 
==12701== HEAP SUMMARY:
==12701==     in use at exit: 0 bytes in 0 blocks
==12701==   total heap usage: 52 allocs, 52 frees, 2,843 bytes allocated
==12701== 
==12701== All heap blocks were freed -- no leaks are possible
==12701== 
==12701== For counts of detected and suppressed errors, rerun with: -v
==12701== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)