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