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