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