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