1: /* 2: * Copyright (c) 1980 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[] = "@(#)vgrindefs.c 5.1 (Berkeley) 6/5/85"; 9: #endif not lint 10: 11: #define BUFSIZ 1024 12: #define MAXHOP 32 /* max number of tc= indirections */ 13: 14: #include <ctype.h> 15: /* 16: * grindcap - routines for dealing with the language definitions data base 17: * (code stolen almost totally from termcap) 18: * 19: * BUG: Should use a "last" pointer in tbuf, so that searching 20: * for capabilities alphabetically would not be a n**2/2 21: * process when large numbers of capabilities are given. 22: * Note: If we add a last pointer now we will screw up the 23: * tc capability. We really should compile termcap. 24: * 25: * Essentially all the work here is scanning and decoding escapes 26: * in string capabilities. We don't use stdio because the editor 27: * doesn't, and because living w/o it is not hard. 28: */ 29: 30: static char *tbuf; 31: static char *filename; 32: static int hopcount; /* detect infinite loops in termcap, init 0 */ 33: char *tskip(); 34: char *tgetstr(); 35: char *tdecode(); 36: char *getenv(); 37: 38: /* 39: * Get an entry for terminal name in buffer bp, 40: * from the termcap file. Parse is very rudimentary; 41: * we just notice escaped newlines. 42: */ 43: tgetent(bp, name, file) 44: char *bp, *name, *file; 45: { 46: register char *cp; 47: register int c; 48: register int i = 0, cnt = 0; 49: char ibuf[BUFSIZ]; 50: char *cp2; 51: int tf; 52: 53: tbuf = bp; 54: tf = 0; 55: filename = file; 56: tf = open(filename, 0); 57: if (tf < 0) 58: return (-1); 59: for (;;) { 60: cp = bp; 61: for (;;) { 62: if (i == cnt) { 63: cnt = read(tf, ibuf, BUFSIZ); 64: if (cnt <= 0) { 65: close(tf); 66: return (0); 67: } 68: i = 0; 69: } 70: c = ibuf[i++]; 71: if (c == '\n') { 72: if (cp > bp && cp[-1] == '\\'){ 73: cp--; 74: continue; 75: } 76: break; 77: } 78: if (cp >= bp+BUFSIZ) { 79: write(2,"Vgrind entry too long\n", 23); 80: break; 81: } else 82: *cp++ = c; 83: } 84: *cp = 0; 85: 86: /* 87: * The real work for the match. 88: */ 89: if (tnamatch(name)) { 90: close(tf); 91: return(tnchktc()); 92: } 93: } 94: } 95: 96: /* 97: * tnchktc: check the last entry, see if it's tc=xxx. If so, 98: * recursively find xxx and append that entry (minus the names) 99: * to take the place of the tc=xxx entry. This allows termcap 100: * entries to say "like an HP2621 but doesn't turn on the labels". 101: * Note that this works because of the left to right scan. 102: */ 103: tnchktc() 104: { 105: register char *p, *q; 106: char tcname[16]; /* name of similar terminal */ 107: char tcbuf[BUFSIZ]; 108: char *holdtbuf = tbuf; 109: int l; 110: 111: p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 112: while (*--p != ':') 113: if (p<tbuf) { 114: write(2, "Bad vgrind entry\n", 18); 115: return (0); 116: } 117: p++; 118: /* p now points to beginning of last field */ 119: if (p[0] != 't' || p[1] != 'c') 120: return(1); 121: strcpy(tcname,p+3); 122: q = tcname; 123: while (q && *q != ':') 124: q++; 125: *q = 0; 126: if (++hopcount > MAXHOP) { 127: write(2, "Infinite tc= loop\n", 18); 128: return (0); 129: } 130: if (tgetent(tcbuf, tcname, filename) != 1) 131: return(0); 132: for (q=tcbuf; *q != ':'; q++) 133: ; 134: l = p - holdtbuf + strlen(q); 135: if (l > BUFSIZ) { 136: write(2, "Vgrind entry too long\n", 23); 137: q[BUFSIZ - (p-tbuf)] = 0; 138: } 139: strcpy(p, q+1); 140: tbuf = holdtbuf; 141: return(1); 142: } 143: 144: /* 145: * Tnamatch deals with name matching. The first field of the termcap 146: * entry is a sequence of names separated by |'s, so we compare 147: * against each such name. The normal : terminator after the last 148: * name (before the first field) stops us. 149: */ 150: tnamatch(np) 151: char *np; 152: { 153: register char *Np, *Bp; 154: 155: Bp = tbuf; 156: if (*Bp == '#') 157: return(0); 158: for (;;) { 159: for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 160: continue; 161: if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 162: return (1); 163: while (*Bp && *Bp != ':' && *Bp != '|') 164: Bp++; 165: if (*Bp == 0 || *Bp == ':') 166: return (0); 167: Bp++; 168: } 169: } 170: 171: /* 172: * Skip to the next field. Notice that this is very dumb, not 173: * knowing about \: escapes or any such. If necessary, :'s can be put 174: * into the termcap file in octal. 175: */ 176: static char * 177: tskip(bp) 178: register char *bp; 179: { 180: 181: while (*bp && *bp != ':') 182: bp++; 183: if (*bp == ':') 184: bp++; 185: return (bp); 186: } 187: 188: /* 189: * Return the (numeric) option id. 190: * Numeric options look like 191: * li#80 192: * i.e. the option string is separated from the numeric value by 193: * a # character. If the option is not found we return -1. 194: * Note that we handle octal numbers beginning with 0. 195: */ 196: tgetnum(id) 197: char *id; 198: { 199: register int i, base; 200: register char *bp = tbuf; 201: 202: for (;;) { 203: bp = tskip(bp); 204: if (*bp == 0) 205: return (-1); 206: if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 207: continue; 208: if (*bp == '@') 209: return(-1); 210: if (*bp != '#') 211: continue; 212: bp++; 213: base = 10; 214: if (*bp == '0') 215: base = 8; 216: i = 0; 217: while (isdigit(*bp)) 218: i *= base, i += *bp++ - '0'; 219: return (i); 220: } 221: } 222: 223: /* 224: * Handle a flag option. 225: * Flag options are given "naked", i.e. followed by a : or the end 226: * of the buffer. Return 1 if we find the option, or 0 if it is 227: * not given. 228: */ 229: tgetflag(id) 230: char *id; 231: { 232: register char *bp = tbuf; 233: 234: for (;;) { 235: bp = tskip(bp); 236: if (!*bp) 237: return (0); 238: if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 239: if (!*bp || *bp == ':') 240: return (1); 241: else if (*bp == '@') 242: return(0); 243: } 244: } 245: } 246: 247: /* 248: * Get a string valued option. 249: * These are given as 250: * cl=^Z 251: * Much decoding is done on the strings, and the strings are 252: * placed in area, which is a ref parameter which is updated. 253: * No checking on area overflow. 254: */ 255: char * 256: tgetstr(id, area) 257: char *id, **area; 258: { 259: register char *bp = tbuf; 260: 261: for (;;) { 262: bp = tskip(bp); 263: if (!*bp) 264: return (0); 265: if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 266: continue; 267: if (*bp == '@') 268: return(0); 269: if (*bp != '=') 270: continue; 271: bp++; 272: return (tdecode(bp, area)); 273: } 274: } 275: 276: /* 277: * Tdecode does the grung work to decode the 278: * string capability escapes. 279: */ 280: static char * 281: tdecode(str, area) 282: register char *str; 283: char **area; 284: { 285: register char *cp; 286: register int c; 287: int i; 288: 289: cp = *area; 290: while (c = *str++) { 291: if (c == ':' && *(cp-1) != '\\') 292: break; 293: *cp++ = c; 294: } 295: *cp++ = 0; 296: str = *area; 297: *area = cp; 298: return (str); 299: }