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