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 #define BUFSIZ 1024 8 #define MAXHOP 32 /* max number of tc= indirections */ 9 10 #include <ctype.h> 11 #include <locale.h> 12 #include <string.h> 13 /* 14 * grindcap - routines for dealing with the language definitions data base 15 * (code stolen almost totally from termcap) 16 * 17 * BUG: Should use a "last" pointer in tbuf, so that searching 18 * for capabilities alphabetically would not be a n**2/2 19 * process when large numbers of capabilities are given. 20 * Note: If we add a last pointer now we will screw up the 21 * tc capability. We really should compile termcap. 22 * 23 * Essentially all the work here is scanning and decoding escapes 24 * in string capabilities. We don't use stdio because the editor 25 * doesn't, and because living w/o it is not hard. 26 */ 27 28 static char *tbuf; 29 static char *filename; 30 static int hopcount; /* detect infinite loops in termcap, init 0 */ 31 char *tgetstr(); 32 char *getenv(); 33 34 static char *tdecode(char *str, char **area); 35 static char *tskip(char *bp); 36 static int tnchktc(void); 37 static int tnamatch(char *np); 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 int 47 tgetent(char *bp, char *name, char *file) 48 { 49 char *cp; 50 int c; 51 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 static int 108 tnchktc(void) 109 { 110 char *p, *q; 111 char tcname[16]; /* name of similar terminal */ 112 char tcbuf[BUFSIZ]; 113 char *holdtbuf = tbuf; 114 int l; 115 116 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 117 while (*--p != ':') 118 if (p<tbuf) { 119 vgrind_msg = gettext("Bad vgrind entry\n"); 120 write(2, vgrind_msg, strlen(vgrind_msg)); 121 return (0); 122 } 123 p++; 124 /* p now points to beginning of last field */ 125 if (p[0] != 't' || p[1] != 'c') 126 return(1); 127 strcpy(tcname,p+3); 128 q = tcname; 129 while (q && *q != ':') 130 q++; 131 *q = 0; 132 if (++hopcount > MAXHOP) { 133 vgrind_msg = gettext("Infinite tc= loop\n"); 134 write(2, vgrind_msg, strlen(vgrind_msg)); 135 return (0); 136 } 137 if (tgetent(tcbuf, tcname, filename) != 1) 138 return(0); 139 for (q=tcbuf; *q != ':'; q++) 140 ; 141 l = p - holdtbuf + strlen(q); 142 if (l > BUFSIZ) { 143 vgrind_msg = gettext("Vgrind entry too long\n"); 144 write(2, vgrind_msg, strlen(vgrind_msg)); 145 q[BUFSIZ - (p-tbuf)] = 0; 146 } 147 strcpy(p, q+1); 148 tbuf = holdtbuf; 149 return(1); 150 } 151 152 /* 153 * Tnamatch deals with name matching. The first field of the termcap 154 * entry is a sequence of names separated by |'s, so we compare 155 * against each such name. The normal : terminator after the last 156 * name (before the first field) stops us. 157 */ 158 static int 159 tnamatch(char *np) 160 { 161 char *Np, *Bp; 162 163 Bp = tbuf; 164 if (*Bp == '#') 165 return(0); 166 for (;;) { 167 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 168 continue; 169 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 170 return (1); 171 while (*Bp && *Bp != ':' && *Bp != '|') 172 Bp++; 173 if (*Bp == 0 || *Bp == ':') 174 return (0); 175 Bp++; 176 } 177 } 178 179 /* 180 * Skip to the next field. Notice that this is very dumb, not 181 * knowing about \: escapes or any such. If necessary, :'s can be put 182 * into the termcap file in octal. 183 */ 184 static char * 185 tskip(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 static int 204 tgetnum(char *id) 205 { 206 int i, base; 207 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 int 237 tgetflag(char *id) 238 { 239 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(char *id, char **area) 264 { 265 char *bp = tbuf; 266 267 for (;;) { 268 bp = tskip(bp); 269 if (!*bp) 270 return (0); 271 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 272 continue; 273 if (*bp == '@') 274 return(0); 275 if (*bp != '=') 276 continue; 277 bp++; 278 return (tdecode(bp, area)); 279 } 280 } 281 282 /* 283 * Tdecode does the grung work to decode the 284 * string capability escapes. 285 */ 286 static char * 287 tdecode(char *str, char **area) 288 { 289 char *cp; 290 int c; 291 int i; 292 293 cp = *area; 294 while (c = *str++) { 295 if (c == ':' && *(cp-1) != '\\') 296 break; 297 *cp++ = c; 298 } 299 *cp++ = 0; 300 str = *area; 301 *area = cp; 302 return (str); 303 } 304