0

I have just encountered a problem which I couldn't found the reason. The code works as Below:

  1. There is a while loop which does a lot of stuff in the middle (Does not even written), and has a non-blocking IO to receive the end process command (which is a string).
  2. At any time, a user could come and wants to finish the process. He/She would press some keys and then press the [Enter] button.
  3. After select find out there is an input, it uses the input string and evaluates with the EXIT string and end the process.

Now the problem is when I use the exitFlag string as the termination key it just don't accept it and keeps writing <the printf() statement> until i hit cntrl+c. I have tried scanf(), fputs(), fread() to recieve input string from stdin and also used fflush(stdin) and fflush(stdout) to clear the std buffers.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>


int main() 
{ 
    fd_set          s;
    struct timeval  timeout;
    timeout.tv_sec = 3;
    timeout.tv_usec = 0;
    char exitFlag[10];
    int sFlag;
    printf("Enter \"EXIT\" to End the Process\n"); 
    while (1)
    {
        fflush(stdin); fflush(stdout);
        FD_ZERO(&s);
        FD_SET(STDIN_FILENO, &s);
        timeout.tv_sec = 3; timeout.tv_usec = 0;
        sFlag = select(STDIN_FILENO+1, &s, NULL, NULL, &timeout);
        printf("%d: ", sFlag);
        if      (sFlag < 0) _exit(0);
        else if (sFlag == 0) 
        {
            printf("-\n");
            fflush(stdin); fflush(stdout);
        }
        else     
        {
            fflush(stdin); fflush(stdout);
            scanf("%[^\n]s", &exitFlag); // fgets( exitFlag, 10, stdin); // fread( exitFlag, 10, 1 ,stdin);
            printf("You have Pressed %s, Enter \"EXIT\" to End the Process: ", exitFlag); 
            printf("%s\n", exitFlag);
            if ( memcmp(&exitFlag, "EXIT", 4) == 0 ) break;
        }
    }

  return 0; 
}


Ironically, I don't encounter similar problem when using char as the termination key input rather than string. I would be very thankful if someone explains where I made mistake in the IO or any other place.

I have also reviewed prior questions carefully but they had seemed to work with a single keyboard press which was not safe for common use!

include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>


int main() 
{ 
    fd_set          s;
    struct timeval  timeout;
    timeout.tv_sec = 3;
    timeout.tv_usec = 0;
    char exitFlag;
    int sFlag;
    while (1)
    {
        fflush(stdout);
        FD_ZERO(&s);
        FD_SET(STDIN_FILENO, &s);
        timeout.tv_sec = 3; timeout.tv_usec = 0;
        sFlag = select(STDIN_FILENO+1, &s, NULL, NULL, &timeout);
        if      (sFlag < 0) _exit(0);
        else if (sFlag == 0) 
        {
            fflush(stdout);
            printf("-\n");
        }
        else     
        {
            fflush(stdin);
            scanf("%c", &exitFlag);
            printf("You have Pressed %c, Press E to exit: ", exitFlag); 
            printf("%c\n", exitFlag);
            fflush(stdout);
            if ( exitFlag =='E' ) break;
        }
    }

  return 0; 

}

Msain
  • 1
  • 3
    `fflush(stdin);` This causes undefined behaviour. – Gerhardh Aug 16 '22 at 08:30
  • 1
    `scanf("%[^\n]s", &exitFlag);` What do you think that s will do? – Shawn Aug 16 '22 at 08:31
  • @Gerhardh Thanks for you answer but which one do you mean? I have tested all three of them but it didn't change anything. I would appriciate it if you be more precise. – Msain Aug 16 '22 at 08:34
  • All of them. Flushing `stdin` always is undefined behaviour. Not necessarily causing your problem but still undefined. – Gerhardh Aug 16 '22 at 08:35
  • `scanf(" %[^\n]", exitFlag);` and `scanf(" %c", &exitFlag);` with the spaces will clear the newlines. – Weather Vane Aug 16 '22 at 08:36
  • @Shawn Thanks a lot. But its a common notation. you can check it here [link] (https://www.scaler.com/topics/c/c-string-input-output-function/) _italic_ beside i have used fread fgets and other IO types. I'm not sure its the problem – Msain Aug 16 '22 at 08:37
  • 3
    In your link "We can also use the expression `%[^\n]s` inside `scanf()` to take the complete line including spaces as a string input in C" is ***wrong***. – Weather Vane Aug 16 '22 at 08:39
  • @WeatherVane Thanks for your comment. Still with *fread()* and *fgets()* we would have the same *error**. I have checked them too – Msain Aug 16 '22 at 08:41
  • 3
    Don't mix the use of `scanf` with `fgets` from the same stream. I suggest you find a better tutorial, it advises how to clear *trailing* newline, which is horrible kludge and faulty too. Use the space I showed to clear *leading* whitespace. The page also makes no mention of [scanf() leaves the newline char in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer). – Weather Vane Aug 16 '22 at 08:44
  • 2
    It also recommends the use of `gets()` which is dangerous and obsolete. – Weather Vane Aug 16 '22 at 08:46
  • Thanks for all the comments. I have learned a lot and appreciate your recommendations. Nonetheless, if I even comment out the whole **else {}** and remove **fflush()** this problem will still exists! – Msain Aug 16 '22 at 08:57
  • Have you [added the spaces](https://stackoverflow.com/questions/73370894/why-simple-read-string-from-non-blocking-io-in-c-behaves-oddly-in-loop#comment129573199_73370894)? Some explanation: most of the format specifiers for `scanf` automatically filter leading whitespace, but `%c` and `%[]` and `%n` do not. Adding a space in front of the `%` instructs `scanf` to filter leading whitespace here too. The reason is those three specifiers allow you to read every character including whitespace, but a way is provided so they behave like `%d` and `%f` and `%s` where needed. – Weather Vane Aug 16 '22 at 08:59
  • Thanks a lot @WeatherVane for your explanation. I have found that earlier and now you have given me more ideas yet, I have done something much simpler. I just put a *printf("Hi\n")* and removed everything in the **else{}** but the problem still exists. I have also removed **fflush()** in everywhere. – Msain Aug 16 '22 at 09:02
  • Your page recommends using the dreadful `%[^\n]%*c` to remove the newline. But the `%[]` format is no different from the others: they all leave the newline in the buffer. So for their remedy to be any use you'd have put that nasty kludge in every other use of `scanf` too. Use `scanf` as it intended. The key is understanding how it handles whitespace in the input, in the format string, and with different specifiers. The tutorial completely overlooks that the **real** problem is that `%c`and `%[]` do not filter the whitespace, unless you put a space in front of the format spec. – Weather Vane Aug 16 '22 at 09:09
  • Buffered input like `scanf` or `fgets` might block regardless of `select`'s result. `select` can tell you that the *next* `read` should not block, even if only a single byte is available. `scanf` wants to read as many input as necessary for the specified format string, which could be more than currently available, and it may use more than one `read` call. `fgets` tries to read until a line termination which poses a similar problem. You should combine `select` with `read` and implement your own buffering. (You could parse a buffer with `sscanf`, but better use other parsing for user input.) – Bodo Aug 16 '22 at 09:54

0 Answers0