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