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