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