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