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