Source: CS:APP3e, Bryant and O’Hallaron, problem 3.71

“Write a function good_echo that reads a line from standard input and writes it to standard output. Your implementation should work for an input line of arbitrary length. You may use the library function fgets, but you must make sure your function works correctly even when the input line requires more space than you have allocated for your buffer. Your code should also check for error conditions and return when one is encountered.”

My solution

fgets() returns NULL on error or EOF if no characters were read, and otherwise returns the buffer pointer. We can treat the return value as a boolean value to enable buffer reuse, by making the expression fgets(buf, BUFMAX, stdin) the boolean condition for a while loop. The loop will continue for as long as the return value of fgets() is truthy. (This is the compact style one sees in the character I/O section of K&R, §1.5.)

fgets() appends a data-terminating ‘\0’, so we can use that as a loop-terminating condition when initializing a for loop to iterate over the buffer.

This for loop calls putchar() to send each character to stdout. The return value of putchar() is the character, so we can test the expression putchar(buf[i]) for equality with the newline character ‘\n’ and return if we encounter it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>

#define BUFMAX 8

void good_echo() {
    char buf[BUFMAX];
    int i;

    // For as long as `fgets()` does not return NULL or EOF —
    // that is, for as long as it is still accepting stdin...
    while (fgets(buf, BUFMAX, stdin)) {

        // At any point when `buf` has been filled, iterate over it.
        for (i = 0; buf[i] != '\0'; i++) {

            // `putchar()` sends the char to stdout. It also returns
            // the char, so we can test it to see if it's a newline.
            if (putchar(buf[i]) == '\n') return;
        }
    }

}


int main() {
    good_echo();
    return 0;
}