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