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