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