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) 06/05/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 } 300