IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Problème conversion de char à char*


Sujet :

C

  1. #1
    Membre averti
    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 36
    Par défaut Problème conversion de char à char*
    Bonjour à toutes et à tous.

    Voici mon problème, je veux incorporer du code dans mon application et pour cela je le convertis en base64.
    Pour ce faire, je lis caractère par caractère puis je mets le tout dans un char*, sauf qu'avant j'utilisais une bibliothèque qui s'occuper du "buffer" et donc de l'ajout de caractère mais comme je veux être indépendant de cette bibliothèque, j'ai développé ma propre fonction.
    C'est là que des problèmes (allocation mémoire ?) sont apparus...

    Voici un exemple "tout bête", si je veux convertir:
    c'est un test ! <- ça fonctionne
    qu'en pensez-vous ? <- ça ne fonctionne pas
    qu'en pensez-vous <- ça fonctionne
    c'est un test <- ça ne fonctionne pas

    Auriez-vous une idée ?
    Ps: désolé pour le titre je n'ai pas trouvé mieux !

    main.c
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <math.h>
    #include "base64.h"
     
    // Compilation: gcc -std=gnu99 main.c -o b64 -lm
     
    int main(int argc, char *files[])
    {
        size_t size_out;
        char* tmp_code = NULL;
        tmp_code = malloc(1); // Correcte ?
        strcpy (tmp_code, "");
        for (int i = 1; files[i]; i++)
        {
            printf("Ouverture du fichier: %s\n", files[i]);
            FILE *file;
            int c;
            if((file = fopen(files[i], "r")) != NULL)
            {
                c = fgetc(file);
                while (c != EOF)
                {
                    /* Ma fonction qui pose problème ??? */
                    char* test = malloc(1);
                    sprintf(test, "%c", (char)c);
                    tmp_code = realloc(tmp_code, strlen(tmp_code) + strlen(test) + 1);
                    strcat (tmp_code, test);
                    free(test);
                    /*                                    */
    		c = fgetc(file);
                }
                fclose(file);
            } else {
                printf("Impossible d'ouvrir le fichier: %s !\n", files[i]);
            }
        }
        printf("Le texte est:\n%s\n", tmp_code);
        char* encoded = base64_encode(tmp_code, &size_out);
        free(tmp_code);
        if (encoded == NULL)
        {
            printf("Impossible d'encoder !\n");
        } else {
            printf("Codage:\n%s\n", encoded);
            char* decoded = base64_decode(encoded, &size_out);
            printf("Decodage:\n%s\n", decoded);
            free(encoded);
            free(decoded);
        }
    }
    base64.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
     
    static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                                    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                                    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
                                    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                                    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                                    'w', 'x', 'y', 'z', '0', '1', '2', '3',
                                    '4', '5', '6', '7', '8', '9', '+', '/'};
     
    static char *decoding_table = NULL;
    static int mod_table[] = {0, 2, 1};
     
    static void build_decoding_table()
    {
        decoding_table = malloc(256);
        for (int i = 0; i < 0x40; i++)
        {
            decoding_table[encoding_table[i]] = i;
        }
    }
     
    static char *base64_encode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        *output_length = (size_t) (4.0 * ceil((double) input_length / 3.0));
        char *encoded_data = malloc(*output_length);
        if (encoded_data == NULL)
        {
            return NULL;
        }
        for (int i = 0, j = 0; i < input_length;)
        {
     
            uint32_t octet_a = i < input_length ? data[i++] : 0;
            uint32_t octet_b = i < input_length ? data[i++] : 0;
            uint32_t octet_c = i < input_length ? data[i++] : 0;
            uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
            encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
        }
        for (int i = 0; i < mod_table[input_length % 3]; i++)
        {
            encoded_data[*output_length - 1 - i] = '=';
        }
        return encoded_data;
    }
     
    static char *base64_decode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        if (decoding_table == NULL)
        {
            build_decoding_table();
        }
        if (input_length % 4 != 0)
        {
            return NULL;
        }
        *output_length = input_length / 4 * 3;
        if (data[input_length - 1] == '=')
        {
            (*output_length)--;
        }
        if (data[input_length - 2] == '=')
        {
            (*output_length)--;
        }
        char *decoded_data = malloc(*output_length);
        if (decoded_data == NULL)
        {
            return NULL;
        }
        for (int i = 0, j = 0; i < input_length;)
        {
            uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
            if (j < *output_length)
            {
                decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
            }
        }
        return decoded_data;
    }
    Merci d'avance pour vos réponses.
    Cordialement,
    Nicolas.

  2. #2
    Membre émérite
    Avatar de Kirilenko
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2011
    Messages
    234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 234
    Par défaut
    Bonsoir,

    Effectivement, tu as des problèmes au niveau de tes allocations mémoires. Pour comprendre d'où cela vient, il faut se souvenir que sprintf ajoute un caractère de fin de chaîne à ton tableau ; il faut donc y prévoir de la place. Le même problème de non-gestion du caractère de fin de chaîne est présent dans les fonctions base64_encode et base64_decode. Par ailleurs, je ne comprends pas pourquoi tu utilises l'allocation dynamique de manière si importante ; quand tu connais une taille plancher pour ton tableau à l'avance, utilise un simple tableau.

    Citation Envoyé par C11 (n1570), § 7.21.6.6 The sprintf function
    A null character is written at the end of the characters written.
    Avec ces modifications, tu ne sembles plus avoir de problèmes au niveau de la gestion mémoire. Pour ce qui est du fonctionnement de la fonction, tu sembles avoir les clés en main pour en modifier le traitement. En passant, évite de mettre des définitions de fonction (sauf inline) dans tes fichiers d'en-têtes. Ces derniers sont faits pour être inclus à plusieurs reprises ; or, il ne doit exister (si on parle dans la généralité) qu'une seule définition d'un même objet. Là, ça fonctionne parce que tu n'as qu'un seul fichier source, mais imagine ça avec un projet plus conséquent. La règle que tu peux te fixer, sauf exception : les déclarations dans le fichier d'en-tête ; les définitions dans le fichier source.

    Bonne soirée.
    Récursivité en C : épidémie ou hérésie ?

    "Pour être un saint dans l'Église de l'Emacs, il faut vivre une vie pure. Il faut se passer de tout logiciel propriétaire. Heureusement, être célibataire n'est pas obligé. C'est donc bien mieux que les autres églises" - Richard Stallman

  3. #3
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Il y a plusieurs problèmes dans ce code… Comments inline*:

    Tout d’abord, le main*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <math.h>
    #include "base64.h"
     
    /* Compilation: gcc -std=gnu99 main.c -o b64 -lm */
     
    /* La taille de block d’allocation de notre buffer de lecture... */
    #define BUFF_BLOCK 1024
     
    int main(int argc, char *files[])
    {
        /* Size_in est la taille exacte de l’entrée, allocated est la taille allouée pour cette entrée... */
        size_t size_out, size_in = 0, allocated = 0;
        char *tmp_code = NULL;
        /* Pas de déclaration de variable dans le code, ce n’est plus C-correcte, et un compilateur gcc récent
         * va normalement râler , sinon ! */
        int i;
     
        /* Pourquoi allouer un seul char ? on a quand même un peu mémoire, sur nos machines modernes ! */
        /* Par principe, j’utilise *toujours* sizeof(type), ça permet de mieux savoir à quoi on joue,
         * et rend le code plus portable ! */
        tmp_code = malloc(BUFF_BLOCK * sizeof(char));
        allocated += BUFF_BLOCK;
        /* Pas besoin d’un strcpy pour ça ! */
        /* strcpy (tmp_code, ""); */
        tmp_code[0] = '\0'; /* On pourrait aussi tout simplement utiliser calloc... */
        /* Me semble plus sûr d’utiliser aussi argc ! */
        for (i = 1; i < argc && files[i]; i++)
        {
            FILE *file;
            printf("Ouverture du fichier: %s\n", files[i]);
            if((file = fopen(files[i], "r")) != NULL)
            {
                int ic;
                /* Autant utiliser for... */
                for(ic = fgetc(file); ic != EOF; ic = fgetc(file))
                {
                    /* Si nécessaire, on alloue un nouveau bloc de mémoire. */
                    if (size_in >= allocated)
                    {
                        allocated += BUFF_BLOCK;
                        tmp_code = realloc(tmp_code, allocated);
                        /* Idéalement, il faudrait tester que tmp_code est toujours valable... */
                    }
                    /* Mais pourquoi diantre utiliser malloc pour un seul char ??? */
                    /* Suffit de (re)convertir ic en char, et de l’assigner à l’indice courant ! */
                    tmp_code[size_in] = (char)ic;
                    /* Ici aussi, on pourrait "emboîter" dans l’instruction précédente, mais ces effets de bords
                     * ne facilitent pas la lecture et peuvent être source de problèmes... */
                    /* De toute façon, le compilateur est parfaitement capable d’optimiser ça comme un grand ! */
                    size_in++;
                }
                fclose(file);
            }
            else
            {
                printf("Impossible d'ouvrir le fichier: %s !\n", files[i]);
            }
        }
     
        /* On rajoute nous-même le NULL final ! */
        /* Si nécessaire, on alloue un nouveau bloc de mémoire. */
        if (size_in >= allocated)
        {
            allocated += BUFF_BLOCK;
            tmp_code = realloc(tmp_code, allocated);
            /* Idéalement, il faudrait tester que tmp_code est toujours valable... */
        }
        tmp_code[size_in] = '\0';
        size_in++;
     
        printf("Le texte concatene est:\n%s\n", tmp_code);
        char *encoded = base64_encode(tmp_code, &size_out);
        free(tmp_code);
        if (encoded == NULL)
        {
            printf("Impossible d'encoder !\n");
        }
        else
        {
            printf("Codage:\n%s\n", encoded);
            char *decoded = base64_decode(encoded, &size_out);
            if (decoded == NULL)
            {
                printf("Impossible de decoder !\n");
            }
            else
            {
                printf("Decodage:\n%s\n", decoded);
                free(encoded);
            }
            free(decoded);
        }
    }
    Et ensuite, l’include (c’est quoi, ces manières de mettre du code dans un .h*? )*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
     
    static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                                    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                                    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
                                    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                                    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                                    'w', 'x', 'y', 'z', '0', '1', '2', '3',
                                    '4', '5', '6', '7', '8', '9', '+', '/'};
     
    /* Mais pourquoi diable ne pas se faciliter la vie avec un tableau basique, ici ??? */
    /* De plus, quand ce tableau est-il libéré ? */
    static char decoding_table[256];
    static int decoding_table_initialized = 0;
    static int mod_table[] = {0, 2, 1};
     
    static void build_decoding_table()
    {
        int i;
        for (i = 0; i < 0x40; i++)
        {
            decoding_table[encoding_table[i]] = i;
        }
        decoding_table_initialized = 1;
    }
     
    static char *base64_encode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        char *encoded_data;
        int i, j;
     
        *output_length = (size_t) (4.0 * ceil((double) input_length / 3.0)) + 1; /* Le '\0' final ! */
        encoded_data = malloc(*output_length * sizeof(char));
        if (encoded_data == NULL)
        {
            return NULL;
        }
        for (i = 0, j = 0; i < input_length;)
        {
     
            uint32_t octet_a = i < input_length ? data[i++] : 0;
            uint32_t octet_b = i < input_length ? data[i++] : 0;
            uint32_t octet_c = i < input_length ? data[i++] : 0;
            uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
            encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
        }
        for (i = 0; i < mod_table[input_length % 3]; i++)
        {
            encoded_data[*output_length - 2 - i] = '=';
        }
        /* Ne pas oublier le '\0' final ! Sinon, strlen ne fonctionnera pas sur cette "chaîne" ! */
        encoded_data[*output_length - 1] = '\0';
        return encoded_data;
    }
     
    static char *base64_decode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        int i, j;
     
        if (!decoding_table_initialized)
        {
            build_decoding_table();
        }
        if (input_length % 4 != 0)
        {
            return NULL;
        }
        *output_length = input_length / 4 * 3 + 1; /* Encore le '\0' final ! */
        for (i = 1; data[input_length - i] == '='; i++)
        {
            (*output_length)--;
        }
     
        char *decoded_data = malloc(*output_length * sizeof(char));
        if (decoded_data == NULL)
        {
            return NULL;
        }
        for (i = 0, j = 0; i < input_length;)
        {
            uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
            /* Ici, c’est la même chose que j < *output_length - 1, mais autant toujours jouer la sécurité ! */
            if (j + 2 < *output_length - 1)
            {
                decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
            }
        }
        /* Ne pas oublier le '\0' final ! Sinon, strlen ne fonctionnera pas sur cette "chaîne" ! */
        decoded_data[*output_length - 1] = '\0';
        return decoded_data;
    }

  4. #4
    Membre émérite
    Avatar de Kirilenko
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2011
    Messages
    234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 234
    Par défaut
    Bonsoir,

    Quitte à faire des modifications, autant le faire correctement.

    Citation Envoyé par mont29
    Pas de déclaration de variable dans le code, ce n’est plus C-correcte, et un compilateur gcc récent va normalement râler , sinon !
    Le C99 date d'il y a 13 ans, quand même.

    Citation Envoyé par mont29
    Par principe, j’utilise *toujours* sizeof(type), ça permet de mieux savoir à quoi on joue, et rend le code plus portable.
    Ok pour le début de la phrase (même si ça tourne autour des goûts et des couleurs), mais la portabilité n'a rien à faire là-dedans. Sur un environnement standard, sizeof(char) est toujours égal à 1.

    Citation Envoyé par mont29
    On pourrait aussi tout simplement utiliser calloc...
    À moins que ton compilateur soit suffisamment intelligent pour connaître ton but (en l'occurrence, mettre un simple caractère de fin de chaîne en première case), il risque de te sortir un bon gros code de copie par blocs, à condition que ton tableau soit bien aligné... Bref, c'est pas vraiment ce que j'appellerais du code efficace (et je parle à peine des performances ici).

    Citation Envoyé par mont29
    Me semble plus sûr d’utiliser aussi argc !
    Étant donné, que, toujours d'après les spécifications, argv[argc] est garanti d'être un pointeur nul, la vérification supplémentaire est redondante (quelques fois, je comprends Shannon !).

    Citation Envoyé par mont29
    Autant utiliser for...
    Ce n'est pas spécialement évident ici, tu as une redondance dans la première et la troisième section. Ça tourne un peu autour de la subjectivité ; personnellement, en goret de la programmation que je suis, j'aurais fait un simple while ((c = getc(f)) != EOF).

    Citation Envoyé par mont29
    Si nécessaire, on alloue un nouveau bloc de mémoire.
    Pour les réallocations, il est largement préférable d'utiliser une progression géométrique. Si Jean-Marc Bourguet passe par ici, il pourra peut-être te communiquer le lien vers son site déterminant un facteur optimal (inférieur au nombre d'or) ; je ne l'ai pas sous la main.

    Citation Envoyé par mont29
    Idéalement, il faudrait tester que tmp_code est toujours valable...
    Et dans le cas où tmp_code n'est pas valable, tu te retrouves avec une fuite mémoire. Quitte à tester les résultats des allocations, autant le faire bien.

    Enfin, tu devrais sans doute faire tourner le code que tu as communiqué sous un débogueur, un petit tour de valgrind m'a permis de voir qu'il y avait quelques instructions douteuses.

    Bonne soirée.
    Récursivité en C : épidémie ou hérésie ?

    "Pour être un saint dans l'Église de l'Emacs, il faut vivre une vie pure. Il faut se passer de tout logiciel propriétaire. Heureusement, être célibataire n'est pas obligé. C'est donc bien mieux que les autres églises" - Richard Stallman

  5. #5
    Membre averti
    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 36
    Par défaut
    Bonsoir,

    Tout d'abord merci pour vos réponses.
    Alors pour ce qui est de mettre des fonctions dans un fichier .h, c'est promis demain j'arrête !

    J'ai quand même gardé mon principe d'allouer uniquement la mémoire nécessaire, j'ai dû aussi modifier ton code en remplaçant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    if (j + 2 < *output_length - 1)
     
                par 
     
    if (j + 1 < *output_length - 1)
    sinon ça me "bouffait" le dernier caractère dans la fonction de décodage.

    Voici la version finale, qui fonctionne parfaitement et qui selon valgrind:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    ==16808== HEAP SUMMARY:
    ==16808==     in use at exit: 0 bytes in 0 blocks
    ==16808==   total heap usage: 342 allocs, 342 frees, 58,774 bytes allocated
    ==16808== 
    ==16808== All heap blocks were freed -- no leaks are possible
    ==16808== 
    ==16808== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    ==16808== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <math.h>
     
    // Compilation: gcc -std=gnu99 main.c -o b64 -lm
     
    char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                                    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                                    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
                                    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                                    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                                    'w', 'x', 'y', 'z', '0', '1', '2', '3',
                                    '4', '5', '6', '7', '8', '9', '+', '/'};
     
    char decoding_table[256];
    int decoding_table_initialized = 0;
    int mod_table[] = {0, 2, 1};
     
    void build_decoding_table()
    {
        int i;
        for (i = 0; i < 0x40; i++)
        {
            decoding_table[encoding_table[i]] = i;
        }
        decoding_table_initialized = 1;
    }
     
    char *base64_encode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        char *encoded_data;
        int i, j;
     
        *output_length = (size_t) (4.0 * ceil((double) input_length / 3.0)) + 1;
        encoded_data = malloc(*output_length * sizeof(char));
        if (encoded_data == NULL)
        {
            return NULL;
        }
        for (i = 0, j = 0; i < input_length;)
        {
     
            uint32_t octet_a = i < input_length ? data[i++] : 0;
            uint32_t octet_b = i < input_length ? data[i++] : 0;
            uint32_t octet_c = i < input_length ? data[i++] : 0;
            uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
            encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
        }
        for (i = 0; i < mod_table[input_length % 3]; i++)
        {
            encoded_data[*output_length - 2 - i] = '=';
        }
        encoded_data[*output_length - 1] = '\0';
        return encoded_data;
    }
     
    char *base64_decode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        int i, j;
     
        if (!decoding_table_initialized)
        {
            build_decoding_table();
        }
        if (input_length % 4 != 0)
        {
            return NULL;
        }
        *output_length = input_length / 4 * 3 + 1;
        for (i = 1; data[input_length - i] == '='; i++)
        {
            (*output_length)--;
        }
     
        char *decoded_data = malloc(*output_length * sizeof(char));
        if (decoded_data == NULL)
        {
            return NULL;
        }
        for (i = 0, j = 0; i < input_length;)
        {
            uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
            if (j + 1 < *output_length - 1)
            {
                decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
            }
        }
        decoded_data[*output_length - 1] = '\0';
        return decoded_data;
    }
     
    int main(int argc, char *files[])
    {
        int j = 0;
        size_t size_out;
        char* tmp_code = malloc(1);
        tmp_code[0] = '\0';
        for (int i = 1; files[i]; i++)
        {
            printf("Ouverture du fichier: %s\n", files[i]);
            FILE *file;
            int c;
            if((file = fopen(files[i], "r")) != NULL)
            {
                while ((c = getc(file)) != EOF)
                {
                    tmp_code = realloc(tmp_code, strlen(tmp_code) + 2);
                    if (!tmp_code)
                    {
                        printf("Erreur !!!\n");
                        return 0;
                    }
                    tmp_code[j] = (char)c;
                    tmp_code[j+1] = '\0';
                    j++;
                }
                fclose(file);
            } else {
                printf("Impossible d'ouvrir le fichier: %s !\n", files[i]);
            }
        }
        tmp_code[j++] = '\0';
        printf("Le texte est:\n%s\n", tmp_code);
        char* encoded = base64_encode(tmp_code, &size_out);
        free(tmp_code);
        if (encoded == NULL)
        {
            printf("Impossible d'encoder !\n");
        } else {
            printf("Codage:\n%s\n", encoded);
            char* decoded = base64_decode(encoded, &size_out);
            free(encoded);
            if (decoded == NULL)
            {
                printf("Impossible de décoder !\n");
            } else {
                printf("Decodage:\n%s\n", decoded);
                free(decoded);
            }
        }
        return 0;
    }
    Si vous avez encore des remarques constructives à me faire, n'hésitez pas.

    Ps: la façon dont j'utilise la fonction main pour lire le fichier d'entrée n'a aucune importance car j'ai écrit ceci rapidement que pour montrer mon problème et que ce bout de code est dans un autre programme qui lui gère tout correctement !

    Merci encore pour vos réponses,
    Cordialement,
    Nicolas.

  6. #6
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Citation Envoyé par Kirilenko Voir le message
    Bonsoir,

    Quitte à faire des modifications, autant le faire correctement.

    Le C99 date d'il y a 13 ans, quand même.
    Oui, mais sans l’option qui va bien (-std=gnu99), gcc ne compile pas… Donc ce n’est pas encore considéré comme “par défaut”, si ça n’apporte pas un réel avantage, pourquoi s’embêter*?

    Citation Envoyé par Kirilenko Voir le message
    Ok pour le début de la phrase (même si ça tourne autour des goûts et des couleurs), mais la portabilité n'a rien à faire là-dedans. Sur un environnement standard, sizeof(char) est toujours égal à 1.
    Certes… mais autant âtre là aussi logique et prendre de bonnes habitudes, non*?

    Citation Envoyé par Kirilenko Voir le message
    À moins que ton compilateur soit suffisamment intelligent pour connaître ton but (en l'occurrence, mettre un simple caractère de fin de chaîne en première case), il risque de te sortir un bon gros code de copie par blocs, à condition que ton tableau soit bien aligné... Bref, c'est pas vraiment ce que j'appellerais du code efficace (et je parle à peine des performances ici).
    C’est bien pour ça que j’ai conservé cette version du code… C’est vrai que j’aurais pu me passer de ce commentaire, ceci dit.

    Citation Envoyé par Kirilenko Voir le message
    Étant donné, que, toujours d'après les spécifications, argv[argc] est garanti d'être un pointeur nul, la vérification supplémentaire est redondante (quelques fois, je comprends Shannon !).
    Ben je ne savais pas, maintenant je sais*!

    Citation Envoyé par Kirilenko Voir le message
    Ce n'est pas spécialement évident ici, tu as une redondance dans la première et la troisième section. Ça tourne un peu autour de la subjectivité ; personnellement, en goret de la programmation que je suis, j'aurais fait un simple while ((c = getc(f)) != EOF).
    D’accord, mais encore une fois, c’est une question de lisibilité… À mon goût, évidemment*!

    Citation Envoyé par Kirilenko Voir le message
    Et dans le cas où tmp_code n'est pas valable, tu te retrouves avec une fuite mémoire. Quitte à tester les résultats des allocations, autant le faire bien.
    Yep, très juste, faudrait un pointeur temporaire… Mais comme de toute façon je n’avais pas écrit de test (nulle part, d’ailleurs, pour tmp_code)…

    Citation Envoyé par Kirilenko Voir le message
    Enfin, tu devrais sans doute faire tourner le code que tu as communiqué sous un débogueur, un petit tour de valgrind m'a permis de voir qu'il y avait quelques instructions douteuses.
    En effet, y avait des erreurs aux "conditions limites" (quand l’entrée était vide et que les chaînes étaient donc vides.

    Bon, du coup, code corrigé*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <math.h>
     
    /* Compilation: gcc -std=gnu99 main.c -o b64 -lm */
     
    static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                                    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                                    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
                                    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                                    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                                    'w', 'x', 'y', 'z', '0', '1', '2', '3',
                                    '4', '5', '6', '7', '8', '9', '+', '/'};
     
    /* Mais pourquoi diable ne pas se faciliter la vie avec un tableau basique, ici ??? */
    /* De plus, quand ce tableau est-il libéré ? */
    static char decoding_table[256];
    static int decoding_table_initialized = 0;
    static int mod_table[] = {0, 2, 1};
     
    static void build_decoding_table()
    {
        int i;
        for (i = 0; i < 0x40; i++)
        {
            decoding_table[encoding_table[i]] = i;
        }
        decoding_table_initialized = 1;
    }
     
    static char *base64_encode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        char *encoded_data;
        int i, j;
     
        *output_length = (size_t) (4.0 * ceil((double) input_length / 3.0));
        encoded_data = malloc((*output_length + 1) * sizeof(char)); /* + 1 pour le '\0' final ! */
        if (encoded_data == NULL)
        {
            return NULL;
        }
        for (i = 0, j = 0; i < input_length;)
        {
     
            uint32_t octet_a = i < input_length ? data[i++] : 0;
            uint32_t octet_b = i < input_length ? data[i++] : 0;
            uint32_t octet_c = i < input_length ? data[i++] : 0;
            uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
            encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
            encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
        }
     
        for (i = 0; i < mod_table[input_length % 3]; i++)
        {
            encoded_data[*output_length - 1 - i] = '=';
        }
        /* Ne pas oublier le '\0' final ! Sinon, strlen ne fonctionnera pas sur cette "chaîne" ! */
        encoded_data[*output_length] = '\0';
        return encoded_data;
    }
     
    static char *base64_decode(const char *data, size_t *output_length)
    {
        size_t input_length = strlen(data);
        int i, j;
     
        if (!decoding_table_initialized)
        {
            build_decoding_table();
        }
        if (input_length % 4 != 0)
        {
            return NULL;
        }
        *output_length = input_length / 4 * 3;
        for (i = 0; i < input_length && data[input_length - i] == '='; i++)
        {
            (*output_length)--;
        }
     
        char *decoded_data = malloc((*output_length + 1) * sizeof(char)); /* Encore le '\0' final ! */
        if (decoded_data == NULL)
        {
            return NULL;
        }
        for (i = 0, j = 0; i < input_length;)
        {
            uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
            uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
            /* Ici, c’est la même chose que j < *output_length, mais autant toujours jouer la sécurité ! */
            if (j + 2 < *output_length)
            {
                decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
                decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
            }
        }
        /* Ne pas oublier le '\0' final ! Sinon, strlen ne fonctionnera pas sur cette "chaîne" ! */
        decoded_data[*output_length] = '\0';
        return decoded_data;
    }
     
    /* La taille de block d’allocation de notre buffer de lecture... */
    #define BUFF_BLOCK 1024
     
    int main(int argc, char *files[])
    {
        /* Size_in est la taille exacte de l’entrée, allocated est la taille allouée pour cette entrée... */
        size_t size_out, size_in = 0, allocated = 0;
        char *tmp_code = NULL;
        /* Pas de déclaration de variable dans le code, ce n’est plus C-correcte, et un compilateur gcc récent
         * va normalement râler , sinon ! */
        int i;
     
        /* Pourquoi allouer un seul char ? on a quand même un peu mémoire, sur nos machines modernes ! */
        /* Par principe, j’utilise *toujours* sizeof(type), ça permet de mieux savoir à quoi on joue,
         * et rend le code plus portable ! */
        tmp_code = malloc(BUFF_BLOCK * sizeof(char));
        if (!tmp_code)
        {
            printf("Error allocating some memory for the reading buffer, aborting!\n");
            return -1;
        }
        allocated += BUFF_BLOCK;
        /* Pas besoin d’un strcpy pour ça ! */
        /* strcpy (tmp_code, ""); */
        tmp_code[0] = '\0';
        for (i = 1; files[i]; i++)
        {
            FILE *file;
            printf("Ouverture du fichier: %s\n", files[i]);
            if((file = fopen(files[i], "r")) != NULL)
            {
                int ic;
                /* Autant utiliser for... */
                for(ic = fgetc(file); ic != EOF; ic = fgetc(file))
                {
                    /* Si nécessaire, on alloue un nouveau bloc de mémoire. */
                    if (1 && size_in >= allocated)
                    {
                        char *tc;
                        allocated += BUFF_BLOCK;
                        tc = realloc(tmp_code, allocated);
                        if (!tc)
                        {
                            printf("Error reallocating some memory for the reading buffer, aborting!\n");
                            free(tmp_code);
                            return -1;
                        }
                        tmp_code = tc;
                    }
                    /* Mais pourquoi diantre utiliser malloc pour un seul char ??? */
                    /* Suffit de (re)convertir ic en char, et de l’assigner à l’indice courant ! */
                    tmp_code[size_in] = (char)ic;
                    /* Ici aussi, on pourrait "emboîter" dans l’instruction précédente, mais ces effets de bords
                     * ne facilitent pas la lecture et peuvent être source de problèmes... */
                    /* De toute façon, le compilateur est parfaitement capable d’optimiser ça comme un grand ! */
                    size_in++;
                }
                fclose(file);
            }
            else
            {
                printf("Impossible d'ouvrir le fichier: %s !\n", files[i]);
            }
        }
     
        /* On rajoute nous-même le NULL final ! */
        /* Si nécessaire, on alloue un nouveau bloc de mémoire. */
        if (size_in >= allocated)
        {
            allocated += BUFF_BLOCK;
            tmp_code = realloc(tmp_code, allocated);
            /* Idéalement, il faudrait tester que tmp_code est toujours valable... */
        }
        tmp_code[size_in] = '\0';
        size_in++;
     
        printf("Le texte concatene est:\n%s\n", tmp_code);
        char *encoded = base64_encode(tmp_code, &size_out);
        free(tmp_code);
        if (encoded == NULL)
        {
            printf("Impossible d'encoder !\n");
        }
        else
        {
            printf("Codage:\n%s\n", encoded);
            char *decoded = base64_decode(encoded, &size_out);
            if (decoded == NULL)
            {
                printf("Impossible de decoder !\n");
            }
            else
            {
                printf("Decodage:\n%s\n", decoded);
                free(encoded);
            }
            free(decoded);
        }
    }

  7. #7
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Désolé nikobordx, j’avais pas vu votre réponse…

    J’ai remis mon code corrigé en un seul fichier (l’idée d’ajouter le caractère '\0' au compte de output_length n’était pas bonne [du tout*! ])…

    Sinon, pour le tampon de lecture, mieux vaut avoir alloué un peu trop de mémoire (d’autant qu’avec les OS modernes, cette mémoire n’est pas obligatoirement réellement consommée, tant qu’on n’y accède pas), plutôt que d’appeler dix mille fois realloc()… Sans parler de l’appel à strlen() dans chaque*! Au moins, utilisez une variable incrémentée à chaque nouveau caractère… Tout plutôt que strlen*!

    [edit]À voir le print de Valgrind, realloc semble de lui-même implémenter un système d’allocation par blocs*? En tout cas, 342 allocations, ça me semble peu, pour les 57000 octets alloués en tout, vu qu’un petit tiers est censé avoir été alloué octet par octet…[/edit]

  8. #8
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par mont29 Voir le message
    Oui, mais sans l’option qui va bien (-std=gnu99), gcc ne compile pas… Donc ce n’est pas encore considéré comme “par défaut”, si ça n’apporte pas un réel avantage, pourquoi s’embêter*?
    ...
    Bonsoir,

    juste en passant, l'option -std=gnu99 active la compilation en C99 avec les extensions gnu, pour compiler en C99 sans les extensions gnu l'option à utiliser est -std=c99, et si on veut un C99 strict il faut lui ajouter l'option -pedantic. Un -ansi est équivalent à -std=c90.

    C99 n'est pas le défaut (pour l'instant c'est gnu90) car toutes les fonctionalités ne sont pas encore implémentées (cf l'état d'avancement pour la version 4.7).

  9. #9
    Membre averti
    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 36
    Par défaut
    Pour ce qui est de "strlen", je l'avais effectivement remplacé (juste après avoir posté) par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    size += 2;
    tmp_code = realloc(tmp_code, size);
    Tout fonctionne très bien, je vais enfin pouvoir avancer, merci à tous !
    Nicolas.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. erreur de type char**,char*,char
    Par evoliptic dans le forum Débuter
    Réponses: 9
    Dernier message: 31/07/2014, 14h39
  2. Problème conversion const char *
    Par autoz dans le forum Débuter
    Réponses: 5
    Dernier message: 15/10/2009, 21h19
  3. Réponses: 1
    Dernier message: 17/06/2009, 18h18
  4. problème conversion chaine 10 char
    Par Hurin dans le forum C
    Réponses: 4
    Dernier message: 11/04/2008, 10h56
  5. [MFC] Problème de conversion CString vers Char *
    Par Darkenshin dans le forum MFC
    Réponses: 10
    Dernier message: 02/12/2005, 14h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo