Search code examples
carraysfork

Fork array assignment conflict


I have the following and try to read from file line by line and then put that line to an array.

char **history = malloc(sizeof(char*) * 1000);
int history_size = 0;

fp = fopen(file, "r");
if (fp == NULL)
    exit(EXIT_FAILURE);

while ((read = getline(&line, &len, fp)) != -1) {
    if(strstr(line, "\n") != NULL) {
        line[strlen(line) - 1] = '\0';
    }

    if (strcmp(line, "History") == 0) {
        for(int m = 0; m < history_size; m++) {
            printf("%s\n", history[m]);
        }
    } else {
        //printf("....%s\n", line);
        history[history_size++] = line;
    }
}

fclose(fp);
if (line)
    free(line);

Problem is that array does not collect lines and when I run the code it prints "History" as many as lines in the file. I could not handle it for days. History should keep the lines that occurs before History line.


Solution

  • It has nothing to do with fork().

    From the manpage of getline:

    If *lineptr is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. (In this case, the value in *n is ignored.)

    If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updating *lineptr and *n as necessary.

    The problem is that you are assigning the address to the array elements. At last they all point to the same address(depends on getline's realloc). As you didn't malloc, so getline must have done that internally, and on subsequent calls realloc is used inside getline which would generally retain the same address. So over the loop as your line is updated, all array elements are likely to be updated as well because most array elements point to the same address.

    So finally when you reach the condition when your line is "History", they just showed what line contained (i.e. "History") because all of them pointed to the same address as line.

    This is the main cause of problem, but fork() would surely deviate you from the way you want your output afterwards. Although fork() would only disturb your output and can be easily handled by not printing when fork() returned 0.

    Corrections:

    1) As you told in comments on your question you are doing execvp() if fork() == 0, it shouldn't cause problems. All you need to do is:

    history[history_size] = malloc(sizeof(char)* (strlen(line)+1));
    strcpy(history[history_size++], line);
    

    instead of directly assigning it.

    2) Remember to free() on every iteration the memory allocated by getline() in line.