Strawberry Cat
Strawberry Cat

Reputation: 1

Mutating array of char* using a function but string values change after function exit.

I am building a simple command line interpreter. It reads user input from stdin and parses it to run programs in seperate child processes.

User input will be in the format: ... with arg1 to arg4 being possible command line arguments.

(I am constraining the input format, path is <100 chars long, each cmd line argument is <20 chars long, there are up to 4 cmd line arguments)

For now the main() function only reads in one line of user input.

My problem is, I abstracted the input parsing and storing into a function called extractArgs(char**,char*). This function parses the input and extracts the data needed for the execv(char* argc, char* argv[]) arguments.

It definitely parses the input correctly. I put debugging print statements in main and extractArgs to print out the strings in the argv array. The one in extractArgs shows that argv holds the correct values, but the one in main prints nonsense.

I suspect it has something to do with memory being recycled after extractArgs finishes execution, so the argv pointers end up pointing at nonsense when the old values are overwritten..

Is this correct? What can I do to stop that? Must I use malloc? Is there no way to simply keep the pointers in the argv array from being recycled?

// argv is the char* array to store cmd args from stdin for execv, 
// path is string to store path from user input
int extractArgs(char** argv, char* path)
{
    const char* fmessageNotFound = "%s not found\n";
    struct stat buf;

    const char delim[2] = " ";
    char argStr[90];
    char* token;
    int i;

    scanf("%s", path); // get path

    // handle invalid program path
    if (stat(path, &buf) == -1) 
    {
        printf(fmessageNotFound, path);
        return -1;
    }

    argv[0] = path; // argv[0] must be program name
    argv[1] = NULL; // set terminating element for 0 argument case.

    // read all cmd line args.
    // constraints: up to 4 args, arg lengths <20 chars
    fgets(argStr, 90, stdin);
    argStr[strcspn(argStr, "\n")] = '\0'; // clear trailing \n

    // populate argv array
    token = strtok(argStr, " ");
    for (i = 1; i < 5 && token != NULL; i++)
    {
        argv[i] = token;
        token = strtok(NULL, " ");
    }
    // set terminating element after populating argv
    argv[i] = NULL;

    // test and print all the arguments
    i = 0;
    while (argv[i] != NULL) {

        // EVERYTHING WORKS HERE
        printf("%s\n", argv[i]);
        i++;
    }

    return 0;
}

int main()
{
    pid_t chpid;
    char* argv[6];  // max 4 cmd args + name + NULL terminator
    char path[100]; // command path length <100

    if (extractArgs(argv, path) == -1) // invalid path
    {
        fgets(path, 100, stdin); // clean up newline
        return;
    }

    // Print values in argv
    int i = 0;
    while (argv[i] != NULL) {

        // PRINTS NONSENSE.
        printf("%s\n", argv[i]);
        i++;
    }

    chpid = fork();
    if (chpid == 0)
    {
        execv(path, argv);
    }
    wait(NULL);
    return 0;
}

Upvotes: 0

Views: 676

Answers (1)

Some programmer dude
Some programmer dude

Reputation: 409166

In the extractArgs function, you store pointers to the local array argStr in your argv array.

Once the function returns those pointers are no longer valid as argStr has gone out of scope. This leads to undefined behavior when you try to use the stray pointers in the argv array.

Upvotes: 1

Related Questions