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