Search code examples
ciostdin

Reading from STDIN with the read() function in c: a clarification


I'm trying to write the simplest, lowest level, most basic way to read from STDIN and do something with what I read when I reach a certain size (let's say 16 chars?). Consider this snippet:

#include <unistd.h>
#include <stdio.h>

#define BUFF 16

int main()
{
    char buf[BUFF];
    int just_read = -1;

    while(just_read != 0)
    {
        printf("entering the loop\njust_read: %d\n", just_read);
        just_read = read(0, buf, BUFF);
    }
}

I'm telling the read() func to try and read BUFF chars (16 in this case) but if I introduce a new line with Enter in the stdin I can see from my debug printf line that I'm starting another loop while I'd naively expect to stay in the read() until 16 chars are read. What's the signal sent by Enter? What would be the leanest way to do something (let's say write on STDOUT) with the read data every BUFF chars?

Output ex for BUFF 5:

stdin:
12<enter>
45678<ctrl+d>
stdout:
12
45

Solution

  • You need to keep track of the last position in buf where data was added. And you need to give a pointer to the next byte of buf to add data.

    Typically something like this:

    ssize_t read_all(int fd, char *buffer, size_t to_read)
    {
        // The position to read into
        char *current_pos = buffer;
    
        while (to_read > 0)
        {
            ssize_t nread = read(fd, current_pos, to_read);
            if (nread < 0)
            {
                // Error
                return -1;
            }
            else if (nread == 0)
            {
                // End of the file or input
                break;
            }
    
            // Next position to read into
            current_pos += nread;
    
            // We have read some, read less next time
            to_read -= nread;
        }
    
        // At this point we either have read all
        // or we have reached the end of the file/input
    
        // Return how much we actually did read
        return current_pos - buffer;
    }
    

    The read_all function will attempt to read all into the provided buffer. It will return either -1 on error, or the size that was actually read. If there was an end-of-file the value returned will be less than the value requested (could be 0 if the EOF was the first thing that happened).

    You could use it like this:

    char buf[BUFF];
    ssize_t nread = read_all(STDIN_FILENO, buf, sizeof buf);
    if (nread == -1)
    {
        perror("read_all");
    }
    else if (nread < sizeof buf)
    {
        printf("Short read of %zd bytes: %*s\n", nread, nread, buf);
    }
    else
    {
        printf("Read everything (%zd bytes): %*s\n", nread, nread, buf);
    }