[comp.unix.questions] Terminal modes in Ultrix's System V emulation

leo@philmds.UUCP (Leo de Wit) (10/04/88)

When porting a database application to Ultrix (Oracle dbms), I found
out we had to use System V libraries (grrr 8-), since the Oracle
libraries depended upon them. I don't want to discuss whether Oracle's
decision to not use the native libraries is a correct one (maybe some
other time 8-), but you can imagine some of our code had to be
rewritten (most of the Unix specific stuff had already been written
before we had Oracle running on Ultrix and were aware of this
problem).

The problem:
I want to read characters from a terminal, without echo and not having
to wait for a newline. After each character I might do some processing,
based on the character read. The old solution did something along the
lines of:

#include <sgtty.h>

    struct sgttyb newbuf, oldbuf; /* oldbuf needed for restoring afterwards */

    ioctl(0,TIOCGETP,&oldbuf);
    newbuf = oldbuf;
    newbuf.sg_flags &= ~ECHO;
    newbuf.sg_flags |= CBREAK;
    ioctl(0,TIOCSETP,&newbuf);

and now filedescriptor 0 is OK for my purposes (I can use stdio or whatever
from now on and all works great).

Using System V's terminal modes there seems to be no real equivalent of
'cbreak'; so I had to use non-canonical mode to be able to read on a
per character basis (right ?).  We don't have System V manuals around
and the Ultrix manuals weren't such great help on this point; most
things I found out using Sun manuals and include files (e.g.
termio(4V)). I got this far:

#include <sys/termio.h>

    struct termio newbuf, oldbuf; /* oldbuf needed for restoring afterwards */

    ioctl(0,TCGETA,&oldbuf);
    newbuf = oldbuf;
    newbuf.c_lflag &= ~(ECHO|ICANON);
    ioctl(0,TCSETA,&newbuf);

Input is indeed not echoed and on a per character basis. However, the
newlines printed to my terminal (using fd 1 via printf's) are not
translated; so I get a simple newline, instead of newline & carriage
return. I tried to solve this by specifying

    newbuf.c_oflag |= OPOST|ONLRET;

but setting this and reinspecting with TCGETA revealed that the OPOST
bit was cleared! (and of course the output again cluttered over the screen).
    Experimenting with ICANON and OPOST showed that whenever the ICANON
bit was off in the local flag, the OPOST bit was cleared in the output
flag, so it seems to be impossible to do post-processing whenever the
mode is non-canonical.
    I also tried ioctl's on filedescriptor 1, but they have the same
effect (setting fd 0 sets also fd 1 and vice versa, which is perhaps
reasonable if you consider they affect the same device).
    I also tried using TIOCSETP and sgttyb structs (they seem to be
defined in the include files if the symbol SYSTEM_FIVE is defined) but
to no avail.

I compile and link using the -Y flag of /bin/cc which should be
sufficient to select the correct parts of include files and the correct
libraries.  Any help is very much appreciated!


                                Leo.

asmodeus@tree.UUCP (Jonathan Ballard) (10/06/88)

In article <829@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:
> 
> When porting a database application to Ultrix (Oracle dbms), I found
> out we had to use System V libraries (grrr 8-), since the Oracle
> libraries depended upon them. I don't want to discuss whether Oracle's
...  [stuff deleted]
> The problem:
> I want to read characters from a terminal, without echo and not having
> to wait for a newline. After each character I might do some processing,
> based on the character read. The old solution did something along the
...[more stuff deleted]

This code here is what I used also...

> 
> #include <sys/termio.h>
> 
>     struct termio newbuf, oldbuf; /* oldbuf needed for restoring afterwards */
> 
>     ioctl(0,TCGETA,&oldbuf);
>     newbuf = oldbuf;
>     newbuf.c_lflag &= ~(ECHO|ICANON);
>     ioctl(0,TCSETA,&newbuf);
> 


Have you triing using a switch() to post process what you need?  This  is
what i use.   If your just looking for a return you could do something
like...
 
	c=getchar();
	switch(c) {
		case RETURN : 	printf("\n");
				break;
		default : 	printf(c);
				break;
	}


RETURN could be the real value or '\n' if you want to go by how the
compiler would implement it.
This is how I get the terminal set...

#include <termio.h>   /* include needed */

struct termio  save, term; /* global varibles used */


	/* check and see if terminal */
	if ( ioctl (0, TCGETA, &term) == -1 ) {
		fprintf (stderr, "standard input not a tty\n");
		exit (1);
	}
	/* set terminal for one-char input */
	save = term;
	term.c_lflag &= ~ICANON;  /* single key input ready */
	term.c_lflag &= ~ECHO;	  /* turn off echo */
	term.c_cc[VMIN] = 1;	/* minimal of one key for input being ready */
	term.c_cc[VTIME] = 0;   /*  turn off time-out */
	ioctl (0, TCSETA, &term);


	/* to reset old tty state */
	ioctl (0, TCSETA, &save);

That seems to be the usual way other programmers use too... 
Well.. hope it helps...
-- 
         _
   |    | \	 	UUCP e-mail:  ..!{csusac,pacbell}!sactoh0!jhballar
   |    |-< 	    		      ..!{csusac,pacbell}!sactoh0!tree!asmodeus
|__|on  |_/allard  		      ..!csusac!tree!asmodeus