Flushing pending input so that a user's
typeahead isn't read at the next prompt.
|
The Problem.
The code in listing 13.6 of the book the book "SAMs Teach Yourself C for Linux Programming
in 21 Days" has a problem.
The problem occurs when a program reads user input, then does some time consuming
processing (say more than a couple of seconds) before reading more user input.
In this situation, the user might type some more on the keyboard while the program
is processing and these characters will be buffered by the operating system and
read on the next read call.
The idea of the existing code was to use fflush (stdin) to flush all the stored
characters from the input buffer.
As Benjamin Black pointed out, the
C Programming FAQ
states that the behavior of fflush () is defined only for output streams.
To make matters worse there is no standard ANSI/ISO C way of doing this.
Fortunately there is a POSIX solution.
The POSIX solution is to use the low-level unbuffered input functions or GNU/Linux
specific higher level function which in turn use the lower level ones.
The High Level Solution.
The high level solution uses the GNU readline library for handling user input and
functions defined in <termios.h> for modifying the behavior of the terminal
on the fly.
The code for this example may be downloaded
here.
and there is also a version with line numbers is available
here.
Instructions for compiling this program is in the comments at the top of the file.
This program includes some common header files on lines 46 to 48 and then includes
<termios.h> and <readline/readline.h> on lines 49 and 60.
The termios.h header is available on nearly all Unix-like operating systems while
readline.h is a non-standard header which is usually available on all GNU/Linux
systems.
(If it is not, it may because the a readline package or the readline-devel package has
not been installed.)
Lines 57 and 58 contain prototypes for functions implemented further on in the file.
As the comments on lines 64 to 69 explain, this program changes the behavior of the
terminal during operations.
If the program were to change these setting and then exit, the changes would be
permanent which is rather undesirable.
For this reason, we use the atexit() function to register a function (named
restore_terminal ()) which will be called when the program exits.
To read input from the user we use readline () function on line 73.
This function allows full command line editing just like the bash shell allowing
the user to use the backspace key to correct typing mistakes.
The call to readline () returns a string which is passed to sscanf ()
to read the int.
The sscanf () function is similar to scsanf () but scans a string
rather than input from the user.
The call to sleep (5) on line 82 simulates the processing of data that might
be occurring, however before that a call is made to
silence_terminal (TERM_SILENCE_ON) on line 79.
Similarly silence_terminal (TERM_SILENCE_OFF) on line 87 is called when we
want to turn the terminal back on before calling readline () again on line
88.
The restore_terminal () function is implemented on lines 100 to 102.
It simply calls silence_terminal (TERM_SILENCE_OFF) to restore the
terminal settings.
The real trick of this program are in the function silence_terminal () on
lines 104 to 140.
This function needs to store the original terminal settings when it is first called.
It also needs to store modified terminal settings for when it sets the terminal
into silent mode.
This is achieved by the use of static variables.
Remember that static variables are variables which are initialized on the first call
to the function but retain (ie remember) their values between calls (unlike automatic
variables which have no memory).
A static int (line 116) is used to allow the function to remember whether it
has been run before or not and two static struct termios structures are used
to store the original and silent terminal settings.
On the first call to silence_terminal () the int initialized is set to
zero and the statements from lines 120 to 125 are executed.
The original terminal settings are retrieved and stored in the original
structure on line 120.
A second copy of these settings is retrieved on line 122 and modified on line 124
before the initialized variable is set to one on line 125.
Lines 128 to 136 either modify or restore the terminal settings depending on whether
the on parameter is TRUE or FALSE.
In addition, when the terminal is restored to its original condition all pending
input characters (which have been stored by the operating system) are discarded
using a call to tcflush().
You are encouraged to modify and experiment with this program to reassure yourself
that it does indeed work as its supposed to.
It is also worth reading the man pages for readline (type 'man readline' in an
xterm) and the terminal control functions like tcflush and tcgetattr
which are covered on the termios man page.
The Low Level Solution.
Coming soon.
|