1

Consider this program (a basic implementation of cat):

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

int main() {
  char buffer[4096];
  ssize_t bytes_read;
  while ( ( bytes_read = read(STDIN_FILENO, buffer, sizeof buffer) ) > 0) {
    ssize_t bytes_written = write(STDOUT_FILENO, buffer, bytes_read);
    if (bytes_written == -1) return errno;
    assert(bytes_read == bytes_written);
  }
  if (bytes_read == -1) return errno;
  assert(bytes_read == 0);
  return 0;
}

I was wondering why this hangs waiting for read(2) input to be terminated with a \n, when I didn't specify something like readline().

My initial assumption for read was that it would return 0 whenever input wasn't available and the associated file descriptor open, and -1 (a common representation for EOF) when it was closed, and perhaps another negative integer for error.

After reading the manual, and writing the program, I was under the impression that read(2) would block until any input was available with size >0 and immediately return with that information; however, it seems the terminal causes it to block until it receives a \n character.

My question is: Why does this not adhere to my expectations, and what is really happening here?

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
user129393192
  • 797
  • 1
  • 8
  • 4
    This has nothing to do with `read(2)`, it's the terminal driver. It allows you to edit your input and doesn't make the data available until you type Return or Ctl-d. – Barmar Apr 20 '23 at 22:01
  • 1
    Notice that it doesn't happen when reading from a file or pipe, or when the terminal is in raw mode. – Barmar Apr 20 '23 at 22:01
  • Input can be unbuffered, block buffered or line buffered. See the man page for `setvbuf`. Your terminal is set to line buffered. – stark Apr 20 '23 at 22:03
  • 1
    @stark `setvbuf()` does not apply to file-descriptor-based calls such as `read()`, only `FILE *`-based streams calls. – Andrew Henle Apr 20 '23 at 22:04
  • 2
    Using `assert()` for error checking is not a good idea. First, `assert()` is a no-op if compiled with `NDEBUG`, and you can't control how your code will be compiled in the future. Second, aborting the entire process is usually an **extreme** overreaction - programs are written in the real-world to do things, and in the real world things go wrong. Just about the worst thing a real-world program designed to actually do something can do when something like unexpected input is received is to give up, abort, and drop core. – Andrew Henle Apr 20 '23 at 22:09
  • 2
    `assert()` is intended to detect logic errors when debugging, not runtime errors that can actually occur in production. – Barmar Apr 20 '23 at 22:25
  • This is canonical mode of PTY. – dimich Apr 20 '23 at 22:28
  • Possible duplicate: [Canonical vs. non-canonical terminal input](https://stackoverflow.com/q/358342/12149471) – Andreas Wenzel Apr 20 '23 at 22:29
  • So awhat is exactly happening with `read()`? Is the program in a blocked state until the terminal receives a new line? – user8393252 Apr 20 '23 at 22:48
  • See [this answer](https://stackoverflow.com/a/912796/12149471) for an example of how to turn off canonical mode. – Andreas Wenzel Apr 20 '23 at 22:53
  • I have the same question as @user8393252 – user129393192 Apr 21 '23 at 02:06

0 Answers0