Funny
Funny

Reputation: 193

Reading lines from stdout with fgets()

After reading documentation, Stack answers and C standard, I still seem not to get something.

I wanted to redirect the output to the certain file and then compare the output with the predicted result; I'm going to indicate the main issue using comments

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_SEQ 120

void printer() {
    for (int i = 0; i < 5; i++) {
        printf("%d0 ", i);
        for (int j = 0; j < 6; j++) {
            printf("0%d ", j);
        }
        printf("\n");
    }
    printf("\n");
}

void check_string() {
    FILE* pointer = freopen("./check_str_out", "w", stdout);

    char* storage = malloc(MAX_SEQ);
    char* test_results[] = {"00 00 01 02 03 04 05 \n", "10 00 01 02 03 04 05 \n",
    "20 00 01 02 03 04 05 \n", "30 00 01 02 03 04 05 \n", "40 00 01 02 03 04 05 \n", "\n"};
    int counter = 0;
    printer();

    freopen("/dev/tty", "w", stdout);
    FILE* output = fopen("./check_str_out", "r");  //It was added in the new version
    printf("sth\n");
    while (fgets(storage, MAX_SEQ, output)) {  //previous: while (fgets(storage, MAX_SEQ, pointer))
        printf("sth2\n");
        printf("count: %d, stor: %s, test: %s, comp: %d\n", counter, storage, test_results[counter], strcmp(storage, test_results[counter])); //E) strcmp(storage, test_results[counter++])
        counter++; 
    }
    free(storage);
    fclose(output);

}

int main(void) {
    check_string();
    return 0;
}

The question is: why did this code not work when I used pointer? Why did I have to open the file? Now everything is correct. I've tested also with:

FILE* pointer = freopen("./check_str_out", "w+", stdout);

in order to check, whether it is a matter of mode, but it didn't work.

For clarity: it doesn't print the while loop if there is pointer in fgets() used, it doesn't enter it.

EDIT: From this description I can't see the fclose() of the file opened by freopen(), however it looks like the stream has been closed. What is the cause of that?

Upvotes: 1

Views: 840

Answers (1)

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

You're accessing test_results out of bounds so your program has undefined behavior.

void printer() {
    for (int i = 0; i < 5; i++) {
        printf("%d0 ", i);
        for (int j = 0; j < 6; j++) {
            printf("0%d ", j);
        }
        printf("\n");
    }
    printf("\n");         // this line makes it print 6 lines to the file
}

And in your while loop counter will reach 5 and test_results[5] is out of bounds.

Always test the return value from functions that may fail, like freopen, and also make sure that you're not running out of bounds when depending on external data (like your data file, that may get corrupted for other reasons).

Example:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_SEQ 120

void printer() {
    for (int i = 0; i < 5; i++) {
        printf("%d0 ", i);
        for (int j = 0; j < 6; j++) {
            printf("0%d ", j);
        }
        printf("\n");
    }
    printf("\n"); // this extra line doesn't matter if you check bounds later
}

void check_string() {
    if (freopen("./check_str_out", "w", stdout)==NULL) {
        perror("./check_str_out");
        return;
    }

    char* storage = malloc(MAX_SEQ);
    if (storage == NULL) {
        perror("malloc");
        return;
    }

    char* test_results[] = {"00 00 01 02 03 04 05 \n",
                            "10 00 01 02 03 04 05 \n",
                            "20 00 01 02 03 04 05 \n",
                            "30 00 01 02 03 04 05 \n",
                            "40 00 01 02 03 04 05 \n"};
    int counter = 0;
    printer();

    if (freopen("/dev/tty", "w", stdout) == NULL) {
        perror("/dev/tty");
        return;
    }

    FILE* output = fopen("./check_str_out", "r");
    if(output == NULL) {
        perror("./check_str_out");
        return;
    }

    while (counter < sizeof test_results / sizeof *test_results &&
           fgets(storage, MAX_SEQ, output))
    {
        printf("count: %d, stor: %s, test: %s, comp: %d\n", counter,
               storage, test_results[counter],
               strcmp(storage, test_results[counter]));
        counter++; 
    }
    free(storage);
    fclose(output);
}

int main(void) {
    check_string();
    return 0;
}

Regarding pointer:
pointer == stdout throughout the whole program after FILE* pointer = freopen("./check_str_out", "w", stdout);. You can't read from stdout. It's write-only.

"I can't seen the fclose() of the file opened by freopen(), however it looks like the stream has been closed. What is the cause of that?" - stdout is closed automatically when the program terminates.

Upvotes: 2

Related Questions