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