简体   繁体   中英

C: Losing strings when exiting functions

I'm trying to implement a basic shell, I have several functions within it that deal with strings, trying to find file names, implement something equivalent to *argv[] and so on.

I have strings in main() , which are passed to a function to be populated. Next the program returns to main() , which passes the strings to another function to be acted upon.

I was debugging with lldb and found that I was successfully populating the strings with the correct values in the first function but upon exiting the function, re-entering main() the output_str string was NULL again. I thought strings, since they point to space in memory would retain values. They seem to for all but one case, when flag = 1 in the code below.

I can't figure out what's happening as the values seem to only be lost after the final } of the function.

Edited to add complete code, hope it isn't too large.

The code works with say, cat input.txt but not with cat input.txt>output.txt when I try to redirect the output from stdout to a file Thank you for your help in advance.

Here is the function .c file:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

void sig_handler(int signo)
{
        if (signo == SIGINT)
        {
                fprintf(stdout, "\n");
                fflush(stdout);
        }
}
int check_redirect(char *line, int flag)
{
        int n = 0;
        if (line == NULL) return (flag);
        else
        {
                do
                {
                        if (line[n] == '>') flag = 1;
                        n++;
                }while (line[n] != '\0');
        }

        return (flag);
}

void string_breakdown(char *line, char **output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug)
{
        char *sep = " \n";
        char *delim = ">\n";

        if (line != NULL)
        {
                temp = strtok(line, delim);
                while (temp != NULL)
                {
                        output_str[count] = temp;

                        if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);

                        count++;
                        output_str = realloc (output_str, (count + 1) * sizeof (char *) );

                        temp = strtok(NULL, delim);
                }
                if (flag)
                {
                        count = 0;
                        strcpy(filename, output_str[1]);
                        output_str[1] = NULL;

                        *saved_stdout = dup(1);

                        *f = open(filename , O_WRONLY|O_CREAT|O_TRUNC, 0666);

                        dup2(*f, 1);

                        temp = strtok(*output_str[0], sep);
                        while (temp != NULL)
                        {
                                output_str[count] = temp;

                                //if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);

                                count++;
                                output_str = realloc (output_str, (count + 1) * sizeof (char *));

                                temp = strtok(NULL, sep);
                        }
                }

                else
                {
                        count = 0;
                        temp = strtok(line, sep);
                        while (temp != NULL)
                        {
                                output_str[count] = temp;

                                if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);

                                count++;
                                output_str = realloc (output_str, (count + 1) * sizeof (char *));

                                temp = strtok(NULL, sep);
                        }
                }
        }
}

void com_exec(char *line, char **output_str, char *filename, int *f, int *saved_stdout, int flag, int debug)
{
        char *command = malloc(sizeof(char *));
        command = output_str[0];
        char *name = "HOME";
        int ret_val = 0;
        pid_t child_pid;
        int child_status;
        if (command == NULL);
        else if (strcmp("cd", command) == 0)
        {
                if (output_str[1] == NULL) output_str[1] = getenv(name);

                ret_val = 0;
                ret_val = chdir(output_str[1]);
                if (ret_val) perror(NULL);
        }

        else
        {
                child_pid = fork ();
                if (child_pid == 0)
                {
                        if (debug)
                        {
                                system(line);
                                fprintf(stderr, "Post System Pre Exec\n1:%s\n2:%s\n3:%s\n", line, output_str[0], command);
                                sleep(2);
                        }

                        execvp(command, output_str);

                        if (flag)
                        {
                                close(*f);

                                dup2(*saved_stdout, 1);
                                close(*saved_stdout);

                        }

                        fprintf (stdout, "Unknown command\n");
                        exit (0);
                }

                else
                {
                        if (flag)
                        {
                                close(*f);

                                dup2(*saved_stdout, 1);
                                close(*saved_stdout);
                        }

                        signal(SIGINT, sig_handler);

                        usleep(500000);

                        //Parent process waits for child to finish
                        if (debug) fprintf (stderr, "parent waiting\n");

                        wait(&child_status);
                        waitpid(child_pid, &child_status, 0);

                        signal(SIGINT, SIG_DFL);
                }
        }

Here is the functions .h file:

#ifndef SHELL_H_INCLUDED
#define SHELL_H_INCLUDED

void sig_handler(int signo);

int prompt(char *line, size_t len, ssize_t read);

int check_redirect(char *line, int flag);

void string_breakdown(char *line, char **output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug);

void com_exec(char *line, char **output_str, char *filename, int *f, int *saved_stdout, int flag, int debug);

#endif // LINKLAYER_H_INCLUDED

Below is main.c, where the function is called.

#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

#include "shell.h"

int main(void)
{
        int debug = 0;

        char *line = NULL;
        size_t len = 0;
        ssize_t read = 0;

        int flag = 0;

        int f = 0;
        int saved_stdout = 0;

        do
        {
                flag = 0;

                //read = prompt(line, len, read);
                char buffer[15];
                time_t now = time(NULL);
                strftime(buffer, 15, "[%d/%m %H:%M]", localtime(&now) );

                fprintf(stdout, "%s # ", buffer);

                signal(SIGINT, SIG_IGN);
                read = getline (&line, &len, stdin);
                signal(SIGINT, SIG_DFL);

                flag = check_redirect(line, flag);

                char **output_str = malloc(sizeof(char *));
                int count = 0;
                char* temp = NULL;
                char *filename = malloc(sizeof(char *));

                string_breakdown(line, output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // function call of problem function

                com_exec(line, output_str, filename, &f, &saved_stdout, flag, debug);
        } while (read != EOF);

        if (debug) fprintf(stderr, "parent exiting\n");
        else fprintf(stdout, "\n");

        return 0;
}
        output_str = realloc (output_str, (count + 1) * sizeof (char *) );

This line re-assigns the value of the the local parameter variable output_str , but the new value in no way makes it back to the caller of the string_breakdown function - meaning that the pointer it has will probably be left dangling, and will cause problems when used ("undefined behavior", manifesting in strange program behavior or crashing).

You need to understand that within the function, output_str is a local variable. You can change its value, but that won't affect the value of any variable in the caller.

You call the function from main :

             string_breakdown(line, output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // The call of the above function

main also uses output_str as the variable name, but again, this is a different variable. One variable is local to main , the other is local to string_breakdown , even though they share the same name. Due to the realloc call above, the pointer value in main 's output_str will most likely be invalid on return from string_breakdown , because it is not updated to point to the newly allocated memory. That's why you are "losing" the string values on return from the function - the output_str variable in main is no longer actually pointing to the array of strings, which has been moved to a different location via realloc .

Typically you resolve this kind of problem by adding another level of indirection, changing the output_str parameter from a char ** to a char *** :

void string_breakdown(char *line, char ***output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug)

and

       (*output_str)[count] = temp;

and

       *output_str = realloc (*output_str, (count + 1) * sizeof (char *) );

and so on. You need to adjust the call in main as well:

             string_breakdown(line, &output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // The call of the above function

Because you are passing a pointer to main 's output_str variable, the called function is now able to modify its value.

You should also understand that string_breakdown as written modifies the string which the line parameter points to. That's because it uses strtok , and strtok replaces delimiters with nul bytes as it processes the string. So, it is odd that you pass this modified line buffer to com_exec after processing it with string_breakdown .

I get several warnings when I try to compile your code; main.c uses fprintf but doesn't #include <stdio.h> , and uses malloc but doesn't #include <stdlib.h> .

your realloc does nothing.

you mean *output_ptr = realloc....

actually it does something, but its really bad

this is also wrong

output_str[count] = temp;

and this

filename = output_str[1];

you need to distinguish - a pointer to your buffer, a pointer to the pointer to your buffer.

  char * buffer = *output_str; // to remove the confusion
  strcpy(&buffer[count], temp); // assigning pointers doesnt copy things


filename = buffer[1]; // is hat what you mean - filename is one char

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM