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