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