Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why errno can set to zero by scanf?(when enter "ctrl+D")

Tags:

c

linux

The manual tells us:errno is never set to zero by any system call or library function. But I want to know, Why errno can set to zero by scanf in following codes?(when scanf:enter the"ctrl+D")

#include <stdio.h>
#include <errno.h>
int main()
{
    int i;
    errno = 5;
    printf("errno:%d\n",errno);
    if (scanf("%d", &i) < 1)
        perror("scanf");
    printf("errno:%d\n",errno);
    printf("i:%d\n", i);
    return 0;
}
like image 614
conquer Avatar asked Oct 20 '13 12:10

conquer


People also ask

What does scanf return if there is no input?

The scanf routine is designed to read input until a character is an input that conflicts with the format string. When such a character is read, then and only then does scanf return. So, if there is no input available, scanf just waits.

How do I stop EOF scanf?

A single Ctrl-D will flush the typed 'chars' and printing them. Then after that it will wait again for input, although an EOF has been sent with the Ctrl-D . If we press Ctrl-D again the 2nd time directly after the 1st (2x) or just Enter it will terminate.

Why scanf stops at space?

scanf() just stops once it encounters a whitespace as it considers this variable "done".

How do I scanf until enter?

We should use "%[^\n]", which tells scanf() to take input string till user presses enter or return key. Scanf scans till user presses enter or till user presses return key.


1 Answers

I can find the following code in the glibc implementation of vfscanf(), (line 589-607 in the linked file, as of the time of writing) which the implementation of scanf() calls:

if (skip_space || (fc != L_('[') && fc != L_('c')
                         && fc != L_('C') && fc != L_('n')))
  {
    /* Eat whitespace. */
    int save_errno = errno;
    __set_errno (0);
    do
      /* We add the additional test for EOF here since otherwise
         inchar will restore the old errno value which might be
         EINTR but does not indicate an interrupt since nothing
         was read at this time. */
      if (__builtin_expect ((c == EOF || inchar () == EOF)
                             && errno == EINTR, 0))
         input_error ();
    while (ISSPACE (c));
    __set_errno (save_errno);
    ungetc (c, s);
    skip_space = 0;
  }

input_error() is #defined as:

#define input_error()         do {     
                          errval = 1; 
                          if (done == 0) done = EOF;
                          goto errout; 
                        } while (0)

where errout is the label for the cleanup code at the end.

So it looks like errno is getting set to 0 prior to the inchar() call, and the old value is later replaced, leaving errno unchanged. But if an error occurs and that if statement executes (notably, if inchar() evaluates to EOF, which is what is happening in this case), it looks like the code to reset errno to its original value may be being skipped. That being said, the condition will only be true if errno == EINTR and therefore not zero, which certainly doesn't appear to be the case, here, so it may have nothing to do with this code, but this is only place I can see errno being set to 0. inchar() itself does mess around with errno, as the comment suggests, and can set errno to inchar_errno, which is initialized to 0 on line 223, so it's also possible there's some other execution path where inchar_errno is not updated but gets assigned to errno anyway.

like image 144
Crowman Avatar answered Oct 12 '22 07:10

Crowman