[comp.sources.unix] v12i025: Query terminal for its type

rsalz@uunet.UU.NET (Rich Salz) (10/20/87)

Submitted-by: Craig Bishop <charlie.oz.au!craig>
Posting-number: Volume 12, Issue 25
Archive-name: qterm.alt

[  This program sends an Escape sequence to your terminal, and interprets
   the result to see what kind of terminal you have.  It's table-driven,
   so adding new terminals is easy.  It is a rewrite of the qterm
   program published in Volume 10.  --r$  ]

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
# This archive contains:
# README Makefile qtermtab gstty.h qterm.h qterm.c query.c table.c

echo x - README
cat > "README" << '//E*O*F README//'
This is a rewrite of the qterm program which came across the net in
comp.sources.unix the title was v10i072: Query terminal for its type.
I decided to use the program before looking at the code.  Eventually
I decided to rewrite it. 

It is completely compatible with the original so if you are already
using it I suggest you change to this one. There are couple more
flags added to the program which are useful when testing a terminals
responses to the various standard query sequences.

Qterm is particularly valuable in port selector and terminal server 
environments. Most terminals can respond to the standard ANSI query
sequences, others which cannot will often do answerback.

If you find any bugs please report them to me.

Craig Bishop 		ACSNET:	craig@charlie.oz
			ARPA:	craig%charlie.oz.au@uunet.uu.net
			UUCP:	...!uunet!munnari!charlie.oz!craig
//E*O*F README//

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
#
#	Makefile for qterm.
#	
#	If this is a USG system then add the flag -DUSG to the
#	CFLAGS variable.
#

DESTDIR	      =	/usr/local/bin
CFLAGS	      = -O -DTABLEFILE=\"$(TABLEDIR)/$(TABLE)\"
SOURCES	      =	qterm.c query.c table.c
OBJECTS	      = qterm.o query.o table.o
HDRS	      =	gstty.h qterm.h
BINARY	      = qterm
LIBS	      =	
MAN	      =	qterm.1
MANDIR	      =	/usr/local/man/man1
TABLE	      =	qtermtab
TABLEDIR      = /usr/local/lib

.DEFAULT:;	co -q $<

all:		$(BINARY)

$(BINARY):	$(OBJECTS)
		cc -o $(BINARY) $(OBJECTS) $(LIBS)

$(OBJECTS):	$(HDRS)

install:;	cp $(BINARY) $(DESTDIR)
		chown bin $(DESTDIR)/$(BINARY)
		chmod 751 $(DESTDIR)/$(BINARY)
		cp $(MAN) $(MANDIR)
		chown man $(MANDIR)/$(MAN)
		chmod 644 $(MANDIR)/$(MAN)

install_table:;	cp $(TABLE) $(TABLEDIR)
		chown lib $(TABLEDIR)/$(TABLE)
		chmod 644 $(TABLEDIR)/$(TABLE)

clean:;		rm -f $(BINARY) $(OBJECTS)

print:;		pr Makefile $(HDRS) $(SOURCES) | lpr
//E*O*F Makefile//

echo x - qtermtab
cat > "qtermtab" << '//E*O*F qtermtab//'
#
#SendStr ReceiveStr		TermName	FullTermName
#
^[Z	^[iBO         		h29	 	Zenith z29 in zenith mode
^[Z	^[/K          		h29	 	Zenith z29 in zenith mode
^[Z	^[[?1;0c     		vt100    	Base vt100
^[Z	^[[?1;1c     		vt100   	vt100 with STP
^[Z	^[[?1;2c     		vt100   	ANSI/VT100 Clone
^[Z	^[[?1;3c     		vt100    	vt100 with AVO and STP
^[Z	^[[?1;4c     		vt100    	vt100 with GPO
^[Z	^[[?1;5c     		vt100    	vt100 with GPO and STP
^[Z	^[[?1;6c     		vt100    	vt100 with GPO and AVO
^[Z	^[[?1;7c     		vt100    	vt100 with GPO, STP, and AVO
^[Z	^[[?6c        		vt100  	 	Generic vt100
^[Z	^[[?8c        		vt100    	TeleVideo 970
^[Z	^[[0n         		vt100    	AT&T Unix PC 7300
^[Z	^[[?l;0c     		vt100    	AT&T Unix PC 7300
^[Z	^[[?12c       		vt100    	Concept from Pro 350/UNIX
^[Z	^[[?;c        		vt100    	Concept From Pro 350/UNIX
^[Z	^[[=1;1c     		avt-4p-s 	Concept with 4 pages memory
^[Z	^[[=1;2c     		avt-8p-s 	Concept with 8 pages memory
^[Z	^[/Z          		vt52	 	Generic vt52
^[Z	^[[?10c       		la120	 	DEC Writer III
^[Z	^[[?1;11c    		cit101e  	CIE CIT-101 Enhanced w/Graphics
#^[Z	^[[?1;11c    		xt100+   	Northern Tech LANPARSCOPE
^[Z	^[[?12;7;0;102c 	vt125	 	DEC Pro 350 in vt125 mode
^[Z	^[[?62;1;2;6;7;8;9c 	vt220   	DEC VT220
^[Z	^[[?62;1;4;6;7;8;9;15c	vt200-sb	Microvax II VMS
^[Z	^[[62;1;2;6;8c		f220     	Freedom 220 DEC clone
^[Z	^[[?63;1;2;6;7;8c 	tvi9220  	TeleVideo 9220
//E*O*F qtermtab//

echo x - gstty.h
cat > "gstty.h" << '//E*O*F gstty.h//'
/*
**	gstty.h - defines and declarations for the getting and setting
**		  of terminal parameters on BSD and USG systems.
**
**	Author: Craig Bishop
**		loosly based on original program by Michael Cooper.
**
**	$Header: gstty.h,v 1.1 87/09/30 15:25:46 craig Exp $
*/

#include	<sys/ioctl.h>

#ifdef	USG
#include	<termio.h>

struct	termio	otty;
struct	termio	ntty;

#define	setterm()	(\
				(void)ioctl(0, TCGETA, &otty),\
				ntty = otty,\
				ntty.c_lflag &= ~ICANON,\
				ntty.c_lflag &= ~ECHO,\
				ntty.c_cc[VMIN] = 1,\
				ntty.c_cc[VTIME] = 0,\
				(void)ioctl(0, TCSETAF, &ntty)\
			)

#define	fixterm()	(void)ioctl(0, TCSETAF, &otty)
#else
struct	sgttyb	otty;
struct	sgttyb	ntty;

#define	setterm()	(\
				(void)ioctl(0, TIOCGETP, &otty),\
				ntty = otty,\
				ntty.sg_flags |= CBREAK,\
				ntty.sg_flags &= ~ECHO,\
				(void)ioctl(0, TIOCSETP, &ntty)\
			)

#define	fixterm()	(void)ioctl(0, TIOCSETP, &otty)
#endif
//E*O*F gstty.h//

echo x - qterm.h
cat > "qterm.h" << '//E*O*F qterm.h//'
/*
**	qterm.h - defines and declarations for the qterm modules.
**
**	Author: Craig Bishop
**		loosly based on original program by Michael Cooper.
**
**	$Header: qterm.h,v 1.1 87/09/30 15:25:52 craig Exp $
*/

/* defines for qterm */

#define	ABSEND		"\05"		/* Answerback string */
#define	ALTSEND		"\033[c"	/* Alternative string */
#define DFLTSEND	"\033Z"		/* Default string */
#define	CMASK		0177		/* Character mask */
#define	ESC		'\033'		/* Escape in octal */
#define	MAXTERMS	100		/* Maximum # of terminals for qterm */
#define	STRFILE		".qterm"	/* File containing terminal strings */

/*
** This is  the number of seconds for alarm timeouts. Some slower systems
** may need to increase this value to 2 or maybe 3 seconds. This value
** effects how long qterm will run, because for each query string sent
** the program must wait at least this long.
*/

#define	WAIT		1		

#define	TRUE		1
#define	FALSE		0

#define NULLSTR		(char *) NULL
#define	NULLQT		(struct qt *) NULL
#define	STREQUAL	(0)

#define FIELDSIZ	30 
#define	FULLSIZ		80

/* define the query terminal structure */

struct	qt
{
	char	sendstr[FIELDSIZ];	/* String to send to terminal */
	char	recvstr[FIELDSIZ];	/* String expected in response */
	char	termname[FIELDSIZ];	/* Terminal name */
	char	fullname[FULLSIZ];	/* Full terminal name & description */
};

/* declare externals */

extern	int		Fflag;
extern	int		fflag;
extern	int		qflag;
extern	int		sflag;

extern	char		querystr[];
extern	char		recvbuf[];
extern	char		*name;

extern	struct	qt	*termtab[];

/* declare procedures */

int		mktable(),
		readtabfile();

char		getch();

char		*decode(),
		*fixctl(),
		*getenv(),
		*lalloc(),
		*listen(),
		*malloc();

void		catchint(),
		dumb(),
		prinfo(),
		wakeup();

FILE		*fopen();

struct	qt	*compare(),
		*dotab();
//E*O*F qterm.h//

echo x - qterm.c
cat > "qterm.c" << '//E*O*F qterm.c//'
/*
**	qterm.c - Qterm is used to query a terminal to determine the name
**		  of the terminal. This is done by sending a equiry string
**		  to the terminal and reading in a response.
**
**	Author: Craig Bishop
**		loosly based on original program by Michael Cooper.
*/

static	char	rcsid[] = "$Header: qterm.c,v 1.1 87/09/30 15:25:55 craig Exp $";

#include	<stdio.h>
#include	<signal.h>

#include	"gstty.h"
#include	"qterm.h"

/* declare flags */

int	fflag = FALSE;		/* use user's own .qterm file */
int	Fflag = FALSE;		/* same as above, but don't add our own table */
int	qflag = FALSE;		/* quiet mode */
int	sflag = FALSE;		/* print strings */

/* declare global variables */

char		decodebuf[BUFSIZ];
char		querystr[FIELDSIZ];
char		recvbuf[FIELDSIZ];
char		*name;
char		usage[] = "usage: %s [ -a ] [ -d ] [ -e ] [ -f ] [ -F ] [ -q ] [ -s ]\n";

struct	qt	*termtab[MAXTERMS];


int
main(argc, argv)
int	argc;
char	*argv[];
{
	register int	i;
	register int	j;
	struct	 qt	*qtp;

	name = argv[0];

	for ( i = 1; i < argc; i++ )
	{
		if ( argv[i][0] != '-' )
		{
			(void)fprintf(stderr, usage, name);
			return 1;
		}

		for ( j = 1; argv[i][j] != NULL; j++ )
			switch ( argv[i][j] )
			{
			case 'a':
				(void)strcpy(querystr, ALTSEND);
				break;
			case 'd':
				(void)strcpy(querystr, DFLTSEND);
				break;
			case 'e':
				(void)strcpy(querystr, ABSEND);
				break;
			case 'f':
				fflag = TRUE;
				break;
			case 'F':
				Fflag = TRUE;
				break;
			case 'q':
				qflag = TRUE;
				break;
			case 's':
				sflag = TRUE;
				break;
			default:
				(void)fprintf(stderr, usage, name);
				return 1;
			}
	}

	if ( mktable() == FALSE )
		return 1;

	if ( !isatty(0) ) 
	{
		(void)fprintf(stderr, "%s: Not a tty.\n", name);
		return 1;
	}

	setterm();
	(void)signal(SIGHUP, catchint);
	(void)signal(SIGINT, catchint);
	(void)signal(SIGQUIT, catchint);
	(void)setbuf(stdout, 0);
	(void)setbuf(stderr, 0);
	qtp = dotab();
	fixterm();

	if ( qtp != NULLQT )
		prinfo(qtp);
	else
		dumb();

	return 0;
}


void
catchint()
{
	fixterm();
	exit(1);
}


void
prinfo(ttent)
struct	qt	*ttent;
{
	register int		len;
	register struct	qt	*qtp = ttent;

	if ( sflag )
	{
		len = strlen(recvbuf);

		(void)fprintf
		(
			stderr,
			"%s receives %d character%s: %s\n", 
			name,
			len,
			(len == 1) ? "" : "s",
			decode(recvbuf)
		);
	}

	if ( !qflag )
		if ( *qtp->fullname != NULL )
			(void)fprintf
			(
				stderr,
				"Terminal recognized as %s (%s)\n", 
				qtp->termname,
				qtp->fullname
			);
		else
			(void)fprintf
			(
				stderr,
				"Terminal recognized as %s\n", 
				qtp->termname
			);

	(void)puts(qtp->termname);
}


void
dumb()
{
	register int	len;

	if ( sflag )
	{
		len = strlen(recvbuf);

		(void)fprintf
		(
			stderr,
			"%s receives %d character%s",
			name,
			len,
			(len == 1) ? "" : "s"
		);
		
		if ( len )
			(void)fprintf(stderr, ": %s\n", decode(recvbuf));
		else
			(void)fputs(".\n", stderr);
	}

	if ( !qflag )
		(void)fprintf
		(
			stderr,
			"Terminal NOT recognized - defaults to \"dumb\".\n"
		);

	(void)puts("dumb");
}


char	*
decode(str)
char	*str;
{
	register char	*dbp = decodebuf;
	register char	*cp1 = str;
	register char	*cp2;
	char		tmp[10];

	for ( *dbp = NULL, cp2 = tmp; *cp1 != NULL; cp1++ )
	{
		if ( *cp1 == ESC )
		{
			(void)strcat(dbp, "<esc> ");
			continue;
		}

		if ( (*cp1 <= 33) || (*cp1 == 127) )
		{
			(void)sprintf(cp2, "\\%o ", *cp1);
			(void)strcat(dbp, cp2);
			continue;
		}

		(void)sprintf(cp2, "%c ", *cp1);
		(void)strcat(dbp, cp2);
	}

	return dbp;
}
//E*O*F qterm.c//

echo x - query.c
cat > "query.c" << '//E*O*F query.c//'
/*
**	query.c - send query strings to the terminal and listen for 
**		  the answering strings.
**
**	Author: Craig Bishop
**		loosely based on a the original program by Michael Cooper.
*/

static	char	rcsid[] = "$Header: query.c,v 1.1 87/09/30 15:25:58 craig Exp $";

#include	<stdio.h>
#include	<setjmp.h>
#include	<signal.h>

#include	"qterm.h"

/* declare global variables */

jmp_buf	env;


struct	qt	*
dotab()
{
	static	 int		firsttime = TRUE;
	register struct	qt	**qtpp = termtab;
	register struct qt	*lastqtp;
	struct	 qt		*term;

	(void) fflush(stdin);

	for ( ; *qtpp != NULLQT; lastqtp = *qtpp++ )
	{
		if
		(
			firsttime
			||
			strcmp((*qtpp)->sendstr, lastqtp->sendstr) != STREQUAL
		)
		{
			firsttime = FALSE;
			(void)fputs((*qtpp)->sendstr, stderr);

			if ( listen() == NULLSTR )
				continue;
		}
		else
			continue;

		if ( (term = compare(recvbuf, qtpp)) != NULLQT )
			return term;
	}

	return NULLQT;
}


char	*
listen()
{
	register int	i;

	if ( setjmp(env) )
	{
		(void)fflush(stdin);
		return recvbuf;
	}

	(void)signal(SIGALRM, wakeup);

	for ( i = 0; i < FIELDSIZ - 1; recvbuf[++i] = NULL )
	{
		(void)alarm(WAIT);
		recvbuf[i] = getch();
		(void)alarm(0);
	}

	(void)fflush(stdin);
	return recvbuf;
}


struct	qt	*
compare(str, ttstart)
char		*str;
struct	qt	**ttstart;
{
	register char		*cp1 = str;
	register char		*cp2 = (*ttstart)->sendstr;
	register struct	qt	**qtpp = ttstart;

	for ( ; *qtpp != NULLQT && strcmp(cp2, (*qtpp)->sendstr) == STREQUAL; qtpp++ )
		if ( strcmp(cp1, (*qtpp)->recvstr) == STREQUAL )
			return *qtpp;

	return NULLQT;
}


char
getch()
{
	char	c;

	(void)read(0, &c, 1);
	return (c & CMASK);
}


void
wakeup()
{
	longjmp(env, 1);
}
//E*O*F query.c//

echo x - table.c
cat > "table.c" << '//E*O*F table.c//'
/*
**	table.c - read in the terminal query table(s).
**
**	Author: Craig Bishop
**		loosely based on a the original program by Michael Cooper.
*/

static	char	rcsid[] = "$Header: table.c,v 1.1 87/09/30 15:26:01 craig Exp $";

#include	<stdio.h>
#include	<pwd.h>

#include	"qterm.h"

#define talloc(T)	(T*) lalloc(sizeof(T))

/* declare global variables */

char	fixbuf[FIELDSIZ + 1];

/* declare procedures */

struct	passwd	*getpwuid();


int
mktable()
{
	char		file[BUFSIZ];
	char		*home;
	struct	passwd	*pwd;

	if ( fflag || Fflag )
	{
		if ( (home = getenv("HOME")) == NULLSTR )
		{
			if ( (pwd = getpwuid(getuid())) == (struct passwd *) NULL )
			{
				(void)fprintf
				(
					stderr,
					"%s: Who the hell are you????\n",
					name
				);

				return FALSE;
			}

			home = pwd->pw_dir;
		}

		(void)sprintf(file, "%s/%s", home, STRFILE);
		
		if ( readtabfile(file) == FALSE )
			return FALSE;
	}

	if ( !Fflag )
		return readtabfile(TABLEFILE);

	return TRUE;
}


int
readtabfile(file)
char	*file;
{
	register int		line;
	register char		*cp;
	register struct qt	**qtpp = termtab;
	char			buf[BUFSIZ];
	FILE			*fp;

	if ( (fp = fopen(file, "r")) == (FILE *) NULL )
	{
		(void)fprintf
		(
			stderr,
			"%s: Cannot read input file %s.\n",
			name,
			file
		);

		return FALSE;
	}

	for
	(
		*qtpp = talloc(struct qt), line = 1, cp = buf;
		qtpp < &termtab[MAXTERMS - 1] && fgets(cp, BUFSIZ, fp);
		line++
	)
	{
		if ( *cp == '#' || *cp == '\n')
			continue;
		
		(void)sscanf
		(
			cp,
			"%s%s%s\t%[^\n]", 
			(*qtpp)->sendstr,
			(*qtpp)->recvstr,
			(*qtpp)->termname,
			(*qtpp)->fullname
		);

		if ( *(*qtpp)->sendstr == NULL )
			continue;

		if ( *(*qtpp)->recvstr == NULL || *(*qtpp)->termname == NULL )
		{
			(void)fprintf
			(
				stderr,
				"%s: Error parsing qterm file %s on line %d.\n",
				 name,
				 file,
				 line
			);

			(void)fclose(fp);
			return FALSE;
		}

		if ( querystr[0] != NULL )
			(void)strcpy((*qtpp)->sendstr, querystr);
		else
			(void)strcpy((*qtpp)->sendstr, fixctl((*qtpp)->sendstr));

		(void)strcpy((*qtpp)->recvstr, fixctl((*qtpp)->recvstr));
		*++qtpp = talloc(struct qt);
	}

	(void)fclose(fp);
	(void)free(*qtpp);
	*qtpp = NULLQT;

	if ( qtpp == &termtab[MAXTERMS - 1] )
	{
		(void)fprintf
		(
			stderr,
			"%s: Greater than maximum terminals of %d.",
			name,
			MAXTERMS
		);

		return FALSE;
	}

	return TRUE;
}


char	*
fixctl(str)
char	*str;
{
	register char	*cp1 = str;
	register char	*cp2 = fixbuf;

	for ( *cp2 = NULL; *cp1 != NULL; cp1++, cp2++ )
	{
		if ( *cp1 == '^')
			*cp2 = *++cp1 & 037;
		else
			*cp2 = *cp1;
	}

	*cp2 = NULL;
	return(fixbuf);
}


char *
lalloc(size)
int	size;
{
	register char	*cp;

	if ( (cp = malloc((unsigned)size)) == NULLSTR )
	{
		(void)fprintf
		(
			stderr,
			"%s: No memory for malloc(%d)\n",
			name,
			size
		);

		exit(1);
	}

	return cp;
}
//E*O*F table.c//

exit 0