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