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