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