Ziirish's Home :: Blog

Ziirish's Pub

 
 

Pas mal occupé ces derniers temps par différents projets personnels mais aussi professionnels...

Mais j'aime ne pas avoir le temps de m'ennuyer !

Aujourd'hui, je vous propose un peu de code puisque je me suis replongé quelques minutes dans le code de Shelldone

En ce moment, j'avance (très doucement) sur quelques problématiques de parsing. Je ne reviendrais pas sur le principe du shell, même si je ne pense pas m'être déjà étalé sur le sujet sur ce blog. Ce qu'on peut dire très brièvement, c'est qu'un shell, pour simplifier un peu c'est un truc tout bête qui ne sert qu'à lire ce qu'on tape au clavier, et parser cet ensemble d'informations avec le schéma suivant :


command arg1 arg2 arg3 ...

Dans l'idée, dès qu'on rencontre un caractère "espace" on considère qu'on a un nouveau mot, et donc un nouvel argument. Sauf qu'en pratique, on peut protéger un groupe de mots avec différents caractères tels que " et ' On peut aussi protéger un espace avec le caractère \ Ces cas particuliers rendent la tâche de parsing un peu compliquée.

Je vous propose donc ci-dessous une pièce du parser qui va me découper une phrase en différents mots tout en faisant attention à la séquence "\ " qui indique qu'on ne doit pas couper notre mot ici !

On commence avec quelques petites améliorations des fonctions standards qui nous éviterons moultes contrôles :


#ifndef _BSD_SOURCE
    #define _BSD_SOURCE
#endif

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

#define TRUE  1
#define FALSE 0

int
xmin (int a, int b)
{
    return a < b ? a : b;
}

int
xmax (int a, int b)
{
    return a > b ? a : b;
}

void
xfree (void *ptr)
{
    if (ptr != NULL)
    {   
        free (ptr);
    }   
}

size_t
xstrlen (const char *in)
{
    const char *s;
    if (in == NULL)
    {
        return 0;
    }
    for (s = in; *s; ++s);
    return (s - in);
}

void *
xmalloc (size_t size)
{
    void *ret = malloc (size);
    if (ret == NULL)
        err (2, "xmalloc can not allocate %lu bytes", (u_long) size);
    return ret;
}

void *
xcalloc (size_t nmem, size_t size)
{
    void *ret = calloc (nmem, size);
    if (ret == NULL)
        err (2, "xcalloc can not allocate %lu bytes", (u_long) (size * nmem));
    return ret;
}

void *
xrealloc (void *src, size_t new_size)
{
    void *ret;
    if (src == NULL)
    {
        ret = xmalloc (new_size);
    }
    else
    {
        ret = realloc (src, new_size);
    }
    if (ret == NULL)
        err (2, "xrealloc can not reallocate %lu bytes", (u_long) new_size);
    return ret;
}

char *
xstrdup (const char *dup)
{
    size_t len;
    char *copy;

    len = xstrlen (dup);
    if (len == 0)
        return NULL;
    copy = xmalloc (len + 1);
    if (copy != NULL)
        strncpy (copy, dup, len + 1);
    return copy;
}

int
xstrcmp (const char *c1, const char *c2)
{
    size_t s1 = xstrlen (c1);
    size_t s2 = xstrlen (c2);

    if (s1 == 0 && s2 == 0)
        return 0;
    if (s1 == 0)
        return -1;
    if (s2 == 0)
        return +1;
    return strncmp (c1, c2, xmax (s1, s2));
}

Et voici LA fonction magique :


char **
xstrsplitspace (const char *src, size_t *size)
{
    char **ret;
    int i, idx, len, j;
    size_t tot = xstrlen (src);
    *size = 0;
    for (i = 0; i < (int) tot; i++)
    {
        if (src[i] == ' ')
        {
            if (i == 0 || (i > 0 && src[i-1] != '\\' && src[i-1] != ' '))
                (*size)++;
        }
    }
    if (*size == 0)
    {
        *size = 1;
        ret = xcalloc (1, sizeof (char *));
        ret[0] = xstrdup (src);
        return ret;
    }
    if ((i > 1 && src [i-1] == ' ' && src[i-2] == '\\') ||
        (i > 0 && src[i-1] != ' '))
        (*size)++;
    ret = xcalloc (*size, sizeof (char *));
    idx = 0;
    len = 0;
    j = 0;
    for (i = 0; i < (int) tot; i++)
    {
        if (src[i] == ' ')
        {
            if (i == 0)
            {
                while (src[i] == ' ' && i < (int) tot)
                    i++;
                idx = i;
            }
            else if (i > 0 && src[i-1] != '\\' && src[i-1] != ' ')
            {
                fprintf (stdout, "word %d, idx: %d\n", j, idx);
                len = i - idx;
                ret[j] = xmalloc ((len + 1) * sizeof (char));
                snprintf (ret[j], len+1, "%s", src+idx);
                j++;
                while (src[i] == ' ' && i < (int) tot)
                    i++;
                idx = i;
            }
        }
    }
    if ((i > 1 && src[i-1] == ' ' && src[i-2] == '\\') ||
        (i > 0 && src[i-1] != ' '))
    {
        fprintf (stdout, "word %d, idx: %d\n", j, idx);
        len = i - idx;
        ret[j] = xmalloc ((len + 1) * sizeof (char));
        snprintf (ret[j], len+1, "%s", src+idx);
    }

    return ret;
}

Voilà comment on va l'utiliser :


int
main (int argc, char **argv)
{
    int i;
    size_t l;
    char **res = xstrsplitspace (argv[1], &l);
    fprintf (stdout, "original string: '%s' => %d element(s)\n", argv[1], l);
    for (i = 0; i < (int) l; i++)
    {
        fprintf (stdout, "res[%d]: '%s'\n", i, res[i]);
        xfree (res[i]);
    }
    xfree (res);

    return 0;
}

Et voilà ce que ça donne :


ziirish@carbon:~/workspace/strsplit$ gcc --std=c89 -o strsplit strsplit.c
ziirish@carbon:~/workspace/strsplit$ ./strsplit "hello"
original string: 'hello' => 1 element(s)
res[0]: 'hello'
ziirish@carbon:~/workspace/strsplit$ ./strsplit "hello world"
word 0, idx: 0
word 1, idx: 6
original string: 'hello world' => 2 element(s)
res[0]: 'hello'
res[1]: 'world'
ziirish@carbon:~/workspace/strsplit$ ./strsplit "hello world this\ is\ a\ test"
word 0, idx: 0
word 1, idx: 6
word 2, idx: 12
original string: 'hello world this is a test' => 3 element(s)
res[0]: 'hello'
res[1]: 'world'
res[2]: 'this\ is\ a\ test'
ziirish@carbon:~/workspace/strsplit$ ./strsplit "hello world this\ is\ a\ test   well \ "
word 0, idx: 0
word 1, idx: 6
word 2, idx: 12
word 3, idx: 32
word 4, idx: 37
original string: 'hello world this is a test   well  ' => 5 element(s)
res[0]: 'hello'
res[1]: 'world'
res[2]: 'this\ is\ a\ test'
res[3]: 'well'
res[4]: ' \ '