date and time created 83/06/27 13:57:03 by sam
[unix-history] / usr / src / lib / libterm / termcap.c
CommitLineData
7bc051a7
SL
1#ifndef lint
2static char sccsid[] = "@(#)termcap.c 4.1 (Berkeley) %G%";
3#endif
4
5#define BUFSIZ 1024
6#define MAXHOP 32 /* max number of tc= indirections */
7#define E_TERMCAP "/etc/termcap"
8
9#include <ctype.h>
10/*
11 * termcap - routines for dealing with the terminal capability data base
12 *
13 * BUG: Should use a "last" pointer in tbuf, so that searching
14 * for capabilities alphabetically would not be a n**2/2
15 * process when large numbers of capabilities are given.
16 * Note: If we add a last pointer now we will screw up the
17 * tc capability. We really should compile termcap.
18 *
19 * Essentially all the work here is scanning and decoding escapes
20 * in string capabilities. We don't use stdio because the editor
21 * doesn't, and because living w/o it is not hard.
22 */
23
24static char *tbuf;
25static int hopcount; /* detect infinite loops in termcap, init 0 */
26char *tskip();
27char *tgetstr();
28char *tdecode();
29char *getenv();
30
31/*
32 * Get an entry for terminal name in buffer bp,
33 * from the termcap file. Parse is very rudimentary;
34 * we just notice escaped newlines.
35 */
36tgetent(bp, name)
37 char *bp, *name;
38{
39 register char *cp;
40 register int c;
41 register int i = 0, cnt = 0;
42 char ibuf[BUFSIZ];
43 char *cp2;
44 int tf;
45
46 tbuf = bp;
47 tf = 0;
48#ifndef V6
49 cp = getenv("TERMCAP");
50 /*
51 * TERMCAP can have one of two things in it. It can be the
52 * name of a file to use instead of /etc/termcap. In this
53 * case it better start with a "/". Or it can be an entry to
54 * use so we don't have to read the file. In this case it
55 * has to already have the newlines crunched out.
56 */
57 if (cp && *cp) {
58 if (*cp!='/') {
59 cp2 = getenv("TERM");
60 if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
61 strcpy(bp,cp);
62 return(tnchktc());
63 } else {
64 tf = open(E_TERMCAP, 0);
65 }
66 } else
67 tf = open(cp, 0);
68 }
69 if (tf==0)
70 tf = open(E_TERMCAP, 0);
71#else
72 tf = open(E_TERMCAP, 0);
73#endif
74 if (tf < 0)
75 return (-1);
76 for (;;) {
77 cp = bp;
78 for (;;) {
79 if (i == cnt) {
80 cnt = read(tf, ibuf, BUFSIZ);
81 if (cnt <= 0) {
82 close(tf);
83 return (0);
84 }
85 i = 0;
86 }
87 c = ibuf[i++];
88 if (c == '\n') {
89 if (cp > bp && cp[-1] == '\\'){
90 cp--;
91 continue;
92 }
93 break;
94 }
95 if (cp >= bp+BUFSIZ) {
96 write(2,"Termcap entry too long\n", 23);
97 break;
98 } else
99 *cp++ = c;
100 }
101 *cp = 0;
102
103 /*
104 * The real work for the match.
105 */
106 if (tnamatch(name)) {
107 close(tf);
108 return(tnchktc());
109 }
110 }
111}
112
113/*
114 * tnchktc: check the last entry, see if it's tc=xxx. If so,
115 * recursively find xxx and append that entry (minus the names)
116 * to take the place of the tc=xxx entry. This allows termcap
117 * entries to say "like an HP2621 but doesn't turn on the labels".
118 * Note that this works because of the left to right scan.
119 */
120tnchktc()
121{
122 register char *p, *q;
123 char tcname[16]; /* name of similar terminal */
124 char tcbuf[BUFSIZ];
125 char *holdtbuf = tbuf;
126 int l;
127
128 p = tbuf + strlen(tbuf) - 2; /* before the last colon */
129 while (*--p != ':')
130 if (p<tbuf) {
131 write(2, "Bad termcap entry\n", 18);
132 return (0);
133 }
134 p++;
135 /* p now points to beginning of last field */
136 if (p[0] != 't' || p[1] != 'c')
137 return(1);
138 strcpy(tcname,p+3);
139 q = tcname;
140 while (q && *q != ':')
141 q++;
142 *q = 0;
143 if (++hopcount > MAXHOP) {
144 write(2, "Infinite tc= loop\n", 18);
145 return (0);
146 }
147 if (tgetent(tcbuf, tcname) != 1)
148 return(0);
149 for (q=tcbuf; *q != ':'; q++)
150 ;
151 l = p - holdtbuf + strlen(q);
152 if (l > BUFSIZ) {
153 write(2, "Termcap entry too long\n", 23);
154 q[BUFSIZ - (p-tbuf)] = 0;
155 }
156 strcpy(p, q+1);
157 tbuf = holdtbuf;
158 return(1);
159}
160
161/*
162 * Tnamatch deals with name matching. The first field of the termcap
163 * entry is a sequence of names separated by |'s, so we compare
164 * against each such name. The normal : terminator after the last
165 * name (before the first field) stops us.
166 */
167tnamatch(np)
168 char *np;
169{
170 register char *Np, *Bp;
171
172 Bp = tbuf;
173 if (*Bp == '#')
174 return(0);
175 for (;;) {
176 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
177 continue;
178 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
179 return (1);
180 while (*Bp && *Bp != ':' && *Bp != '|')
181 Bp++;
182 if (*Bp == 0 || *Bp == ':')
183 return (0);
184 Bp++;
185 }
186}
187
188/*
189 * Skip to the next field. Notice that this is very dumb, not
190 * knowing about \: escapes or any such. If necessary, :'s can be put
191 * into the termcap file in octal.
192 */
193static char *
194tskip(bp)
195 register char *bp;
196{
197
198 while (*bp && *bp != ':')
199 bp++;
200 if (*bp == ':')
201 bp++;
202 return (bp);
203}
204
205/*
206 * Return the (numeric) option id.
207 * Numeric options look like
208 * li#80
209 * i.e. the option string is separated from the numeric value by
210 * a # character. If the option is not found we return -1.
211 * Note that we handle octal numbers beginning with 0.
212 */
213tgetnum(id)
214 char *id;
215{
216 register int i, base;
217 register char *bp = tbuf;
218
219 for (;;) {
220 bp = tskip(bp);
221 if (*bp == 0)
222 return (-1);
223 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
224 continue;
225 if (*bp == '@')
226 return(-1);
227 if (*bp != '#')
228 continue;
229 bp++;
230 base = 10;
231 if (*bp == '0')
232 base = 8;
233 i = 0;
234 while (isdigit(*bp))
235 i *= base, i += *bp++ - '0';
236 return (i);
237 }
238}
239
240/*
241 * Handle a flag option.
242 * Flag options are given "naked", i.e. followed by a : or the end
243 * of the buffer. Return 1 if we find the option, or 0 if it is
244 * not given.
245 */
246tgetflag(id)
247 char *id;
248{
249 register char *bp = tbuf;
250
251 for (;;) {
252 bp = tskip(bp);
253 if (!*bp)
254 return (0);
255 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
256 if (!*bp || *bp == ':')
257 return (1);
258 else if (*bp == '@')
259 return(0);
260 }
261 }
262}
263
264/*
265 * Get a string valued option.
266 * These are given as
267 * cl=^Z
268 * Much decoding is done on the strings, and the strings are
269 * placed in area, which is a ref parameter which is updated.
270 * No checking on area overflow.
271 */
272char *
273tgetstr(id, area)
274 char *id, **area;
275{
276 register char *bp = tbuf;
277
278 for (;;) {
279 bp = tskip(bp);
280 if (!*bp)
281 return (0);
282 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
283 continue;
284 if (*bp == '@')
285 return(0);
286 if (*bp != '=')
287 continue;
288 bp++;
289 return (tdecode(bp, area));
290 }
291}
292
293/*
294 * Tdecode does the grung work to decode the
295 * string capability escapes.
296 */
297static char *
298tdecode(str, area)
299 register char *str;
300 char **area;
301{
302 register char *cp;
303 register int c;
304 register char *dp;
305 int i;
306
307 cp = *area;
308 while ((c = *str++) && c != ':') {
309 switch (c) {
310
311 case '^':
312 c = *str++ & 037;
313 break;
314
315 case '\\':
316 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
317 c = *str++;
318nextc:
319 if (*dp++ == c) {
320 c = *dp++;
321 break;
322 }
323 dp++;
324 if (*dp)
325 goto nextc;
326 if (isdigit(c)) {
327 c -= '0', i = 2;
328 do
329 c <<= 3, c |= *str++ - '0';
330 while (--i && isdigit(*str));
331 }
332 break;
333 }
334 *cp++ = c;
335 }
336 *cp++ = 0;
337 str = *area;
338 *area = cp;
339 return (str);
340}