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