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