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