BSD 4_3_Reno development
[unix-history] / .ref-fdb8e4aac282b72d4670aa3a26d9bba07afc7e6f / usr / src / lib / libterm / termcap.c
CommitLineData
6468808a 1/*
31a14b65
KB
2 * Copyright (c) 1980 The Regents of the University of California.
3 * All rights reserved.
4 *
9c1340a5 5 * %sccs.include.redist.c%
6468808a
DF
6 */
7
7bc051a7 8#ifndef lint
9c1340a5 9static char sccsid[] = "@(#)termcap.c 5.5 (Berkeley) %G%";
31a14b65 10#endif /* not lint */
7bc051a7
SL
11
12#define BUFSIZ 1024
13#define MAXHOP 32 /* max number of tc= indirections */
d89a08fa
JK
14#define PBUFSIZ 512 /* max length of filename path */
15#define PVECSIZ 32 /* max number of names in path */
7bc051a7
SL
16
17#include <ctype.h>
e5f47b7a
KB
18#include "pathnames.h"
19
7bc051a7
SL
20/*
21 * termcap - routines for dealing with the terminal capability data base
22 *
23 * BUG: Should use a "last" pointer in tbuf, so that searching
24 * for capabilities alphabetically would not be a n**2/2
25 * process when large numbers of capabilities are given.
26 * Note: If we add a last pointer now we will screw up the
27 * tc capability. We really should compile termcap.
28 *
29 * Essentially all the work here is scanning and decoding escapes
30 * in string capabilities. We don't use stdio because the editor
31 * doesn't, and because living w/o it is not hard.
32 */
33
34static char *tbuf;
35static int hopcount; /* detect infinite loops in termcap, init 0 */
d89a08fa
JK
36static char pathbuf[PBUFSIZ]; /* holds raw path of filenames */
37static char *pathvec[PVECSIZ]; /* to point to names in pathbuf */
38static char **pvec; /* holds usable tail of path vector */
7bc051a7
SL
39char *tskip();
40char *tgetstr();
41char *tdecode();
42char *getenv();
43
44/*
d89a08fa 45 * Get an entry for terminal name in buffer bp from the termcap file.
7bc051a7
SL
46 */
47tgetent(bp, name)
48 char *bp, *name;
49{
d89a08fa 50 register char *p;
7bc051a7
SL
51 register char *cp;
52 register int c;
d89a08fa
JK
53 char *term, *home, *termpath;
54 char **fname = pathvec;
7bc051a7 55
d89a08fa 56 pvec = pathvec;
7bc051a7 57 tbuf = bp;
d89a08fa 58 p = pathbuf;
7bc051a7
SL
59 cp = getenv("TERMCAP");
60 /*
61 * TERMCAP can have one of two things in it. It can be the
62 * name of a file to use instead of /etc/termcap. In this
63 * case it better start with a "/". Or it can be an entry to
64 * use so we don't have to read the file. In this case it
d89a08fa
JK
65 * has to already have the newlines crunched out. If TERMCAP
66 * does not hold a file name then a path of names is searched
67 * instead. The path is found in the TERMPATH variable, or
68 * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
7bc051a7 69 */
d89a08fa
JK
70 if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */
71 if (termpath = getenv("TERMPATH"))
72 strncpy(pathbuf, termpath, PBUFSIZ);
73 else {
74 if (home = getenv("HOME")) { /* set up default */
75 p += strlen(home); /* path, looking in */
76 strcpy(pathbuf, home); /* $HOME first */
77 *p++ = '/';
78 } /* if no $HOME look in current directory */
e5f47b7a 79 strncpy(p, _PATH_DEF, PBUFSIZ - (p - pathbuf));
e42cb6f9 80 }
7bc051a7 81 }
d89a08fa
JK
82 else /* user-defined name in TERMCAP */
83 strncpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */
e5f47b7a 84
d89a08fa
JK
85 *fname++ = pathbuf; /* tokenize path into vector of names */
86 while (*++p)
87 if (*p == ' ' || *p == ':') {
88 *p = '\0';
89 while (*++p)
90 if (*p != ' ' && *p != ':')
91 break;
92 if (*p == '\0')
93 break;
94 *fname++ = p;
95 if (fname >= pathvec + PVECSIZ) {
96 fname--;
97 break;
98 }
99 }
100 *fname = (char *) 0; /* mark end of vector */
101 if (cp && *cp && *cp != '/') {
102 tbuf = cp;
103 c = tnamatch(name);
104 tbuf = bp;
105 if (c) {
106 strcpy(bp,cp);
107 return (tnchktc());
108 }
109 }
110 return (tfindent(bp, name)); /* find terminal entry in path */
111}
112
113/*
114 * tfindent - reads through the list of files in pathvec as if they were one
115 * continuous file searching for terminal entries along the way. It will
116 * participate in indirect recursion if the call to tnchktc() finds a tc=
117 * field, which is only searched for in the current file and files ocurring
118 * after it in pathvec. The usable part of this vector is kept in the global
119 * variable pvec. Terminal entries may not be broken across files. Parse is
120 * very rudimentary; we just notice escaped newlines.
121 */
122tfindent(bp, name)
123 char *bp, *name;
124{
125 register char *cp;
126 register int c;
127 register int i, cnt;
128 char ibuf[BUFSIZ];
129 int opencnt = 0;
130 int tf;
131
132 tbuf = bp;
133nextfile:
134 i = cnt = 0;
135 while (*pvec && (tf = open(*pvec, 0)) < 0)
136 pvec++;
137 if (!*pvec)
138 return (opencnt ? 0 : -1);
139 opencnt++;
7bc051a7
SL
140 for (;;) {
141 cp = bp;
142 for (;;) {
143 if (i == cnt) {
144 cnt = read(tf, ibuf, BUFSIZ);
145 if (cnt <= 0) {
146 close(tf);
d89a08fa
JK
147 pvec++;
148 goto nextfile;
7bc051a7
SL
149 }
150 i = 0;
151 }
152 c = ibuf[i++];
153 if (c == '\n') {
154 if (cp > bp && cp[-1] == '\\'){
155 cp--;
156 continue;
157 }
158 break;
159 }
160 if (cp >= bp+BUFSIZ) {
161 write(2,"Termcap entry too long\n", 23);
162 break;
163 } else
164 *cp++ = c;
165 }
166 *cp = 0;
167
168 /*
169 * The real work for the match.
170 */
171 if (tnamatch(name)) {
172 close(tf);
173 return(tnchktc());
174 }
175 }
176}
177
178/*
179 * tnchktc: check the last entry, see if it's tc=xxx. If so,
180 * recursively find xxx and append that entry (minus the names)
181 * to take the place of the tc=xxx entry. This allows termcap
182 * entries to say "like an HP2621 but doesn't turn on the labels".
183 * Note that this works because of the left to right scan.
184 */
185tnchktc()
186{
187 register char *p, *q;
188 char tcname[16]; /* name of similar terminal */
189 char tcbuf[BUFSIZ];
190 char *holdtbuf = tbuf;
191 int l;
192
193 p = tbuf + strlen(tbuf) - 2; /* before the last colon */
194 while (*--p != ':')
195 if (p<tbuf) {
196 write(2, "Bad termcap entry\n", 18);
197 return (0);
198 }
199 p++;
200 /* p now points to beginning of last field */
201 if (p[0] != 't' || p[1] != 'c')
202 return(1);
203 strcpy(tcname,p+3);
204 q = tcname;
4a8ecd7e 205 while (*q && *q != ':')
7bc051a7
SL
206 q++;
207 *q = 0;
208 if (++hopcount > MAXHOP) {
209 write(2, "Infinite tc= loop\n", 18);
210 return (0);
211 }
d89a08fa 212 if (tfindent(tcbuf, tcname) != 1) {
e97efd50 213 hopcount = 0; /* unwind recursion */
7bc051a7 214 return(0);
e97efd50 215 }
7bc051a7
SL
216 for (q=tcbuf; *q != ':'; q++)
217 ;
218 l = p - holdtbuf + strlen(q);
219 if (l > BUFSIZ) {
220 write(2, "Termcap entry too long\n", 23);
221 q[BUFSIZ - (p-tbuf)] = 0;
222 }
223 strcpy(p, q+1);
224 tbuf = holdtbuf;
e97efd50 225 hopcount = 0; /* unwind recursion */
7bc051a7
SL
226 return(1);
227}
228
229/*
230 * Tnamatch deals with name matching. The first field of the termcap
231 * entry is a sequence of names separated by |'s, so we compare
232 * against each such name. The normal : terminator after the last
233 * name (before the first field) stops us.
234 */
235tnamatch(np)
236 char *np;
237{
238 register char *Np, *Bp;
239
240 Bp = tbuf;
241 if (*Bp == '#')
242 return(0);
243 for (;;) {
244 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
245 continue;
246 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
247 return (1);
248 while (*Bp && *Bp != ':' && *Bp != '|')
249 Bp++;
250 if (*Bp == 0 || *Bp == ':')
251 return (0);
252 Bp++;
253 }
254}
255
256/*
257 * Skip to the next field. Notice that this is very dumb, not
258 * knowing about \: escapes or any such. If necessary, :'s can be put
259 * into the termcap file in octal.
260 */
261static char *
262tskip(bp)
263 register char *bp;
264{
265
266 while (*bp && *bp != ':')
267 bp++;
268 if (*bp == ':')
269 bp++;
270 return (bp);
271}
272
273/*
274 * Return the (numeric) option id.
275 * Numeric options look like
276 * li#80
277 * i.e. the option string is separated from the numeric value by
278 * a # character. If the option is not found we return -1.
279 * Note that we handle octal numbers beginning with 0.
280 */
281tgetnum(id)
282 char *id;
283{
284 register int i, base;
285 register char *bp = tbuf;
286
287 for (;;) {
288 bp = tskip(bp);
289 if (*bp == 0)
290 return (-1);
291 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
292 continue;
293 if (*bp == '@')
294 return(-1);
295 if (*bp != '#')
296 continue;
297 bp++;
298 base = 10;
299 if (*bp == '0')
300 base = 8;
301 i = 0;
302 while (isdigit(*bp))
303 i *= base, i += *bp++ - '0';
304 return (i);
305 }
306}
307
308/*
309 * Handle a flag option.
310 * Flag options are given "naked", i.e. followed by a : or the end
311 * of the buffer. Return 1 if we find the option, or 0 if it is
312 * not given.
313 */
314tgetflag(id)
315 char *id;
316{
317 register char *bp = tbuf;
318
319 for (;;) {
320 bp = tskip(bp);
321 if (!*bp)
322 return (0);
323 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
324 if (!*bp || *bp == ':')
325 return (1);
326 else if (*bp == '@')
327 return(0);
328 }
329 }
330}
331
332/*
333 * Get a string valued option.
334 * These are given as
335 * cl=^Z
336 * Much decoding is done on the strings, and the strings are
337 * placed in area, which is a ref parameter which is updated.
338 * No checking on area overflow.
339 */
340char *
341tgetstr(id, area)
342 char *id, **area;
343{
344 register char *bp = tbuf;
345
346 for (;;) {
347 bp = tskip(bp);
348 if (!*bp)
349 return (0);
350 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
351 continue;
352 if (*bp == '@')
353 return(0);
354 if (*bp != '=')
355 continue;
356 bp++;
357 return (tdecode(bp, area));
358 }
359}
360
361/*
362 * Tdecode does the grung work to decode the
363 * string capability escapes.
364 */
365static char *
366tdecode(str, area)
367 register char *str;
368 char **area;
369{
370 register char *cp;
371 register int c;
372 register char *dp;
373 int i;
374
375 cp = *area;
376 while ((c = *str++) && c != ':') {
377 switch (c) {
378
379 case '^':
380 c = *str++ & 037;
381 break;
382
383 case '\\':
384 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
385 c = *str++;
386nextc:
387 if (*dp++ == c) {
388 c = *dp++;
389 break;
390 }
391 dp++;
392 if (*dp)
393 goto nextc;
394 if (isdigit(c)) {
395 c -= '0', i = 2;
396 do
397 c <<= 3, c |= *str++ - '0';
398 while (--i && isdigit(*str));
399 }
400 break;
401 }
402 *cp++ = c;
403 }
404 *cp++ = 0;
405 str = *area;
406 *area = cp;
407 return (str);
408}