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