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 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 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 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 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 * 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 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 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 * 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 * 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