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