[comp.unix.programmer] reading chars in raw mode with Ioctl .. How ?

kcw@beach.cis.ufl.edu (Ken Whedbee) (10/16/90)

Hello Continent -

I m looking for example code (bsd derivative .. SunOS perferably) that
reads characters one at a time instead of buffering the chars until a
newline like the read() system call.

I understand that ioctl() can do this by setting the terminal into
raw mode such that no pre-processing of the input takes place.  There's
example code of how to do this on pg. 340 in Bach's book.  But alas,
this is system V code.

Does anyone know of or can give me an example in bsd ?

many thanx!


--
Ken Whedbee                  Internet:  kcw@beach.cis.ufl.edu
University of Florida        UUCP:  ..!uflorida!beach.cis.ufl.edu!kcw
"C Code.  C code run.  Run, code, run... PLEASE!!!"  -- Barbara Toungue

felps@convex.com (Robert Felps) (10/16/90)

In <24905@uflorida.cis.ufl.EDU> kcw@beach.cis.ufl.edu (Ken Whedbee) writes:

>I m looking for example code (bsd derivative .. SunOS perferably) that
>reads characters one at a time instead of buffering the chars until a
>newline like the read() system call.

>I understand that ioctl() can do this by setting the terminal into
>raw mode such that no pre-processing of the input takes place.  There's
>example code of how to do this on pg. 340 in Bach's book.  But alas,
>this is system V code.

>Does anyone know of or can give me an example in bsd ?

This should do BSD or SV single character input.

---------------------------------------------------------------------------
/*
This program resets the tty driver to allow single character input.
It provides for single character input in shell scripts.

Usage:
	ANSWER=`getsks`

To compile and install:

	cc -D[BSD|SV] getsinglekey.c -o getsks
	chmod 555 getsks
	cp getsks /usr/local/bin
 */

#include <sys/types.h>
#include <signal.h>

#ifdef BSD
#include <sgtty.h>
	/* include the BSD terminal handling header file. */
struct sgttyb new, old; /* BSD term-info structure */
#else
	/* include the System V terminal handling header file. */
#include <termio.h>
struct termio new, old; /* SYSV term-info structure */
#define gtty(fd,arg)   (ioctl(fd, TCGETA, arg))
#define stty(fd,arg)   (ioctl(fd, TCSETA, arg))
#endif

main()
{
  int c;

  gtty(0,&old);
  gtty(0,&new);

#ifdef BSD /* set "raw" mode for BSD */
  new.sg_flags &= ~(ECHO|CRMOD);
  new.sg_flags |= RAW;
#else
  new.c_iflag &= ~(INLCR|ICRNL|IUCLC|ISTRIP|IXON|BRKINT);
  new.c_oflag &= ~OPOST;
  new.c_lflag &= ~(ICANON|ISIG|ECHO);
  new.c_cc[VMIN]  = 1;
  new.c_cc[VTIME] = 1;
#endif

  stty(0,&new);

  if (c=getchar()) {
    stty(0,&old);
    putchar(c);
    exit(0);
  }
}
---------------------------------------------------------------------------

Hope it helps,

Robert Felps                                felps@convex.com
Convex Computer Corp                        OS System Specialist
3000 Waterview Parkway                      Tech. Assistant Ctr
Richardson, Tx.  75083                      1(800) 952-0379

brister@decwrl.dec.com (James Brister) (10/16/90)

On 15 Oct 90 20:31:15 GMT, kcw@beach.cis.ufl.edu (Ken Whedbee) said:

> I m looking for example code (bsd derivative .. SunOS perferably) that
> reads characters one at a time instead of buffering the chars until a
> newline like the read() system call.

Here's my (very old) version of the "pick" program from Kernighan and
Ritchie's UNIX programming book. It uses CBREAK mode, and it runs on
Ultrix--but I doubt porting would be a problem.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  pick.c
# Wrapped by brister@westworld on Tue Oct 16 09:51:57 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'pick.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pick.c'\"
else
echo shar: Extracting \"'pick.c'\" \(1018 characters\)
sed "s/^X//" >'pick.c' <<'END_OF_FILE'
X#include <sgtty.h>
X#include <stdio.h>
X
char *progname ;      /* name of program for error message */
int pipeout ;
X
main (argc,argv)
X  int argc ;
X  char *argv [] ;
X{
X  int i ;
X  struct sgttyb oldmode, newmode ;
X
X
X  if (argc == 1)
X    {
X      fprintf (stderr,"%s [-] [files...]\n",progname) ;
X      exit (1) ;
X    }
X
X  pipeout = isatty (0) ;
X  progname = argv [0] ;
X  if ( gtty (0,&oldmode) == -1 )
X    { perror ("gtty") ;} 
X  newmode = oldmode ;
X  newmode.sg_flags |= CBREAK ;
X  if ( stty (0,&newmode) == -1 )
X    { perror ("stty") ; }
X
X  i = 1 ;
X  while ( i < argc && pick (argv [i]) == 1 )
X    i++ ;
X
X  stty (0,&oldmode) ;
X  exit (0) ;
X}
X
pick (s)
X  char *s ;
X{
X  int c ; 
X
X  fprintf (stderr, "%s? ",s) ;
X  c = ttyin () ; 
X  fprintf (stderr,"\n\r") ;
X  if ( c == 'y' )
X    {
X      printf ("%s\n",s) ;
X      return 1 ;
X    }
X  else if ( c == 'q' )
X    return 0 ;
X  else 
X    return 1 ;
X}
X
int ttyin ()
X{ 
X  char c ;
X  if ( ! read (0,&c,sizeof (c)) )
X    { perror ("read") ; return 'q' ; }
X  else
X    return c ; 
X}
X
X 
END_OF_FILE
if test 1018 -ne `wc -c <'pick.c'`; then
    echo shar: \"'pick.c'\" unpacked with wrong size!
fi
# end of 'pick.c'
fi
echo shar: End of shell archive.
exit 0




James
--
James Brister                                           brister@decwrl.dec.com
DEC Western Software Lab., Palo Alto, CA    {uunet,sun,pyramid}!decwrl!brister

guy@auspex.auspex.com (Guy Harris) (10/19/90)

>This should do BSD or SV single character input.

It does, but it may be overkill.  The problem is that the term "raw
mode" is used to refer both to the mode that, in the older V7-based tty
drivers such as the one in BSD, is actually called RAW mode, *and* to
any mode that gives you characters as they're typed.

RAW mode does a *lot* more than just give you characters as they're
typed.  It also:

	puts the line in 8 bits, no parity, mode;

	turns off all output processing;

	turns off special handling of all input characters, including
	the interrupt, quit, suspend (if you have job control), ^S, and
	^Q characters;

etc..

Not all applications that want to read characters one at a time
necessarily want to do all that.

In the older tty driver, you can turn CBREAK mode on, which will only
put the driver in "character at a time" mode and, as such, disable the
line-editing characters; it won't turn off the signal-generating
characters nor ^S nor ^Q, nor will it affect output processing or change
the parity mode of the line.  In the newer tty driver, you can turn
ICANON mode off, which has the same effect.

This does, of course, mean that you now have to *catch* signals such as
SIGINT and, if you have job control, SIGTSTP, and handle them
appropriately; otherwise, if you abort the program by typing your
interrupt character, or stop it by typing the suspend character if you
have job control, the tty will still be in character-at-a-time mode.

It also means you can't use ^S, ^Q, etc.  as input characters; you'd
have to disable those as well.  Individual control characters can
sort-of be disabled in the older driver by setting them to '\377', as
the older driver usually strips input characters to 7 bits before
comparing them with those special characters.  (As of 4.3BSD, though, if
you turn PASS8 on, it won't strip it, so if somebody happens to have a
key that generates, say, the ISO Latin 1 "y with a diaresis" character,
whose character code happens to *be* '\377', you may be in for a
surprise if you set one of those characters to '\377'.  You might try
'\200' instead, as it's a less-likely character to get as input.)

The newer driver lets you disable the signal-generating characters
(interrupt, quit, suspend if you have job control) by turning ISIG off,
and disable ^S and ^Q by turning IXON off. 

If you want 8-bit input - e.g., if your terminal has a META key that
turns the 8th bit on, and you want to be able to use it - PASS8 mode
works with the 4.3BSD and later versions of the older driver; it puts
the tty port into 8 bits, no parity mode, and prevents characters from
being stripped to 7 bits on input.  In the newer driver, you have to
turn the ISTRIP bit off (to prevent characters from being stripped to 7
bits on input), and set the "c_cflag" field to include CS8 and turn off
PARENB (for 8 bits, no parity).

guy@auspex.auspex.com (Guy Harris) (10/31/90)

>A much simpler way to guarantee that program X will not mess up your tty
>modes is to run it under pty. pty is totally transparent to job control,
>but will restore your tty modes properly on stop or exit.

Does it restore my screen as well?  I.e., if it's a full-screen program,
when I hit ^Z, will it either clear the screen or put the cursor at the
bottom of the screen, and when I continue the program will it redraw the
screen?

If not, I'm not interested.

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/31/90)

In article <4214@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
> >A much simpler way to guarantee that program X will not mess up your tty
> >modes is to run it under pty. pty is totally transparent to job control,
> >but will restore your tty modes properly on stop or exit.
> Does it restore my screen as well?  I.e., if it's a full-screen program,
> when I hit ^Z, will it either clear the screen or put the cursor at the
> bottom of the screen, and when I continue the program will it redraw the
> screen?

If that's what your program does, yes. One of my first design criteria
was that pty vi should feel *exactly* like a normal vi. (And it's
amazing how many roadblocks POSIX throws in the way of this goal.)

It's certainly not pty's responsibility to redraw the screen if your
program doesn't. Virtual terminal management does not fall under
pseudo-tty session management. If you want this feature, make it into a
separate program.

Okay, okay, I just spent a few minutes and did the work. Enclosed is
a sloppy fullscreen.c. You might invoke it as

  pty -pcE fullscreen nastyprog

nastyprog will run in a full-screen curses window, in character mode
with echo off. Terminal stop characters will affect pty rather than
going through to nastyprog. Your tty will be restored to its original
mode upon ^Z or nastyprog exit. curses will do what you asked for.

Any other requests?

> If not, I'm not interested.

What a productive attitude.

---Dan

#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <curses.h>

#define SIZ 1024

int f;
int pi[2];
char buf[SIZ];
int r;
int i;

sigchld()
{
 union wait w;
 int x;
 int y;

 if (wait3(&w,WUNTRACED | WNOHANG,(struct rusage *) 0) <= 0)
   return;
 if (w.w_stopval == WSTOPPED)
   kill(f,SIGCONT);
 else
  {
   r = read(pi[0],buf,SIZ);
   if (r > 0)
     for (i = 0;i < r;i++)
       addch(buf[i]);
   refresh();
   move(23,0);
   refresh();
   getyx(stdscr,y,x);
   mvcur(y,x,23,0);
   endwin();
   resetty();
   exit(w.w_retcode & 127);
  }
}

main(argc,argv)
int argc;
char *argv[];
{
 signal(SIGCHLD,sigchld);
 pipe(pi);
 switch(f = fork())
  {
   case -1: break;
   case 0: close(pi[0]);
	   dup2(pi[1],1);
	   close(pi[1]);
	   execvp(argv[1],argv + 1);
	   break;
   default: close(pi[1]);
	    close(0);
	    savetty();
	    initscr();
	    refresh();
	    scrollok(stdscr,1);
	    for (;;)
	     {
	      r = read(pi[0],buf,SIZ);
	      if (r == 0)
	        break;
	      if (r > -1)
		for (i = 0;i < r;i++)
		  addch(buf[i]);
	      refresh();
	     }
	    sigpause(0);
  }
 exit(0);
}