vgrindefs.c revision 282950
1/* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31 32__FBSDID("$FreeBSD: stable/10/usr.bin/vgrind/vgrindefs.c 282950 2015-05-15 08:53:52Z bapt $"); 33 34#define BUFSIZ 1024 35#define MAXHOP 32 /* max number of tc= indirections */ 36 37#include <ctype.h> 38#include <unistd.h> 39 40/* 41 * grindcap - routines for dealing with the language definitions data base 42 * (code stolen almost totally from termcap) 43 * 44 * BUG: Should use a "last" pointer in tbuf, so that searching 45 * for capabilities alphabetically would not be a n**2/2 46 * process when large numbers of capabilities are given. 47 * Note: If we add a last pointer now we will screw up the 48 * tc capability. We really should compile termcap. 49 * 50 * Essentially all the work here is scanning and decoding escapes 51 * in string capabilities. We don't use stdio because the editor 52 * doesn't, and because living w/o it is not hard. 53 */ 54 55static char *tbuf; 56static char *filename; 57static int hopcount; /* detect infinite loops in termcap, init 0 */ 58 59static int tnchktc(void); 60static int tnamatch(char *); 61static char *tskip(register char *); 62static char *tdecode(register char *, char **); 63 64/* 65 * Get an entry for terminal name in buffer bp, 66 * from the termcap file. Parse is very rudimentary; 67 * we just notice escaped newlines. 68 */ 69int 70tgetent(char *bp, char *name, char *file) 71{ 72 register char *cp; 73 register int c; 74 register int i = 0, cnt = 0; 75 char ibuf[BUFSIZ]; 76 int tf; 77 78 tbuf = bp; 79 tf = 0; 80 filename = file; 81 tf = open(filename, O_RDONLY); 82 if (tf < 0) 83 return (-1); 84 for (;;) { 85 cp = bp; 86 for (;;) { 87 if (i == cnt) { 88 cnt = read(tf, ibuf, BUFSIZ); 89 if (cnt <= 0) { 90 close(tf); 91 return (0); 92 } 93 i = 0; 94 } 95 c = ibuf[i++]; 96 if (c == '\n') { 97 if (cp > bp && cp[-1] == '\\'){ 98 cp--; 99 continue; 100 } 101 break; 102 } 103 if (cp >= bp+BUFSIZ) { 104 write(STDERR_FILENO, "Vgrind entry too long\n", 23); 105 break; 106 } else 107 *cp++ = c; 108 } 109 *cp = 0; 110 111 /* 112 * The real work for the match. 113 */ 114 if (tnamatch(name)) { 115 close(tf); 116 return(tnchktc()); 117 } 118 } 119} 120 121/* 122 * tnchktc: check the last entry, see if it's tc=xxx. If so, 123 * recursively find xxx and append that entry (minus the names) 124 * to take the place of the tc=xxx entry. This allows termcap 125 * entries to say "like an HP2621 but doesn't turn on the labels". 126 * Note that this works because of the left to right scan. 127 */ 128static int 129tnchktc(void) 130{ 131 register char *p, *q; 132 char tcname[16]; /* name of similar terminal */ 133 char tcbuf[BUFSIZ]; 134 char *holdtbuf = tbuf; 135 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