[net.bugs.4bsd] tgetent

rlb (08/04/82)

tgetent() uses a global variable named "hopcount" to detect and reject
long tc= chains in the termcap file.  "hopcount" is assumed to be initially
zero, which it is, and then is incremented and compared to 32 each time
a tc= statement is encountered in termcap.

This causes a problem in programs that call tgetent several times
(notably Gosling's emacs on restarting from a pause) "hopcount"
is never reset to zero, ever.  Thus, if you have one tc= statement
in the capability for your terminal, on your 32nd restart into emacs,
the editor crashes with an "infinite tc= loop" error.  It's even worse
on our ADDS regent 40 terminals which have a tc= chain of about five.

The solution is to place the statement

	hopcount = 0;

near the beginning of tgetent() in termcap.c.

Note that if you use tset to define the TERMCAP environment variable,
this problem doesn't arise since the tc= chains are resolved by tset.

Bob Brown (rlb)
Purdue-CS (317)494-6530

karl@osu-eddie.UUCP (Karl Kleinpaste) (12/14/84)

While working with the  tenex  csh  distributed  around  Oct 1983, it became
obvious  that  the vb capability advertised by the man page is not  in  fact
available at all times. Specifically, it does not deal with any capabilities
in  a  personalized termcap file referenced by a TERMCAP env  variable.  The
problem is  that  termcap.c  in  libtermlib.a  has  a  bogus  check for what
constitutes  a  valid  file descriptor, and inadvertently  throws  away  the
reference to one's personalized termcap file.  This bug report is  for  both
4BSD and USG5.0, but I believe it applies to all libtermlibs.

Subject: tgetent() of libtermlib does not use private termcaps correctly.

Index: /usr/src/usr.lib/libtermlib/termcap.c 4.2BSD

Description:
	Routine tgetent() of libtermlib has an incorrect check for whether
	or not the open of a personalized termcap file (referenced by an
	environment variable TERMCAP) succeeds: it checks against 0 as an
	invalid descriptor, when in fact 0 is a perfectly reasonable (if
	somewhat unusual) file descriptor for use with an interactive
	program as something other than stdin. This may sometimes cause
	the reference to the personalized termcap file to be ignored, and
	/etc/termcap will be substituted.

Repeat-By:
	% vi ~/.termcap
	[Create a termcap entry for the current terminal from a similar
	entry in /etc/termcap.  Add a vb capability to the entry; anything
	will do, it doesn't matter what you use.  Make sure that the
	original /etc/termcap version of the termcap entry does not have a
	vb capability, or at least that it's a different vb.]
	% setenv TERMCAP ~/.termcap
	% csh				[Use tenex csh, not /bin/csh.]
	[Subshell starts up...next prompt is from that csh.]
	% ^[				[Hit an ESC.]
	[The csh will beep at you, using ^G, instead of the vb capability
	just added above in ~/.termcap, and will then erase the ^[ as it's
	supposed to.]

	The problem is that the open of ~/.termcap showed up as file
	descriptor 0, which caused tgetent() to ignore it.  (Csh moves
	stdin, stdout, and stderr away from 0, 1, and 2 frequently.)

Fix:
	The routine tgetent() must be fixed so that it checks for file
	descriptors less than zero as an "invalid" condition.  Diffs follow.

	*** termcap.c~	Thu Dec 13 18:24:33 1984
	--- termcap.c	Thu Dec 13 18:26:50 1984
	***************
	*** 44,50
	  	int tf;
	  
	  	tbuf = bp;
	! 	tf = 0;
	  #ifndef V6
	  	cp = getenv("TERMCAP");
	  	/*
	
	--- 44,50 -----
	  	int tf;
	  
	  	tbuf = bp;
	! 	tf = -1;	/* fixed initialization: used to be 0. */
	  #ifndef V6
	  	cp = getenv("TERMCAP");
	  	/*
	***************
	*** 66,72
	  		} else
	  			tf = open(cp, 0);
	  	}
	! 	if (tf==0)
	  		tf = open(E_TERMCAP, 0);
	  #else
	  	tf = open(E_TERMCAP, 0);
	
	--- 66,72 -----
	  		} else
	  			tf = open(cp, 0);
	  	}
	! 	if (tf < 0)		/* fixed test: used to be "tf == 0". */
	  		tf = open(E_TERMCAP, 0);
	  #else
	  	tf = open(E_TERMCAP, 0);

Workaround:
	For those who lack appropriate privileges to edit/recompile the
	source files for libtermlib, the following is a (generally) suitable
	fix for the problem:

	All occurrences of
		if (tgetent(term, area) < 0)
		{
			/* recover from no-termcap-file error condition. */
		}
	can be bracketed thusly:
		{
			int dummyfile = -1;
			dummyfile = open ("/dev/null", 0);
			if (tgetent (term, area) < 0)
			{
				/* recover from error as before. */
			}
			close (dummyfile);
		}

	This makes sure that file descriptor 0 is occupied by some open
	file, since Unix always assigns the lowest-available file descriptor
	when a new file is opened.

	Note that this will not always work.  Since it occupies another file
	descriptor, it may cause programs needing large numbers of files to
	reach the EMFILE "too many open files" conditon.
-- 
From the badly beaten keyboards of him who speaks    +---best address
in textured Technicolor *TyPe* f-O-n-T-s...          |
						     |
Karl Kleinpaste @ Bell Labs, Columbus   614/860-5107 +-----> cbrma!kk
                @ Ohio State University 614/422-0915   osu-eddie!karl