How to overwrite an array of char pointers with a larger list of char pointers?
My function is being passed a struct containing, among other things, a NULL terminated array of pointers to words making up a command with arguments.
I'm performing a glob match on the list of arguments, to expand them into a full list of files, then I want to replace the passed argument array with the new expanded one.
The globbing is working fine, that is, g.gl_pathv is populated with the list of expected files. However, I am having trouble copying this array into the struct I was given.
#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: Also, I realized I need to stick program back in there as cmd->argv[0]
Edit 2: Added call to calloc Edit 3: Edit mem management with tips from Alok Edit 4: More tips from alok Edit 5: Almost working.. the app segfaults when freeing the command structFinally: Seems like I was missing the terminating NULL, so adding the line:
cmd->argv[g.gl_pathc + g.gl_offs] = 0;
开发者_Go百科seemed to make it work.
argv
is an array of pointers of char *
. This means that argv
has space for argc
char *
values. If you try to copy more than that many char *
values into it, you will end up with an overflow.
Most likely your glob
call results in more than argc
elements in gl_pathv
field (i.e, gl_pathc > argc
). This is undefined behavior.
It is similar to the code below:
/* Wrong code */
#include <string.h>
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3, 4 };
memcpy(a, b, sizeof b);
Solution: you should either work with the glob_t
struct directly, or allocate new space to copy gl_pathv
to a new 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 */
Edit: after your edit:
cmd->argv = calloc(g.gl_pathc, sizeof(char *) *g.gl_pathc);
it should work, but each of argv[1]
to argv[g.gl_pathc + g.gl_offs - 1]
is a char *
that is "owned" by the struct glob
. Your memcpy
call is only copying the pointers. When you later do globfree()
, those pointers don't mean anything anymore. So, you need to do copy the strings for your use:
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]);
This makes sure you now have your own private copies of the strings. Be sure to free them (and argv
) once you are done.
There are a few other problems with your code.
- You are doing
*p++
, you should dop++
, since you're not using the value of the dereferencing. - You should really check the return value of
glob
. - Your
paths
variable needsg.gl_pathc + 1
elements, notg.gl_pathc
. (Or more correctly, you need to allocateg.gl_pathc + g.gl_offs
timessizeof *paths
bytes.) - Your
for
loop to copy strings should befor (j=1; j < g.gl_pathc + g.gl_offs; ++j)
. - Make sure you prevent shell from expanding your glob. I.e., call
./a.out '*'
instead of./a.out *
.
Don't you need to multiple g.gl_pathc by sizeof(char *)?
精彩评论