191094Sdes/* 292289Sdes * Copyright (c) 1980, 1993 391094Sdes * The Regents of the University of California. All rights reserved. 491094Sdes * 591094Sdes * Redistribution and use in source and binary forms, with or without 699158Sdes * modification, are permitted provided that the following conditions 799158Sdes * are met: 899158Sdes * 1. Redistributions of source code must retain the above copyright 991094Sdes * notice, this list of conditions and the following disclaimer. 1091094Sdes * 2. Redistributions in binary form must reproduce the above copyright 1191094Sdes * notice, this list of conditions and the following disclaimer in the 1291094Sdes * documentation and/or other materials provided with the distribution. 1391094Sdes * 4. Neither the name of the University nor the names of its contributors 1491094Sdes * may be used to endorse or promote products derived from this software 1591094Sdes * without specific prior written permission. 1691094Sdes * 1791094Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1891094Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1991094Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2091094Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2191094Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2291094Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2391094Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2491094Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2591094Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2691094Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2791094Sdes * SUCH DAMAGE. 2891094Sdes */ 2991094Sdes 3091094Sdes#include <sys/cdefs.h> 3191094Sdes 3291094Sdes__FBSDID("$FreeBSD$"); 3391094Sdes 34114536Sdes#define BUFSIZ 1024 3591094Sdes#define MAXHOP 32 /* max number of tc= indirections */ 3691094Sdes 3791097Sdes#include <ctype.h> 3891094Sdes#include <unistd.h> 3991094Sdes 4091094Sdes/* 4191796Sdes * grindcap - routines for dealing with the language definitions data base 4291094Sdes * (code stolen almost totally from termcap) 4391094Sdes * 4491094Sdes * BUG: Should use a "last" pointer in tbuf, so that searching 4591094Sdes * for capabilities alphabetically would not be a n**2/2 4691094Sdes * process when large numbers of capabilities are given. 4791094Sdes * Note: If we add a last pointer now we will screw up the 48114536Sdes * tc capability. We really should compile termcap. 49114536Sdes * 5091094Sdes * Essentially all the work here is scanning and decoding escapes 5191094Sdes * in string capabilities. We don't use stdio because the editor 5291094Sdes * doesn't, and because living w/o it is not hard. 5391100Sdes */ 5491100Sdes 5591094Sdesstatic char *tbuf; 5691094Sdesstatic char *filename; 5791094Sdesstatic int hopcount; /* detect infinite loops in termcap, init 0 */ 5891094Sdes 5991094Sdesstatic int tnchktc(void); 6091094Sdesstatic int tnamatch(char *); 6191094Sdesstatic char *tskip(register char *); 6291094Sdesstatic char *tdecode(register char *, char **); 6391097Sdes 6491094Sdes/* 6591094Sdes * Get an entry for terminal name in buffer bp, 6691094Sdes * from the termcap file. Parse is very rudimentary; 67114536Sdes * we just notice escaped newlines. 68114536Sdes */ 6991094Sdesint 7091094Sdestgetent(char *bp, char *name, char *file) 7191094Sdes{ 7291094Sdes register char *cp; 7391094Sdes register int c; 7491094Sdes register int i = 0, cnt = 0; 7591094Sdes char ibuf[BUFSIZ]; 7691094Sdes int tf; 7791094Sdes 78107579Sdes tbuf = bp; 7991094Sdes tf = 0; 8091094Sdes filename = file; 8191094Sdes tf = open(filename, O_RDONLY); 8291094Sdes if (tf < 0) 8391097Sdes return (-1); 8491097Sdes for (;;) { 8591097Sdes cp = bp; 8691097Sdes for (;;) { 8791094Sdes if (i == cnt) { 8891094Sdes cnt = read(tf, ibuf, BUFSIZ); 8991094Sdes if (cnt <= 0) { 9091094Sdes close(tf); 9191094Sdes return (0); 9291094Sdes } 9391094Sdes i = 0; 9491094Sdes } 9591094Sdes c = ibuf[i++]; 9691094Sdes if (c == '\n') { 9791094Sdes if (cp > bp && cp[-1] == '\\'){ 9891094Sdes cp--; 9991094Sdes continue; 10091094Sdes } 10191094Sdes break; 10291094Sdes } 10391094Sdes if (cp >= bp+BUFSIZ) { 10491094Sdes write(STDERR_FILENO, "Vgrind entry too long\n", 23); 10591094Sdes break; 10691094Sdes } else 10791094Sdes *cp++ = c; 10891094Sdes } 10991094Sdes *cp = 0; 11091094Sdes 111114536Sdes /* 112114536Sdes * The real work for the match. 11391094Sdes */ 11491094Sdes if (tnamatch(name)) { 11591094Sdes close(tf); 11691094Sdes return(tnchktc()); 11791094Sdes } 11891094Sdes } 11991094Sdes} 12091094Sdes 12191094Sdes/* 122107579Sdes * tnchktc: check the last entry, see if it's tc=xxx. If so, 12391094Sdes * recursively find xxx and append that entry (minus the names) 12491094Sdes * to take the place of the tc=xxx entry. This allows termcap 12591094Sdes * entries to say "like an HP2621 but doesn't turn on the labels". 12691094Sdes * Note that this works because of the left to right scan. 12791094Sdes */ 12891094Sdesstatic int 12991094Sdestnchktc(void) 13091094Sdes{ 13191094Sdes register char *p, *q; 13291100Sdes char tcname[16]; /* name of similar terminal */ 13391100Sdes char tcbuf[BUFSIZ]; 13491100Sdes char *holdtbuf = tbuf; 13591100Sdes int l; 136 137 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 138 while (*--p != ':') 139 if (p<tbuf) { 140 write(STDERR_FILENO, "Bad vgrind entry\n", 18); 141 return (0); 142 } 143 p++; 144 /* p now points to beginning of last field */ 145 if (p[0] != 't' || p[1] != 'c') 146 return(1); 147 strlcpy(tcname, p+3, 16); 148 q = tcname; 149 while (q && *q != ':') 150 q++; 151 *q = 0; 152 if (++hopcount > MAXHOP) { 153 write(STDERR_FILENO, "Infinite tc= loop\n", 18); 154 return (0); 155 } 156 if (tgetent(tcbuf, tcname, filename) != 1) 157 return(0); 158 for (q=tcbuf; *q != ':'; q++) 159 ; 160 l = p - holdtbuf + strlen(q); 161 if (l > BUFSIZ) { 162 write(STDERR_FILENO, "Vgrind entry too long\n", 23); 163 q[BUFSIZ - (p-tbuf)] = 0; 164 } 165 strlcpy(p, q+1, BUFSIZ - (p - holdtbuf)); 166 tbuf = holdtbuf; 167 return(1); 168} 169 170/* 171 * Tnamatch deals with name matching. The first field of the termcap 172 * entry is a sequence of names separated by |'s, so we compare 173 * against each such name. The normal : terminator after the last 174 * name (before the first field) stops us. 175 */ 176static int 177tnamatch(char *np) 178{ 179 register char *Np, *Bp; 180 181 Bp = tbuf; 182 if (*Bp == '#') 183 return(0); 184 for (;;) { 185 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 186 continue; 187 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 188 return (1); 189 while (*Bp && *Bp != ':' && *Bp != '|') 190 Bp++; 191 if (*Bp == 0 || *Bp == ':') 192 return (0); 193 Bp++; 194 } 195} 196 197/* 198 * Skip to the next field. Notice that this is very dumb, not 199 * knowing about \: escapes or any such. If necessary, :'s can be put 200 * into the termcap file in octal. 201 */ 202static char * 203tskip(register char *bp) 204{ 205 206 while (*bp && *bp != ':') 207 bp++; 208 if (*bp == ':') 209 bp++; 210 return (bp); 211} 212 213/* 214 * Return the (numeric) option id. 215 * Numeric options look like 216 * li#80 217 * i.e. the option string is separated from the numeric value by 218 * a # character. If the option is not found we return -1. 219 * Note that we handle octal numbers beginning with 0. 220 */ 221int 222tgetnum(char *id) 223{ 224 register int i, base; 225 register char *bp = tbuf; 226 227 for (;;) { 228 bp = tskip(bp); 229 if (*bp == 0) 230 return (-1); 231 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 232 continue; 233 if (*bp == '@') 234 return(-1); 235 if (*bp != '#') 236 continue; 237 bp++; 238 base = 10; 239 if (*bp == '0') 240 base = 8; 241 i = 0; 242 while (isdigit(*bp)) 243 i *= base, i += *bp++ - '0'; 244 return (i); 245 } 246} 247 248/* 249 * Handle a flag option. 250 * Flag options are given "naked", i.e. followed by a : or the end 251 * of the buffer. Return 1 if we find the option, or 0 if it is 252 * not given. 253 */ 254int 255tgetflag(char *id) 256{ 257 register char *bp = tbuf; 258 259 for (;;) { 260 bp = tskip(bp); 261 if (!*bp) 262 return (0); 263 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 264 if (!*bp || *bp == ':') 265 return (1); 266 else if (*bp == '@') 267 return(0); 268 } 269 } 270} 271 272/* 273 * Get a string valued option. 274 * These are given as 275 * cl=^Z 276 * Much decoding is done on the strings, and the strings are 277 * placed in area, which is a ref parameter which is updated. 278 * No checking on area overflow. 279 */ 280char * 281tgetstr(char *id, char **area) 282{ 283 register char *bp = tbuf; 284 285 for (;;) { 286 bp = tskip(bp); 287 if (!*bp) 288 return (0); 289 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 290 continue; 291 if (*bp == '@') 292 return(0); 293 if (*bp != '=') 294 continue; 295 bp++; 296 return (tdecode(bp, area)); 297 } 298} 299 300/* 301 * Tdecode does the grung work to decode the 302 * string capability escapes. 303 */ 304static char * 305tdecode(register char *str, char **area) 306{ 307 register char *cp; 308 register int c; 309 310 cp = *area; 311 while (c = *str++) { 312 if (c == ':' && *(cp-1) != '\\') 313 break; 314 *cp++ = c; 315 } 316 *cp++ = 0; 317 str = *area; 318 *area = cp; 319 return (str); 320} 321