Is there a way to drain STDIN by reading characters in non-blocking mode?

I was looking at code (as used in prolog_pack.pl) to query the user for a Y/N answer.

This code doesn’t use line discipline whereby the input needs to be terminated by a EOL (that would be the case when reading user input via read_line_to_string/2 for example) but just gets the next character from “the input buffer” (not sure who provides that, but it’s somewhere) with get_single_char/1 :

read_yes_no(YesNo, Default) :-
    get_single_char(Code),
    code_yes_no(Code, Default, YesNo),
    !.

code_yes_no(0'y, _, yes).
code_yes_no(0'Y, _, yes).
code_yes_no(0'n, _, no).
code_yes_no(0'N, _, no).
code_yes_no(_, none, _) :- !, fail.
code_yes_no(C, Default, Default) :-
    answered_default(C).

answered_default(0'\r).
answered_default(0'\n).
answered_default(0'\s).

get_single_char/1 blocks until there is something in the buffer (or an EOF is signalled).

Here lies a problem: If the user typed y twice on a preceding question, the the second y will be picked up by the get_single_char/1 meant for the subsequent question. This leads to erroneous results (that’s a problem that does not occur when reading read_line_to_string/2 as the user has to hit RETURN at least, so I really prefer read_line_to_string/2, which also works nicely with a pipe-from-the-yes-command).

To make sure I’m getting the right y, I want to drain the input buffer before starting with the next question, i.e. read characters in non-blocking mode until no characters are left, then ask. (What happens if someone pipes an infinite stream of y into the program? I guess that would be bad and subject to random outcomes.)

Is it possible to read characters in non-blocking mode?

The library for Primitive character I/O doesn’t seem to provide such a facility.

Indeed, even in C, you need to do a call to fcntl: Nonblocking Get Character

Note that I’m not even sure in which regime I’m working here:

the POSIX manual and all other related documentation explicitly says never to mix IO done on FILE* s and file descriptors

This is probably working on top of the filedescriptors (i.e. man 3P read).

did you try read_pending_codes/3 or read_pending_chars/3?

you may also want to check with_tty_raw/1, as pointed out by Jan here:

1 Like