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