2 * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
21 sccsid
[] = "@(#)termcap.c 1.11 88/02/08 SMI"; /* from UCB 5.1 6/5/85 */
25 #define MAXHOP 32 /* max number of tc= indirections */
26 #define E_TERMCAP "/etc/termcap"
28 #include <sys/types.h>
37 * termcap - routines for dealing with the terminal capability data base
39 * BUG: Should use a "last" pointer in tbuf, so that searching
40 * for capabilities alphabetically would not be a n**2/2
41 * process when large numbers of capabilities are given.
42 * Note: If we add a last pointer now we will screw up the
43 * tc capability. We really should compile termcap.
45 * Essentially all the work here is scanning and decoding escapes
46 * in string capabilities. We don't use stdio because the editor
47 * doesn't, and because living w/o it is not hard.
51 static int hopcount
; /* detect infinite loops in termcap, init 0 */
53 /* forward declarations */
54 static char *tdecode(char *, char **);
55 static void tngetsize(char *);
56 static char *tskip(char *bp
);
57 static char *appendsmalldec(char *, int);
62 * Get an entry for terminal name in buffer bp,
63 * from the termcap file. Parse is very rudimentary;
64 * we just notice escaped newlines.
68 tgetent(char *bp
, char *name
)
80 cp
= getenv("TERMCAP");
82 * TERMCAP can have one of two things in it. It can be the
83 * name of a file to use instead of /etc/termcap. In this
84 * case it better start with a "/". Or it can be an entry to
85 * use so we don't have to read the file. In this case it
86 * has to already have the newlines crunched out.
96 (void) strcpy(bp
, cp
);
102 tf
= open(E_TERMCAP
, 0);
104 tf
= open(E_TERMCAP
, 0);
112 cnt
= read(tf
, ibuf
, BUFSIZ
);
121 if (cp
> bp
&& cp
[-1] == '\\') {
127 if (cp
>= bp
+BUFSIZ
) {
128 (void) write(2, "Termcap entry too long\n", 23);
136 * The real work for the match.
138 if (tnamatch(name
)) {
146 * tnchktc: check the last entry, see if it's tc=xxx. If so,
147 * recursively find xxx and append that entry (minus the names)
148 * to take the place of the tc=xxx entry. This allows termcap
149 * entries to say "like an HP2621 but doesn't turn on the labels".
150 * Note that this works because of the left to right scan.
157 char tcname
[16]; /* name of similar terminal */
159 char *holdtbuf
= tbuf
;
162 p
= tbuf
+ strlen(tbuf
) - 2; /* before the last colon */
165 (void) write(2, "Bad termcap entry\n", 18);
169 /* p now points to beginning of last field */
170 if (p
[0] != 't' || p
[1] != 'c') {
174 (void) strcpy(tcname
, p
+3);
176 while (*q
&& *q
!= ':')
179 if (++hopcount
> MAXHOP
) {
180 (void) write(2, "Infinite tc= loop\n", 18);
183 if (tgetent(tcbuf
, tcname
) != 1) {
184 hopcount
= 0; /* unwind recursion */
187 for (q
= tcbuf
; *q
!= ':'; q
++)
189 l
= p
- holdtbuf
+ strlen(q
);
191 (void) write(2, "Termcap entry too long\n", 23);
192 q
[BUFSIZ
- (p
-tbuf
)] = 0;
194 (void) strcpy(p
, q
+1);
196 hopcount
= 0; /* unwind recursion */
202 * Tnamatch deals with name matching. The first field of the termcap
203 * entry is a sequence of names separated by |'s, so we compare
204 * against each such name. The normal : terminator after the last
205 * name (before the first field) stops us.
217 for (Np
= np
; *Np
&& *Bp
== *Np
; Bp
++, Np
++)
219 if (*Np
== 0 && (*Bp
== '|' || *Bp
== ':' || *Bp
== 0))
221 while (*Bp
&& *Bp
!= ':' && *Bp
!= '|')
223 if (*Bp
== 0 || *Bp
== ':')
230 * Skip to the next field. Notice that this is very dumb, not
231 * knowing about \: escapes or any such. If necessary, :'s can be put
232 * into the termcap file in octal.
239 while (*bp
&& *bp
!= ':')
246 } while (*bp
== ':');
252 * Return the (numeric) option id.
253 * Numeric options look like
255 * i.e. the option string is separated from the numeric value by
256 * a # character. If the option is not found we return -1.
257 * Note that we handle octal numbers beginning with 0.
270 if (*bp
++ != id
[0] || *bp
== 0 || *bp
++ != id
[1])
282 i
*= base
, i
+= *bp
++ - '0';
288 * Handle a flag option.
289 * Flag options are given "naked", i.e. followed by a : or the end
290 * of the buffer. Return 1 if we find the option, or 0 if it is
303 if (*bp
++ == id
[0] && *bp
!= 0 && *bp
++ == id
[1]) {
304 if (!*bp
|| *bp
== ':')
313 * Get a string valued option.
316 * Much decoding is done on the strings, and the strings are
317 * placed in area, which is a ref parameter which is updated.
318 * No checking on area overflow.
322 tgetstr(char *id
, char **area
)
330 if (*bp
++ != id
[0] || *bp
== 0 || *bp
++ != id
[1])
337 return (tdecode(bp
, area
));
342 * Tdecode does the grung work to decode the
343 * string capability escapes.
347 tdecode(char *str
, char **area
)
355 while (((c
= *str
++) != 0) && c
!= ':') {
363 dp
= "E\033^^\\\\::n\nr\rt\tb\bf\f";
376 c
<<= 3, c
|= *str
++ - '0';
377 while (--i
&& isdigit(*str
));
389 #include <sys/ioctl.h>
397 if (ioctl(1, TIOCGWINSZ
, (char *)&ws
) < 0)
399 if (ws
.ws_row
== 0 || ws
.ws_col
== 0 ||
400 ws
.ws_row
> 999 || ws
.ws_col
> 999)
402 cp
= index(bp
, ':'); /* find start of description */
403 bp
= rindex(bp
, 0); /* find end of description */
404 np
= bp
+ 15; /* allow enough room for stuff below */
405 while (bp
>= cp
) /* move description right 15 chars */
407 bp
++; /* bp now points to where ':' used to be */
412 bp
= appendsmalldec(bp
, ws
.ws_row
);
417 bp
= appendsmalldec(bp
, ws
.ws_col
);
419 while (bp
<= np
) /* space fill to start of orig description */
424 appendsmalldec(char *bp
, int val
)
428 if ((i
= val
/ 100) != 0) {
432 *bp
++ = '0'; /* place holder because next test fails */
434 if ((i
= val
/ 10) != 0)
436 *bp
++ = '0' + val
% 10;