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