Search code examples
cargvexecv

Copy argv to another variable to change it without change the original


My program has a variable number of args and I need to make an execv with a new path, so I want to change the value of argv[1] in a different variable without changing it, but it won't let me.

char** arg_exec = malloc(argc * sizeof (char*));
int i;
for(i=0;i <= argc-1; i++)
   arg_exec[i] = strdup(argv[i]);
arg_exec[argc] = NULL;
if( (pid = fork()) == 0){
    arg_exec[1] = strcat(directory , dir_info->d_name); //some variables with the current path and a name
    execv(arg_exec[0], arg_exec);
    printf("Error in process %d\n", getpid());
    return 1;
 }

but after it runs this line arg_exec[1] = strcat(directory , dir_info->d_name); it changes my value of argv[1], and my program fails..

It worked fine with execl, since it was like execl(argv[0],strcat(directory , dir_info->d_name), ..., NULL); but because I have a variable number of arguments to run it, it wouldn't be good to implement that way.

Edit1: Added NULL at the end of the array Edit2: I'm doing a version of the find, so strcat will add to the current directory a folder to look into. This is the initalization of the directory: char *directory = strcat(argv[1],"/");


Solution

  • char *directory = strcat(argv[1],"/"); attemps to modify argv[1] beyond its allocation, which is UB. @alk.

    Modifying argv itself may be UB. Is argv[n] writable?

    So allocate memory for both.

    Note: char** arg_exec = malloc(argc * sizeof (char*)); is insufficient as argv[argc] must be NULL. Need 1 more. Notice argc is not passed to execv().


    Step 1. Make a copy of the pointer array argv[]

    char **argv_new;
    size_t a_size = sizeof *argv_new * (argc + 1);  // + 1 for the final NULL
    argv_new = malloc(a_size);
    memcpy(argv_new, argv, a_size);
    

    Step 2. Form the new arg[1]

    int size = 1 + snprintf(NULL, 0, "%s/%s", argv[1], dir_info->d_name);
    argv_new[1] = malloc(size);
    snprintf(argv_new[1], size, "%s/%s", argv[1], dir_info->d_name);
    

    Use it

    execv(arg_new[0], arg_new);
    free(argv_new[1]);
    free(argv_new);
    

    TBD: Error checking to add for: argc > 1, malloc(), snprintf(), execv()