2010-03-15 18 views
0

Ma fonction est passée une structure contenant, entre autres choses, un tableau terminé par NULL de pointeurs vers des mots constituant une commande avec des arguments.Comment écraser un tableau de pointeurs char avec une plus grande liste de pointeurs char?

J'effectue une correspondance glob sur la liste des arguments, pour les développer en une liste complète de fichiers, puis je veux remplacer le tableau d'arguments transmis par le nouveau.

La globalisation fonctionne correctement, c'est-à-dire que g.gl_pathv est rempli avec la liste des fichiers attendus. Cependant, j'ai du mal à copier ce tableau dans la structure qui m'a été donnée.

#include <glob.h> 

struct command { 
    char **argv; 
    // other fields... 
} 

void myFunction(struct command * cmd) 
{ 
    char **p = cmd->argv; 
    char* program = *p++; // save the program name (e.g 'ls', and increment to the first argument 

    glob_t g; 
    memset(&g, 0, sizeof(g)); 
    g.gl_offs = 1; 
    int res = glob(*p++, GLOB_DOOFFS, NULL, &g); 
    glob_handle_res(res); 
    while (*p) 
    { 
     res = glob(*p, GLOB_DOOFFS | GLOB_APPEND, NULL, &g); 
     glob_handle_res(res); 
    } 

    if(g.gl_pathc <= 0) 
    { 
     globfree(&g); 
    } 

    cmd->argv = malloc((g.gl_pathc + g.gl_offs) * sizeof *cmd->argv); 

    if (cmd->argv == NULL) { sys_fatal_error("pattern_expand: malloc failed\n");} 
    // copy over the arguments 
    size_t i = g.gl_offs; 
    for (; i < g.gl_pathc + g.gl_offs; ++i) 
     cmd->argv[i] = strdup(g.gl_pathv[i]); 

    // insert the original program name 
    cmd->argv[0] = strdup(program); 
    ** cmd->argv[g.gl_pathc + g.gl_offs] = 0; ** 
    globfree(&g); 
} 

void 
command_free(struct esh_command * cmd) 
{ 
    char ** p = cmd->argv; 
    while (*p) { 
     free(*p++); // Segfaults here, was it already freed? 
    } 
    free(cmd->argv); 
    free(cmd); 
} 

Edit 1: De plus, je réalise que je dois tenir le programme de retour là-bas comme cmd-> argv [0]
Edit 2: appel Ajouté à calloc
Edit 3: Modifier mem gestion des conseils de Alok
Edit 4: Autres conseils de Alok
Edit 5: Presque travailler .. l'application segfaults lors de la libération de la commande struct

Enfin: On dirait que je manque le NULL de terminaison, afin d'ajouter la ligne:

cmd->argv[g.gl_pathc + g.gl_offs] = 0; 

semblait fonctionner.

+0

Alors, ça marche maintenant? Si non, quelle erreur obtenez-vous? Voir aussi ma modification la plus récente à ma réponse. Vous allouez deux fois l'espace pour 'paths [0]', une fois avec 'malloc' et encore avec' strdup'. –

+0

oui, ce code était crufty .. voir la dernière édition. il plante toujours en accédant au nouveau tableau ... peut-être que les caractères \ 0 se perdent dans le shuffle? – Casey

+0

Dans 'cmd-> argv [g.gl_pathc + g.gl_offs] = '\ 0';', le ''\ 0'' est équivalent à' 0', ce qui équivaut à 'NULL' ici, donc vous devriez utilisez 'NULL' au lieu de' '\ 0''. –

Répondre

1

argv est un tableau de pointeurs de char *. Cela signifie que argv a de l'espace pour les valeurs argcchar *. Si vous essayez de copier plus que ce nombre de valeurs char *, vous vous retrouverez avec un débordement.

Très probablement vos glob résultats d'appels dans plus de argc éléments gl_pathv champ (i.e., gl_pathc > argc). C'est un comportement indéfini.

Il est similaire au code ci-dessous:

/* Wrong code */ 
#include <string.h> 

int a[] = { 1, 2, 3 }; 
int b[] = { 1, 2, 3, 4 }; 
memcpy(a, b, sizeof b); 

Solution: vous devez soit travailler avec le directement struct glob_t ou allouer un nouvel espace pour copier gl_pathv à une nouvelle char **:

char **paths = malloc(g.gl_pathc * sizeof *paths); 
if (paths == NULL) { /* handle error */ } 
for (size_t i=0; i < g.gl_pathc; ++i) { 
    /* The following just copies the pointer */ 
    paths[i] = g.gl_pathv[i]; 

    /* If you actually want to copy the string, then 
     you need to malloc again here. 

     Something like: 

     paths[i] = malloc(strlen(g.gl_pathv[i] + 1)); 

     followed by strcpy. 
    */ 
} 

/* free all the allocated data when done */ 

Modifier: après votre modification:

cmd->argv = calloc(g.gl_pathc, sizeof(char *) *g.gl_pathc); 

cela devrait fonctionner, mais chacun des argv[1] à argv[g.gl_pathc + g.gl_offs - 1] est un char * qui est "détenu" par le struct glob. Votre appel memcpy copie uniquement les pointeurs. Lorsque vous faites plus tard globfree(), ces pointeurs ne signifient plus rien. Donc, vous devez faire copier les chaînes pour votre utilisation:

size_t i; 
cmd->argv = malloc((g.gl_pathc+g.gl_offs) * sizeof *cmd->argv); 
for (i=g.gl_offs; i < g.gl_pathc + g.gl_offs; ++i) 
    cmd->argv[i] = strdup(g.gl_pathv[i]); 

Cela fait que vous avez maintenant vos propres copies privées des chaînes. Assurez-vous de les libérer (et argv) une fois que vous avez terminé.

Il y a quelques autres problèmes avec votre code.

  1. Vous faites *p++, vous devriez faire p++, puisque vous n'êtes pas en utilisant la valeur du déréférencement.
  2. Vous devriez vraiment vérifier la valeur de retour de glob.
  3. Votre variable paths nécessite g.gl_pathc + 1 éléments, et non g.gl_pathc. (Ou plus correctement, vous devez allouer g.gl_pathc + g.gl_offs fois sizeof *paths octets.)
  4. Votre boucle for pour copier des chaînes doit être for (j=1; j < g.gl_pathc + g.gl_offs; ++j).
  5. Assurez-vous d'empêcher le shell d'étendre votre glob. Par exemple, appelez ./a.out '*' au lieu de ./a.out *.
+0

l'idée derrière ma fonction étant passée la commande struct est que ma fonction est un gestionnaire de sortes qui effectue une opération sur la chaîne de commande avant qu'elle ne soit exécutée. par conséquent, j'ai besoin de modifier la valeur de argv dans le cmd. – Casey

+0

Mais d'où vient l'élément 'argv' de' cmd'? Si c'est un tableau, vous ne pouvez pas simplement dire 'cmd-> argv = ...', ce qui est illégal dans C. C'est comme dire: 'int a [] = {1, 2};' suivi de ' a = malloc (...); –

+0

Peu importe, mon cerveau n'était pas engagé. J'ai "manqué" la définition de "struct cmd". –

0

Vous n'avez pas besoin de plusieurs g.gl_pathc par sizeof (char *)?

+0

hm, oui .. mais je pense qu'une partie du problème est cmd-> argv est probablement plus petit que g.gl_pathv. après tout si l'argv initial est composé de deux éléments: "ls" et "* .h" .. le * .h pourrait être étendu en 100s de noms de fichiers – Casey

+0

bon point. Je viens de repérer le bug à basse portée. – bmargulies