3

I don't understand why I need press Ctrl+D for three times to send the EOF.

In addition, if I press Enter then it only took one Ctrl+D to send the EOF.

How Can I make the change so that only took one Ctrl+D then it can detect the EOF?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE 1024

int main(){
    char buffer[BUF_SIZE];
    size_t msgLength = 1;
    char *msg = malloc(sizeof(char) * BUF_SIZE);

    msg[0] = '\0';
    printf("Message: ");
    while (fgets(buffer, BUF_SIZE, stdin)){
        char *old = msg;
        msgLength += strlen(buffer);
        msg = realloc(msg, msgLength);

        strcat(msg, buffer);
    }
    return 0;
}
Fabrizio
  • 7,603
  • 6
  • 44
  • 104
ELIJAH
  • 53
  • 1
  • 9
  • 3
    Please provide a [mcve]. – Paul R Mar 24 '17 at 14:14
  • `fgets` will return "NULL on error or when end of file occurs while no characters have been read." (`man fgets`). So it won't return null for the very last chunk of input, but only on the next one. – Eugene Sh. Mar 24 '17 at 14:17
  • 3
    I do not reproduce the behavior you describe in a simple test of your code. I speculate that it depends on the details of your input, but it may also have something to do with the *rest* of your program. If you want help, we need a [mcve]. – John Bollinger Mar 24 '17 at 14:27
  • Sad that this question was closed. It seems perfectly legit to me, and explainable. – kmkaplan Mar 24 '17 at 14:34
  • Sorry guys, code is updated. Its MCVE now. – ELIJAH Mar 24 '17 at 14:34
  • @kmkaplan Vote for reopen then... – Eugene Sh. Mar 24 '17 at 14:35
  • 2
    If you add this line as the first line in your loop, after the fgets, and run the program, perhaps it will be illuminating: `printf("GOT: [%s]\n", buffer);` (The first _two_ CTRL-Ds are required to end the partial line you have entered, and then the third CTRL-D, on a so far empty line, closes the input. Also, check this question: http://stackoverflow.com/questions/30137434/why-do-i-need-to-press-ctrld-twice-to-break-out-of-while-c-getchar-eof?rq=1 ) – Thomas Padron-McCarthy Mar 24 '17 at 14:36
  • @ThomasPadron-McCarthy _Two_ CTRL+Ds are required to flush data into the `stdin`? I thought one is enough – Spikatrix Mar 24 '17 at 14:41
  • @CoolGuy: Yes, when you have already entered some characters on the line, you need two CTRL-Ds. When the line is empty, one is enough, and will close the input. – Thomas Padron-McCarthy Mar 24 '17 at 14:42
  • 3
    The root problem is probably that ctrl-D does not mean "end of input", it means "flush input the input buffer to the program immediately". This answer http://stackoverflow.com/questions/42782905/getchar-and-conditioning-a-loop-to-break-with-a-specific-char/42783147#42783147 goes into more detail – JeremyP Mar 24 '17 at 14:44
  • @CoolGuy See my comment (second here) – Eugene Sh. Mar 24 '17 at 14:44
  • @CoolGuy the third is needed because of the stdio layer above raw `read` and its buffering. – kmkaplan Mar 24 '17 at 14:48
  • @EugeneSh. Ok, but I still don't get why there needs to be a third CTRL+D. I mean, user types, say, `blah blah` and then presses CTRL+D. Data will get flushed and `fgets` consumes it. `fgets` waits for input in the second iteration of the loop and the user presses a second CTRL+D. This should send the `EOF` since the input buffer is empty and `fgets` hasn't read anything in the second iteration, right? So, where does the third CTRL+D come from? – Spikatrix Mar 24 '17 at 14:53
  • " Data will get flushed and fgets consumes it." --> yes, but `fgets()` does not yet return, it is waiting for a `\n`. – chux - Reinstate Monica Mar 24 '17 at 17:44
  • Same question as https://stackoverflow.com/questions/29759979/ – M.M Jan 14 '21 at 20:25

2 Answers2

3

The problem you are having is that terminals don't have EOFs -- they're not files, so "end of file" doesn't really make any sense. Instead, they have EOT (end of transmission -- Ctrl+D), which generally registers as an EOF to stdio due to a (happy?) accident.

Files on UNIX signal EOF by returning a size 0 read, and stdio treats any size 0 read as an EOF regardless of whether it is reading from a file or not. EOT causes a terminal to return immediately, which will be a size 0 read (triggering an EOF in stdio) if and only if the input buffer is empty.

The easiest solution is not to worry about it -- just let the user hit Ctrl+D multiple times if they want to signal an EOF here. It should only require two, unless you have odd timing issues going on.

If you really want to, you can attempt to figure out if an EOT was hit -- if the result of fgets does not fill the buffer and does not end with a newline, then the user hit EOT to transmit it, so you could test for that and break out of the loop. This fails if the user enters exactly enough data to fill the fgets buffer and then hits EOT. It can also fail if the user is "typing" too fast for the program to keep up (an EOT while the program is not actually waiting for input has no effect). I say "typing" since the terminal might be a pseudo-terminal with something faking very fast input on the other end (including EOTs)

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
1

If you add this line as the first line in your loop, after the fgets, and run the program, perhaps it will be illuminating:

printf("GOT: [%s]\n", buffer);

The first two Ctrl+D are required to end the partial line you have entered, and then the third Ctrl+D, on a so far empty line, closes the input.

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
Thomas Padron-McCarthy
  • 27,232
  • 8
  • 51
  • 75