0

I am doing a parser getting characters until EOF, I am handling the signals SIGTERM, SIGINT, SIGTSTP and SIGQUIT. So when I send a signal with Ctrl+C (for example to SIGINT) the signal handler prints Ctrl+C and then must continue, but is not continuing.

I figured out that after each Ctrl signal, an EOF is filling the buffer, but I do not know how to get rid of it. I tried to do while(getc(stdin) != EOF); inside the handler, but the parser behave normally, "eating" every character typed until EOF.

How can I receive a Ctrl signal without messing up my stdin?

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

static void nsh_signal_handler(int code)
{
    char c;

    switch(code)
    {
        case SIGQUIT: c = '\\'; break;
    }

    fprintf(stdout, "I will do nothing about your Ctrl - %c\n", c);
}

int main(void)
{
    int c;
    struct sigaction s;

    s.sa_flags = 0;
    s.sa_handler = nsh_signal_handler;
    sigfillset(&s.sa_mask);
    sigaction(SIGQUIT, &s, NULL);

    do
    {
        c = getc(stdin);
        printf("[%c][%d]\n", c, c);
    }
    while(c != EOF);

    return 0;
}

The code above show this error.

ViniciusArruda
  • 970
  • 12
  • 27

2 Answers2

1

The getc call will return EOF for either a true end of file or an error condition.

If you need to differentiate between the two, you can use feof or ferror to do so.

For your particular case, you could simply replace:

while(c != EOF);

with:

while ((c != EOF) || (! feof(stdin)));

Perhaps even better would be to avoid trying to output the character at all if it's one that was caught by the signal. You could do this by changing the loop to something like:

do {
    c = getc(stdin);
    while ((c == EOF) && (!feof(stdin))) { // throw away CTRL-whatever.
        //clearerr (stdin);
        c = getc(stdin);
    }
    printf("[%c][%d]\n", c, c);            // print true characters and last EOF.
}
while (c != EOF);

You'll notice there's a call to clearerr commented out in the code above. If your implementation is one of those that will continuously supply an error indication until you clear the error flag (Linux is not one of those), you may want to uncomment it, especially since it shouldn't otherwise harm the flow of the code.

I actually see that behaviour as a likely violation of the standard since it specifically states:

If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the endoffile indicator for the stream is set and the fgetc function returns EOF. Otherwise, the fgetc function returns the next character from the input stream pointed to by stream. If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF.

In other words, no mention is made of a preceding error indicator preventing future reads. The only way I could see that being allowed is if the text "a read error occurs" included the event "a read error has previously occurred and the error indicator has not been reset", which seems a bit of a stretch to me.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    Once there has been an error (such as getting an `EINTR`, as is happening here) EVERY call to getc will return EOF, and `ferror(stdin)` will be true, until you call `clearerr`. So you need to put a call to `clearerr(stdin)` in the inner while loop before calling `getc`, or it will loop forever (appearing to hang). – Chris Dodd May 25 '16 at 03:27
  • @Chris, actually I don't believe that's so. ISO (C11 7.21.7.1) only states that if the eof flag is set for the stream or it encounters eof, it will return EOF and set the eof flag. Otherwise it gets a character. It can *set* the error flag but it does not check it. – paxdiablo May 25 '16 at 03:41
  • The C standard is completely silent about what happens on an error condition, leaving it implementation defined. Most implementations treat it as persistent -- once the error flag is set, every call will return an error until it is cleared with `clearerr`. – Chris Dodd May 25 '16 at 03:53
  • @Chris: If Linux is one of those "most implementations" you discuss, you may want to test it :-) It recovers fine as is after the EINTR and continues to retrieve real input from the stream. The standard is quite explicit in the required behaviour - the *only* way for fgetc to fail without a preceding end-of-file is if "a read error occurs", otherwise the next character is read. I suppose an implementation *could* define "a read error occurs" as including the case where a read error has previously occurred and not been cleared but that seems a bit of a stretch. However, I'll update to cover it. – paxdiablo May 25 '16 at 04:09
1

If you don't want to have to deal with errors on your read calls after handling the signal, you can set the SA_RESTART flag on the signal handler:

s.sa_flags = SA_RESTART;

which will result in the getc calls not getting interrupted with an EINTR error by the signal handler.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226