You are not logged in.

#1 2019-05-26 03:09:03

Portal
Member
Registered: 2019-03-11
Posts: 48

Read and while function in c (text editing in kilo)

I am reading the source of a text editor called kilo. Here is a line in the source code:

while(nread = read(fd,&c,1)==0);

I've never seen a semicolon-ended while loop... will it loop through the conditions without doing anything? Here is the entire function with the line in question:

int editorReadKey(int fd) {
    int nread;
    char c, seq[3];
    while ((nread = read(fd,&c,1)) == 0);
    if (nread == -1) exit(1);

    while(1) {
        switch(c) {
        case ESC:    /* escape sequence */
            /* If this is just an ESC, we'll timeout here. */
            if (read(fd,seq,1) == 0) return ESC;
            if (read(fd,seq+1,1) == 0) return ESC;

            /* ESC [ sequences. */
            if (seq[0] == '[') {
                if (seq[1] >= '0' && seq[1] <= '9') {
                    /* Extended escape, read additional byte. */
                    if (read(fd,seq+2,1) == 0) return ESC;
                    if (seq[2] == '~') {
                        switch(seq[1]) {
                        case '3': return DEL_KEY;
                        case '5': return PAGE_UP;
                        case '6': return PAGE_DOWN;
                        }
                    }
                } else {
                    switch(seq[1]) {
                    case 'A': return ARROW_UP;
                    case 'B': return ARROW_DOWN;
                    case 'C': return ARROW_RIGHT;
                    case 'D': return ARROW_LEFT;
                    case 'H': return HOME_KEY;
                    case 'F': return END_KEY;
                    }
                }
            }

            /* ESC O sequences. */
            else if (seq[0] == 'O') {
                switch(seq[1]) {
                case 'H': return HOME_KEY;
                case 'F': return END_KEY;
                }
            }
            break;
        default:
            return c;
        }
    }
}

Read returns the number of bytes read. Why is it compared to zero?

Offline

#2 2019-05-26 09:31:14

mpan
Member
Registered: 2012-08-01
Posts: 1,207
Website

Re: Read and while function in c (text editing in kilo)

Portal wrote:

I've never seen a semicolon-ended while loop...

The loop is not semicolon-ended. It contains an instruction in its body. That sole instruction is an empty instruction consisting of colon only. The code is equivalent to:

while ((nread = read(fd,&c,1)) == 0) {
    ;
}

Read returns the number of bytes read. Why is it compared to zero?

Because it may read 0 bytes.


Sometimes I seem a bit harsh — don’t get offended too easily!

Offline

#3 2019-05-26 12:41:12

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,523
Website

Re: Read and while function in c (text editing in kilo)

Please code cautiously.  You're excerpt is completely different from what is in the code: parentheses matter.

But for your actual question, RTFM:

man read wrote:

RETURN VALUE
       On  success,  the  number  of  bytes  read  is returned (zero indicates end of file) ...

So it looks like that command loops until there is something to read on the file descriptor.  Arguably this is an odd design and could likely be better handled by `select`, but entirely out of context it's hard to be sure.  I'm also curious what the fd is connected to and why it would continuously return and EOF when there was nothing to be read.  It's clearly being used like a fifo/pipe, but it seems set up to behavior like a regular file.  If it were actually a fifo or pipe, there would be no need for the loop at all: a single call to read could block until there was data on the pipe.

Last edited by Trilby (2019-05-26 12:46:06)


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#4 2019-05-26 19:41:35

mpan
Member
Registered: 2012-08-01
Posts: 1,207
Website

Re: Read and while function in c (text editing in kilo)

The behaviour with 0 indicating EOF is platform-specific. In the quoted example it is taken from manual for Linux (`man 2 read` instead of `man 3p read`). In the general case⁽¹⁾:

Upon successful completion, these functions shall return a non-negative integer indicating the number of bytes actually read. Otherwise, the functions shall return -1 and set errno to indicate the error.

Also note that in POSIX-compatible systems the term “end-of-file” is not strictly defined and usually is not a permanent condition. You might be able read past the EOF, depending on the stream type and conditions. Therefore even on Linux read returning 0 — from code’s perspective — really means “nothing has been read (= 0 bytes), but you may be more lucky on the next call”.

While I fully agree that select would be a more elegant solution and would use it myself, don’t get fooled into thinking it would solve the busy loop problem or let you avoid checking if read returns 0. It only offsets the issue. select marking a stream as having data to be read doesn’t imply the stream is having data to be read (surprise!). read may still fail to read any data. The reasons may vary: interrupts, the way things are implemented and others. This of course depends on what the file descriptor is representing and other conditions, but don’t blindly assume that select guarantees anything: it is merely giving a hint. A nice example has been given by David Schwartz in SO thread about `read` failing after `select`. `man 2 select` also warns about that possibility:

Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks.  This could for example happen when  data has  arrived  but  upon  examination  has wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready.

Though personally I do not count that as a bug.
____
⁽¹⁾ `pread` in IEEE Std 1003.1-2017


Sometimes I seem a bit harsh — don’t get offended too easily!

Offline

Board footer

Powered by FluxBB