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