[comp.lang.c] Need help w/unbuff'd input.

TIM@ENH.Prime.COM (06/07/90)

    I'm trying to write a function in C on Un*x that will allow me to read one
character unbuffered from stdin. I'm using the Berkeley shell, but I want to
write it to be able to use in the Bourne shell as well. If you're familiar
with Turbo C on the PC, I'm trying to write a getche() function.

    If there's already one there, or you know how to make one function read
one character unbuffered (not the whole program - just that function), please
give me a clue.

    Send replies to address below, or post here. Thanks in advance!

/*
Tim Cantin, Prime Computer, Inc., MS 10-13
500 Old Conn Path, Framingham, MA 01701  (508)879-2960 x3101
(TIM@ENH.Prime.COM) || (csnet-relay!ENH.Prime.COM!TIM)
*/
#include "stddisclaimer.h"

hannum@chopin.psu.edu (Charles Hannum) (06/09/90)

What you need to do is set 'cbreak' mode on your tty.  This tells the tty
driver not to do line editing.  Here's what to do:

  When you want to start character-by-character input:

    #include <sys/ioctl.h>
    #include <sgtty.h>

    struct sgttyb otty, ntty;

    /* emacs-insert-whatever-code-here */

    ioctl(stdin, TIOCGETP, &otty);
    ntty=otty;
    ntty.sg_flags |= CBREAK;
    /* Use the following line if you *don't* want it to echo: */
    /* ntty.sg_flags &= ~ECHO; */
    ioctl(stdin, TIOCSETP, &ntty);

    /* emacs-insert-more-code-here */

  Then, to set the terminal back to its previous mode:

    ioctl(stdin, TIOCSETP, &otty);

  MAKE SURE YOU DO THIS!  If you don't set the tty back to the correct mode,
  you will have no line editing (and if you turned off echo, no echo either),
  which makes life very miserable.  (tcsh sets the mode back automatically,
  BTW, but csh does not.)

NOTE:  This only works on ttys, not sockets or other types of files.
--
 
Virtually,
Charles Martin Hannum		 "Those who say a thing cannot be done should
Please send mail to:		  under no circumstances stand in the way of
hannum@schubert.psu.edu		  he who is doing it." - a misquote

scs@adam.mit.edu (Steve Summit) (06/12/90)

This topic is discussed in the frequently-asked questions list
(question 43 in the June posting).

In article <612@nic.stolaf.edu> hannum@chopin.psu.edu (Charles Hannum) writes:
>What you need to do is set 'cbreak' mode on your tty.

This is true only under some versions of Unix.  It is not true
under other versions of Unix (i.e. under System V and POSIX and
related systems, including, I am told, 4.4bsd, you play with
"min" and "time" parameters to set up various forms of "non-
canonical mode input processing") nor is it generally true under
non-Unix systems.

>    ioctl(stdin, TIOCGETP, &otty);

stdin is usually a FILE *, not the int which ioctl expects.
You could use fileno(stdin).  Note that you should really make
sure the ioctl succeeds -- it will not, for instance, in the
common case when your program has its input redirected from a
file.  (Chris did note later in his posting that "this only works
on ttys.")

                                            Steve Summit
                                            scs@adam.mit.edu

>  (tcsh sets the mode back automatically...

A questionable feature, since (at least on the version of tcsh
here, which may be out of date) the stty command cannot then be
used to adjust terminal driver parameters you do want to change
permanently.  I'm sure there have been flame wars about this
topic before; I probably shouldn't mention it.