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