THE LAST WORD (HOPEFULLY) ON THE GREAT IOCTL DEBATE!

BostonU SysMgr root%bostonu.csnet at csnet-relay.arpa
Tue Aug 13 00:59:03 AEST 1985


Ok, the only thing I am sicker of than changing between SYSV and 4.2
terminal ioctl()s is listening to this endless debate about them (and
all the confusion.)

In a bold attempt to lay this to rest I have written a small package
which provides a simple, portable call interface to what most people
*really* want, either CBREAK (with all control chars other than ^S/^Q
returned whole) or COOKED mode, with a few options for the picky.

Now, the design decisions may not be perfect (as you read it you start
to notice how many details are involved) and there are likely a few bugs
(I just fixed a couple before sending.) I am therefore sending it to
this list to let those that prefer coding to whining to work out the
bugs and add missing features, especially from other variants I don't
have to test this on (eg. Xenix, real V7, SYSIII etc.)

It seems to compile and run (I include a little test program) without a
hitch here on 4.2bsd (VAX) and SYSV (3B5 and PC7300.) My SUN is down but
it will probably work on that also.

As I say in the docs, I realize the overlap with curses but this is
intended to 'do one thing well', be simple to use and, due to it's
complete source availability, provide a wide range of portability for
this common function.

Have fun. Please mail me opinions, results, portings, bug-fixes,
extensions etc. so I can collate the whole mess and eventually give it
away to everyone (or tell me if you think this is a waste of time!)

P.S. Sorry about such a long posting, but it's probably shorter than
what you have heard already about the relative merits of TCGETA vs
TIOCGETP :-)

	-Barry Shein, Boston University

ARPA:	bzs%bu-cs at csnet-relay	CSNET: bzs at csnet-relay
UUCP:	...harvard!bu-cs!bzs

---------------shar file follows---------------------
#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	ttyset.3
#	Makefile
#	ttyconf.h
#	ttyset.h
#	ttyset.c
#	octpus.c
# This archive created: Sun Aug 11 21:37:47 1985
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: over-writing existing file "'README'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'README'
X
XTo make the TTYSET routines edit the file ttyconf.h to reflect the
Xsystem you plan to use this on.
X
XTyping just
X		make
X
Xbuilds ttyset.o. Put ttyset.h someplace where users can find it, they
Xneed the #defines from that file.
X
XFor a sample program to test on your system try:
X
X	make octpus
X
Xwhich when run will echo each character typed in a printable, octal and
Xhex format (exit with ^Z.) Derived from a program of the same name on
Xthe ITS system.
X
XThis software currently is known to work on the following systems:
X
X	Software	Hardware
X	UNIX/4.2bsd	VAX
X	UNIX/SysV	3B5, PC7300 (AT&T UNIX/PC)
X
XPlease add to this list as you either prove out this software or
Xadd new systems. Send all modifications so they may be collated to:
X
XARPA:
X	bzs%bu-cs at csnet-relay
XCSNET:
X	bzs at bu-cs
XUUCP:
X	...!harvard!bu-cs!bzs
XU.S. Mail
X	Barry Shein
X	Academic Computing Center
X	Boston University
X	111 Cummington Street
X	Boston, MA 02215
PORTABILITY_NOW
chmod +x 'README'
if test -f 'ttyset.3'
then
	echo shar: over-writing existing file "'ttyset.3'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'ttyset.3'
X.TH TTYSET 3 "11 August 1985"
X.SH NAME
Xttyset,ttyinit,ttyreset \- set common terminal parameters
X.SH
X.B #include <ttyset.h>
X.PP
X.SM
X.B ttyset(fd,flags)
X.br
X.B int fd ;
X.br
X.B unsigned int flags ;
X.PP
X.SM
X.B ttyinit(fd)
X.br
X.B int fd ;
X.PP
X.SM
X.B ttyreset(fd)
X.br
X.B int fd ;
X.SH DESCRIPTION
XThese routines are intended to allow the setting of common terminal
Xmodes for full screen applications in a system independant way.
XUnfortunately, due to historical precedents, there are at least
Xtwo major and incompatible interfaces to the UNIX terminal driver
X(currently, Berkeley derived 4.x and AT&T derived SYSTEM V.)
XAdditionally, the intention of this package is to provide a portable
Xsolution to any system that supports full-duplex terminal interfaces
Xwith reasonable control over common parameters.
X.PP
X.I Ttyset
Xmodifies the terminal specified by the file descriptor
X.I fd
Xaccording to bits set in the
X.I flags
Xfield.
X.PP
X.I Flags
Xconsists of the logical
X.B or
Xof the following symbolic definitions (from
X.I ttyset.h):
X.TP 5
X.B TTYCHAR
Xcharacter at time input
X.ns
X.TP 5
X.B TTYLINE
Xline buffered input
X.ns
X.TP 5
X.B TTYECHO
Xenable or disable echoing
X.ns
X.TP 5
X.B TTYCR
Xenable or disable CR/NL interpretations
X.ns
X.TP 5
X.B TTYSIG
Xenable or disable interrupt characters
X.ns
X.TP 5
X.B TTYTABX
Xenable or disable tab expansion
X.PP
XIt is an error to request both
X.B TTYCHAR
Xand
X.B TTYLINE
Xin the same call, all other flags may be
Xfreely mixed.
X.PP
XBy
X.I enabling
Xwe always mean resetting to
Xtheir original values when
X.B ttyinit
Xwas called.
X.PP
XUnder no circumstances will either flow control (typically ^S/^Q)
Xor parity bit processing be affected by any of these calls.
X.PP
X.B Ttyinit
Xis used to set up various data structures used internally by
Xthe other routines.
X.B Ttyset
Xwill automatically call this routine if it has not yet been called
Xso it is usually unnecessary to explicitly use this.
X.PP
X.B Ttyreset
Xreturns the terminal specified by
X.I fd
Xto the state it was in when
X.B ttyinit
Xwas called (either explicitly or via a call to
X.B ttyset.)
X.SH "SEE ALSO"
Xtty(4) on Berkeley systems or termio(7) on SystemV, curses(3X),
Xtermcap, terminfo
X.SH DIAGNOSTICS
XAll routines return -1 on error, 0 otherwise. Errors are almost
Xalways the result of using an
X.I fd
Xwhich is not referring to a valid terminal device. Also,
X.B ttyreset
Xwill return an error if
X.B ttyinit was never called.
X.SH BUGS
X.PP
XThere should probably be a way to return the current terminal settings
Xin a system independant manner. Unfortunately, the above flags often
Xset more than one internal parameter so there is no easy result if
Xit finds only some if these parameters set. Preferably, the program
Xwill access the terminal driver only through this package and
Xavoid such dependancies.
X.PP
XBeware that disabling
X.B TTYTABX
Xon UNIX systems also disables
Xany tab delays the user may have
Xrequested.
X.PP
XThe particular settings that result on a system are only an approximation
Xbased on experience of what
X.I typical
Xfull screen packages such as editors or games use. More exotic
Xprograms such as network utilities which require control of the
Xparity bit and other features of the terminal drivers will probably
Xhave to write their own interfaces.
X.PP
XThis package in some ways overlaps features of the curses package.
XThe intention was to set the design goals to be much simpler and to
Xprovide a package which makes the source available to
X.I all
Xusers. Hopefully this will encourage the porting of this package
Xto many different
X.B C
Xenvironments and its inclusion in future software.
XHowever,
X.I curses
Xis an excellent package and should probably be consulted first if
Xyou are writing full screen software primarily intended for
XUNIX systems.
X.SH EXAMPLE
X.PP
XThe following program will read each character as it is typed
Xand print out its octal equivalent (on an ASCII system):
X.sp
X	#include <stdio.h>
X.br
X	#include <ttyset.h>
X.br
X	main()
X.br
X	{
X.br
X		int c ;
X.br
X		if(ttyset(fileno(stdin),TTYCHAR) < 0) {
X.br
X			perror("ttyset") ;
X.br
X			exit(1) ;
X.br
X		}
X.br
X		printf("Type ^C to exit\\r\\n") ;
X.br
X		while((c = getchar() & 0177) != ('C' & 077))
X.br
X			printf("0%o\\r\\n",c) ;
X.br
X		ttyreset(fileno(stdin)) ;
X.br
X		exit(0) ;
X.br
X	}
X.sp
X.SH AUTHOR
XBarry Shein, Boston University
PORTABILITY_NOW
chmod +x 'ttyset.3'
if test -f 'Makefile'
then
	echo shar: over-writing existing file "'Makefile'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'Makefile'
XCFLAGS= -O
XLDFLAGS=
XFILES= README ttyset.3 Makefile ttyconf.h ttyset.h ttyset.c octpus.c
Xttyset.o:	ttyset.c ttyset.h ttyconf.h
X		cc -c ${CFLAGS} ttyset.c
Xoctpus:		octpus.o ttyset.o
X		cc octpus.o ttyset.o -o octpus
Xoctpus.c:	octpus.c ttyset.h
X		cc -c ${CFLAGS} octpus.c
Xshar.out:	${FILES}
X		shar -d PORTABILITY_NOW -p X ${FILES} > shar.out
PORTABILITY_NOW
chmod +x 'Makefile'
if test -f 'ttyconf.h'
then
	echo shar: over-writing existing file "'ttyconf.h'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'ttyconf.h'
X/*
X *	This must be the first definition. One and only one
X *	should be set to 1, the others 0
X *	#if was used throughout the modules so if your
X *	system is the same as a defined system feel free to add
X *	a ' | MYSYSTEM' to the end of any pertinent #if's.
X *	The goal is to free the user from these definitions for
X *	most common terminal settings.
X */
X#define BSD42	1
X#define SYSV	0
X#define PC7300	0
X/*
X *	Now we can properly do our #includes:
X */
X
X#if SYSV | PC7300
X#include <termio.h>
X#endif
X
X#if BSD42
X#include <sgtty.h>
X#endif
X
X/*
X *	In several places in the code we copy a structure
X *	define this macro as best for your system (some C
X *	compilers I assume still do not properly do structure
X *	assignments.) This is always called with:
X *	STRCTCP(target,source,sizeof(target))
X *	so, for examply, you could re-define with something
X *	like a bcopy() or memcpy() routine rather than assignment
X *	    eg:
X *		#define STRCTCP(t,s,sz)	bcopy(&s,&t,sz)
X */
X#define	STRCTCP(t,s,sz)	t = s
PORTABILITY_NOW
chmod +x 'ttyconf.h'
if test -f 'ttyset.h'
then
	echo shar: over-writing existing file "'ttyset.h'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'ttyset.h'
X/*
X *	Definitions for system independant access
X *	to terminal setup options
X */
X
X/*
X *	flags field for ttyset(fd,flags) (OR'd together)
X */
X	/* note: it is meaningless to set these both at once!*/
X#define TTYCHAR	0001	/* Character at a time		*/
X#define TTYLINE 0002	/* Line buffered		*/
X#define TTYECHO	0004	/* Turn on (off) echoing	*/
X#define TTYCR	0010	/* Turn on (off) CR/NL mods	*/
X#define TTYSIG	0020	/* Turn on (off) interrupts	*/
X#define TTYTABX	0040	/* Turn on (off) tab expansion	*/
PORTABILITY_NOW
chmod +x 'ttyset.h'
if test -f 'ttyset.c'
then
	echo shar: over-writing existing file "'ttyset.c'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'ttyset.c'
X/*
X *	TTYSET
X *
X *	A small set of routines for manipulating terminals in a
X *	reasonably system independant way. We assume most people
X *	just want to go into and out of character at a time mode
X *	possibly setting a few little things (like disabling signals
X *	and echo.) These functions will allow software to do this.
X *	I realize that the curses packages is meant to do about the
X *	the same thing but this allows a little finer control and
X *	the sources are free so hopefully it will accrete various
X *	new terminal driver needs for many systems.
X *
X *	Copyright (c) 1985 Barry Shein
X *
X *	Permission is hereby granted to distribute the original and/or
X *	modfied versions of this program so long as this copyright
X *	notice is not removed. This may be included in any software
X *	free of monetary charge to either the vendor or the user
X *	as source or binary, preferably source. This notice applies
X *	to both the C and header (.h) files.
X *
X *	ROUTINES:
X *
X *	ttyinit()	initializes data structures, will be called
X *			automatically by ttyset() or can be called
X *			by the user program. MUST be called before ttyreset()
X *	ttyreset()	after a ttyinit() restores to original terminal state
X *	ttyset()	set terminal modes
X */
X#include "ttyconf.h"	/* for compiling this (system dependent defns)	*/
X#include "ttyset.h"	/* for users (system independant defns)		*/
X
X#if SYSV | PC7300
Xstatic struct termio oldtty, newtty ;
X#endif
X
X#if BSD42
Xstatic struct sgttyb oldtty, newtty ;
Xstatic struct tchars oldtc, newtc ;
Xstruct ltchars  oldltc, newltc ;
Xstatic int ldisc ;
X#endif
X
Xstatic int didinit = 0 ;	/* need to call ttyinit */
X
Xttyinit(fd) int fd ;
X{
X#if SYSV | PC7300
X	if(ioctl(fd,TCGETA,&oldtty) < 0) return(-1) ;
X#endif
X#if BSD42
X	if(ioctl(fd,TIOCGETP,&oldtty) < 0) return(-1) ;
X	if((ioctl(fd,TIOCGETC,&oldtc) < 0) || (ioctl(fd,TIOCGETD,&ldisc) < 0))
X		return(-1) ;
X	STRCTCP(newtc,oldtc,sizeof(newtc)) ;
X	/*
X	 *	ltchars only meaningful under new tty discipline
X	 */
X	if(ldisc == NTTYDISC) {
X		if(ioctl(fd,TIOCGLTC,&oldltc) < 0) return(-1) ;
X		STRCTCP(newltc,oldltc,sizeof(newtc)) ;
X	}
X#endif
X	STRCTCP(newtty,oldtty,sizeof(newtty)) ;
X	didinit = 1 ;
X	return(0) ;
X}
Xttyreset(fd) int fd ;
X{
X	if(!didinit)
X		return(-1) ;
X	STRCTCP(newtty,oldtty,sizeof(newtty)) ;
X#if SYSV | PC7300
X	return(ioctl(fd,TCSETA,&oldtty)) ;
X#endif
X#if BSD42
X	if(ioctl(fd,TIOCSETP,&oldtty) < 0)
X		return(-1) ;
X	STRCTCP(newtc,oldtc,sizeof(newtc)) ;
X	if(ioctl(fd,TIOCSETC,&oldtc) < 0)
X		return(-1) ;
X	if(ldisc == NTTYDISC)
X	{
X		STRCTCP(newltc,oldltc,sizeof(newtc)) ;
X		if(ioctl(fd,TIOCSLTC,&oldltc) < 0) return(-1) ;
X	}
X	return(0) ;
X#endif
X}
X
X/*
X *	ttyset(fd,flags)
X *
X *	set up terminal options in a system independant way
X *
X *		fd		a terminal file descriptor
X *	OR'd together flags:
X *		TTYCHAR		Character at a time mode
X *		TTYLINE		line buffered mode (cooked!)
X *		TTYECHO		if 0 disables echoing, else enables
X *		TTYCR		if 0 disables mapping of INPUT from
X *				CR->LF (newline)
X *				and mapping of OUTPUT from
X *				CR->CR/LF
X *				ELSE reset to old
X *		TTYSIG		if 0 disables things like INTR (often ^C
X *				or DEL) from interrupting a program else
X *				enables.
X *		TTYTABX		if 0 disable tab expansion else reset to old
X */
Xttyset(fd,flags) int fd ; unsigned int flags ;
X{
X	/*
X	 *	If necessary initialize things
X	 */
X	if(!didinit)
X		if(ttyinit(fd) < 0) return(-1) ;
X#if SYSV | PC7300
X	/*
X	 *	Enable or reset character at a time reading
X	 */
X	if(flags & TTYCHAR) {
X		newtty.c_lflag &= ~ICANON ;
X		/*
X		 *	These two parameters set a minimum read
X	 	 *	of one character and an infinite timeout on reads.
X	 	 */
X		newtty.c_cc[VMIN] = 1 ;
X		newtty.c_cc[VTIME] = 0 ;
X	}
X	else if(flags & TTYLINE) {
X		newtty.c_lflag |= (oldtty.c_lflag & ICANON) ;
X		newtty.c_cc[VMIN] = oldtty.c_cc[VMIN] ;
X		newtty.c_cc[VTIME] = oldtty.c_cc[VTIME] ;
X	}
X	/*
X	 *	Enable or reset echo
X	 */
X	if(flags & TTYECHO)
X		newtty.c_lflag |= (oldtty.c_lflag & (ECHO|ECHONL)) ;
X	else
X		newtty.c_lflag &= ~(ECHO|ECHONL) ;
X	/*
X	 *	Enable or reset CR interpretation (both input and output)
X	 */
X	if(flags & TTYCR) {
X		newtty.c_oflag |= (oldtty.c_oflag & (ONLCR|OCRNL|ONLRET)) ;
X		newtty.c_iflag |= (oldtty.c_iflag & (INLCR|ICRNL|IGNCR)) ;
X	}
X	else {
X		newtty.c_oflag &= ~(ONLCR|OCRNL|ONLRET) ;
X		newtty.c_iflag &= ~(ICRNL|INLCR|IGNCR) ;
X	}
X	/*
X	 *	Enable or reset interrupt character processing
X	 */
X	if(flags & TTYSIG)
X		newtty.c_lflag |= (oldtty.c_lflag & ISIG) ;
X	else
X		newtty.c_lflag &= ~ISIG ;
X	/*
X	 *	Enable or reset tab expansion on output
X	 */
X	if(flags & TTYTABX)
X		newtty.c_oflag |= (oldtty.c_oflag & TABDLY) ;
X	else
X		newtty.c_oflag &= ~TABDLY ;	/* watch delays here! */
X	return(ioctl(fd,TCSETA,&newtty)) ;
X#endif
X
X#if BSD42
X	if(flags & TTYCHAR)
X		newtty.sg_flags |= CBREAK ;	/* clear RAW?? */
X	else
X		newtty.sg_flags &= ~(oldtty.sg_flags & CBREAK) ;
X	if(flags & TTYECHO)
X		newtty.sg_flags |= (oldtty.sg_flags & ECHO) ;
X	else
X		newtty.sg_flags &= ~ECHO ;
X	if(flags & TTYCR)
X		newtty.sg_flags |= (oldtty.sg_flags & CRMOD) ;
X	else
X		newtty.sg_flags &= ~CRMOD ;
X	if(flags & TTYSIG) {
X		STRCTCP(newtc,oldtc,sizeof(newtc)) ;
X		if(ioctl(fd,TIOCSETC,&newtc) < 0) goto lose ;
X		if(ldisc == NTTYDISC)
X		{
X			STRCTCP(newltc,oldltc,sizeof(newltc)) ;
X			if(ioctl(fd,TIOCSLTC,&newtc) <0) goto lose ;
X		}
X	}
X	else {
X		newtc.t_intrc =
X		newtc.t_quitc = (char) -1 ;
X		if(ioctl(fd,TIOCSETC,&newtc) < 0) goto lose ;
X		if(ldisc == NTTYDISC)
X		{
X			newltc.t_suspc =
X			newltc.t_dsuspc =
X			newltc.t_flushc =
X			newltc.t_lnextc = (char) -1 ;
X			if(ioctl(fd,TIOCSLTC,&newltc) < 0)
X				goto lose ;
X		}
X	}
X	if(flags & TTYTABX)
X		newtty.sg_flags |= (oldtty.sg_flags & XTABS) ;
X	else
X		newtty.sg_flags &= ~XTABS ;	/* watch delays! */
X	if(ioctl(fd,TIOCSETP,&newtty) < 0)
X		goto lose ;
X	return(0) ;
X/*
X *	We do this here because one or more settings may have already
X *	been done before the failure (tho not at all likely.) I assume
X *	that the calling program, on receiving an error return, will
X *	probably just exit so we better try to reset parameters.
X *	(note, this is not necessary with the TERMIO style interface
X *	as only one call is done.
X */
Xlose:
X	(void) ttyreset(fd) ;	/* ignore return value */
X	return(-1) ;
X#endif
X}
PORTABILITY_NOW
chmod +x 'ttyset.c'
if test -f 'octpus.c'
then
	echo shar: over-writing existing file "'octpus.c'"
fi
sed 's/^X//' << \PORTABILITY_NOW > 'octpus.c'
X/*
X *	Test program for TTYSET routines
X *	This is a version of the old ITS OCTPUS program
X *	which simply goes into character at a time mode
X *	and tells you what each key on your terminal is
X *	transmitting. Typing ^Z offers to exit.
X */
X#include <stdio.h>
X#include "ttyset.h"
Xmain(argc,argv) int argc ; char **argv ;
X{
X	int fd, c ;
X	char buf[100] ;
X
X	if(!isatty(fd = fileno(stdin)))
X	{
X		fprintf(stderr,"Sorry, only useful if stdin is a terminal\n") ;
X		exit(1) ;
X	}
X	if(ttyset(fd,TTYCHAR) < 0)
X	{
X		perror(argv[0]) ;
X		exit(1) ;
X	}
X	printf("Hit any key for a display of what it transmits\r\n") ;
X	printf("Note: ^S/^Q will probably not transmit!\r\n") ;
X	printf("^Z to quit\r\n\n") ;
X	printf("%-8s%8s%8s\r\n","CHAR","OCTAL","HEX") ;
X	for(;;)
X	{
X		/* ASCII! */
X		c = getchar() & 0177 ;	/* we only consider low 7-bits */
X		if((c >= ' ') && (c < 0177))
X			sprintf(buf,"%c ",c) ;
X		else if(c < ' ')
X			sprintf(buf,"^%c",c|0100) ;
X		else if(c == 0177)
X			sprintf(buf,"DEL") ;
X		printf("%-8s",buf) ;
X		sprintf(buf,"%04o",c) ;
X		printf("%8s",buf) ;
X		sprintf(buf,"0x%02x",c) ;
X		printf("%8s",buf) ;
X		printf("\r\n") ;
X		if(c == ('Z' & 077))
X		{
X			printf("---->Do you really want to quit? ") ;
X			fflush(stdout) ;
X			c = getchar() & 0177 ;
X			printf("%c\r\n",c) ;
X			if((c == 'y') || (c == 'Y'))
X				break ;
X		}
X	}
X	ttyreset(fd) ;
X	exit(0) ;
X}
X
PORTABILITY_NOW
chmod +x 'octpus.c'
#	End of shell archive
exit 0



More information about the Comp.unix.wizards mailing list