[net.lang.c] Need Help: 1-char input in C

mort@ihuxn.UUCP (Dubman) (12/22/83)

I am writing a program in C that requires a menu with four choices,
I, D, L, and Q for Insert, Delete, List and Quit.

I want to have it so that the program can key an ASCII value for the key
from the VT100 terminal without having to have the user type RETURN.

I tried using getchar() but inputs an entire line up to the RETURN, returns
the first letter, and shoves the rest in a buffer that is emptied the next
time getchar() is called.

I just read that CP/M's version of C returns the value of the key as soon
as the key is typed, but UNIX C (which I am using) waits for a newline.
Yet, I know that many C utilities do accept a key as soon as I type it.

Any ideas?

						Thanks in Advance,

-- 

Jonathan Dubman - care of:

		Mort Dubman		AT&T Bell Laboratories
		ihnp4!ihuxn!mort	Naperville, IL.

keesan@bbncca.ARPA (Morris Keesan) (12/23/83)

----------------------------
>   I want to have it so that the program can key an ASCII value for the key
>   from the VT100 terminal without having to have the user type RETURN.
>   
>   I tried using getchar() but inputs an entire line up to the RETURN, returns
>   the first letter, and shoves the rest in a buffer that is emptied the next
>   time getchar() is called.
>   
>   I just read that CP/M's version of C returns the value of the key as soon
>   as the key is typed, but UNIX C (which I am using) waits for a newline.
>   Yet, I know that many C utilities do accept a key as soon as I type it.
--------------------------------------------------------
    This is very simple, and it's nothing to do with C, but rather a UNIX(tm)
feature.  In normal modes, the UNIX terminal driver will do just this, hold
the input until a newline is input, and then make it available to the
program.  What you want to do is set CBREAK mode, which will make each
character available as it's typed.  Try setting in from the command line by
typing
	stty cbreak
to see if it behaves the way you want it to, and then put it into your program
using gtty() and stty(), or ioctl().  See ioctl(2) and tty(4).  You should
get the mode settings first, remember them, then set CBREAK, and restore the
original modes when you're through.  Note that CBREAK will effectively disable
any line editing (such as erase and line-kill).
-- 
					Morris M. Keesan
					{decvax,linus,wjh12}!bbncca!keesan
					keesan @ BBN-UNIX.ARPA

ka@hou3c.UUCP (Kenneth Almquist) (12/25/83)

CBREAK mode is specific to BSD.  On System III/V you can get the same
effect by turning off ICANON.  On Version 6 and Version 7 you can turn
on RAW mode, but beware that raw mode turns off almost all driver
processing.  See your manual for details.
				Kenneth Almquist

rf@wu1.UUCP (12/28/83)

The following will work only if you controlling terminal is not
initially in cbreak mode:

   system("stty cbreak");
   c=getchar();
   system("stty -cbreak");

Raw mode should be avoided, since it will sometimes return
characters with whose values are either less than zero or
greater than 127.  A switch to raw mode may also cause the
terminal to lose some output, since raw mode disables the
XON/XOFF protocol.

You probably want to enable cbreak mode when you begin your
program and end it when you're done.  The following less
portable code is a hacked version of something which does work
and is much faseter than system("stty . . .");.  It probably
works, but has not been tested.

struct sgttyb otsgb;

/* iniline -- initialize line

This routine stores the terminal characteristics of stdin in
otsgb and turns on cbreak mode.   If stdin's file is not a
terminal (perhaps for testing,) iniline does nothing.

 */
iniline() {
   struct sgttyb ntsgb;
   
   if (isatty(fileno(stdin))) {
      ioctl(fileno(stdin), TIOCGETP, &otsgb);
      ntsgb = otsgb;
      ntsgb.sg_flags |= CBREAK;
      ioctl(fileno(stdin), TIOCSETP, &ntsgb);
      }
   }

/* resetline -- reset line characteristics

This routine restores the terminal characteristics of stdin,
which should be preserved in otsgb.  If the line's file is not a
terminal (perhaps for testing,) resetline does nothing.

 */
resetline() {
   if (isatty(fileno(stdin))) {
      ioctl(fileno(stdin), TIOCSETP, &otsgb);
      }
   }


			Randolph Fritz
			Western Union Telegraph

dave@utcsrgv.UUCP (Dave Sherman) (12/29/83)

Kenneth Almquist is incorrect about CBREAK. It appears in the
v7 manual and is standard on v7. It did not exist in v6, which
had only RAW.

Dave Sherman
-- 
 {allegra,cornell,decvax,ihnp4,linus,utzoo}!utcsrgv!dave

guy@rlgvax.UUCP (Guy Harris) (12/31/83)

	CBREAK mode is specific to BSD...On Version 6 and Version 7
	you can turn on RAW mode, but beware that raw mode turns off
	almost all driver processing.

CBREAK mode is specific to Version 7 and its descendants (at least its
descendants along the V7-2.xBSD and the V7-32V-3.xBSD-4.xBSD line); it
is not just present in BSD systems.

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

guy@rlgvax.UUCP (Guy Harris) (12/31/83)

Two small addenda to Mr. Fritz's suggestion:

1) Doing the IOCTL's yourself will not just be faster than using
system("stty cbreak"), it will be *massively* faster if "cbreak" mode is
turned on or off more than once per session.

2) The user should ensure that "resetline" is called whenever the program
exits, even if the program is being terminated because the user hit their
interrupt key.  The program should catch the SIGINT signal, and the
routine which catches this signal should restore the terminal modes and then
exit.

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy