11590Srgrimes/* 21590Srgrimes * Copyright (c) 1980, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 3087693Smarkm#include <sys/cdefs.h> 311590Srgrimes 3287693Smarkm__FBSDID("$FreeBSD: releng/10.3/usr.bin/vgrind/vgrindefs.c 282950 2015-05-15 08:53:52Z bapt $"); 3387693Smarkm 341590Srgrimes#define BUFSIZ 1024 351590Srgrimes#define MAXHOP 32 /* max number of tc= indirections */ 361590Srgrimes 371590Srgrimes#include <ctype.h> 3880381Ssheldonh#include <unistd.h> 3980381Ssheldonh 401590Srgrimes/* 411590Srgrimes * grindcap - routines for dealing with the language definitions data base 421590Srgrimes * (code stolen almost totally from termcap) 431590Srgrimes * 441590Srgrimes * BUG: Should use a "last" pointer in tbuf, so that searching 451590Srgrimes * for capabilities alphabetically would not be a n**2/2 461590Srgrimes * process when large numbers of capabilities are given. 471590Srgrimes * Note: If we add a last pointer now we will screw up the 481590Srgrimes * tc capability. We really should compile termcap. 491590Srgrimes * 501590Srgrimes * Essentially all the work here is scanning and decoding escapes 511590Srgrimes * in string capabilities. We don't use stdio because the editor 521590Srgrimes * doesn't, and because living w/o it is not hard. 531590Srgrimes */ 541590Srgrimes 551590Srgrimesstatic char *tbuf; 561590Srgrimesstatic char *filename; 571590Srgrimesstatic int hopcount; /* detect infinite loops in termcap, init 0 */ 581590Srgrimes 59282950Sbaptstatic int tnchktc(void); 60282950Sbaptstatic int tnamatch(char *); 61282950Sbaptstatic char *tskip(register char *); 62282950Sbaptstatic char *tdecode(register char *, char **); 63282950Sbapt 641590Srgrimes/* 651590Srgrimes * Get an entry for terminal name in buffer bp, 661590Srgrimes * from the termcap file. Parse is very rudimentary; 671590Srgrimes * we just notice escaped newlines. 681590Srgrimes */ 69282950Sbaptint 70282950Sbapttgetent(char *bp, char *name, char *file) 711590Srgrimes{ 721590Srgrimes register char *cp; 731590Srgrimes register int c; 741590Srgrimes register int i = 0, cnt = 0; 751590Srgrimes char ibuf[BUFSIZ]; 761590Srgrimes int tf; 771590Srgrimes 781590Srgrimes tbuf = bp; 791590Srgrimes tf = 0; 801590Srgrimes filename = file; 81282950Sbapt tf = open(filename, O_RDONLY); 821590Srgrimes if (tf < 0) 831590Srgrimes return (-1); 841590Srgrimes for (;;) { 851590Srgrimes cp = bp; 861590Srgrimes for (;;) { 871590Srgrimes if (i == cnt) { 881590Srgrimes cnt = read(tf, ibuf, BUFSIZ); 891590Srgrimes if (cnt <= 0) { 901590Srgrimes close(tf); 911590Srgrimes return (0); 921590Srgrimes } 931590Srgrimes i = 0; 941590Srgrimes } 951590Srgrimes c = ibuf[i++]; 961590Srgrimes if (c == '\n') { 971590Srgrimes if (cp > bp && cp[-1] == '\\'){ 981590Srgrimes cp--; 991590Srgrimes continue; 1001590Srgrimes } 1011590Srgrimes break; 1021590Srgrimes } 1031590Srgrimes if (cp >= bp+BUFSIZ) { 10480381Ssheldonh write(STDERR_FILENO, "Vgrind entry too long\n", 23); 1051590Srgrimes break; 1061590Srgrimes } else 1071590Srgrimes *cp++ = c; 1081590Srgrimes } 1091590Srgrimes *cp = 0; 1101590Srgrimes 1111590Srgrimes /* 1121590Srgrimes * The real work for the match. 1131590Srgrimes */ 1141590Srgrimes if (tnamatch(name)) { 1151590Srgrimes close(tf); 1161590Srgrimes return(tnchktc()); 1171590Srgrimes } 1181590Srgrimes } 1191590Srgrimes} 1201590Srgrimes 1211590Srgrimes/* 1221590Srgrimes * tnchktc: check the last entry, see if it's tc=xxx. If so, 1231590Srgrimes * recursively find xxx and append that entry (minus the names) 1241590Srgrimes * to take the place of the tc=xxx entry. This allows termcap 1251590Srgrimes * entries to say "like an HP2621 but doesn't turn on the labels". 1261590Srgrimes * Note that this works because of the left to right scan. 1271590Srgrimes */ 128282950Sbaptstatic int 129282950Sbapttnchktc(void) 1301590Srgrimes{ 1311590Srgrimes register char *p, *q; 1321590Srgrimes char tcname[16]; /* name of similar terminal */ 1331590Srgrimes char tcbuf[BUFSIZ]; 1341590Srgrimes char *holdtbuf = tbuf; 1351590Srgrimes int l; 1361590Srgrimes 1371590Srgrimes p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 1381590Srgrimes while (*--p != ':') 1391590Srgrimes if (p<tbuf) { 14080381Ssheldonh write(STDERR_FILENO, "Bad vgrind entry\n", 18); 1411590Srgrimes return (0); 1421590Srgrimes } 1431590Srgrimes p++; 1441590Srgrimes /* p now points to beginning of last field */ 1451590Srgrimes if (p[0] != 't' || p[1] != 'c') 1461590Srgrimes return(1); 147282950Sbapt strlcpy(tcname, p+3, 16); 1481590Srgrimes q = tcname; 1491590Srgrimes while (q && *q != ':') 1501590Srgrimes q++; 1511590Srgrimes *q = 0; 1521590Srgrimes if (++hopcount > MAXHOP) { 15380381Ssheldonh write(STDERR_FILENO, "Infinite tc= loop\n", 18); 1541590Srgrimes return (0); 1551590Srgrimes } 1561590Srgrimes if (tgetent(tcbuf, tcname, filename) != 1) 1571590Srgrimes return(0); 1581590Srgrimes for (q=tcbuf; *q != ':'; q++) 1591590Srgrimes ; 1601590Srgrimes l = p - holdtbuf + strlen(q); 1611590Srgrimes if (l > BUFSIZ) { 16280381Ssheldonh write(STDERR_FILENO, "Vgrind entry too long\n", 23); 1631590Srgrimes q[BUFSIZ - (p-tbuf)] = 0; 1641590Srgrimes } 165282950Sbapt strlcpy(p, q+1, BUFSIZ - (p - holdtbuf)); 1661590Srgrimes tbuf = holdtbuf; 1671590Srgrimes return(1); 1681590Srgrimes} 1691590Srgrimes 1701590Srgrimes/* 1711590Srgrimes * Tnamatch deals with name matching. The first field of the termcap 1721590Srgrimes * entry is a sequence of names separated by |'s, so we compare 1731590Srgrimes * against each such name. The normal : terminator after the last 1741590Srgrimes * name (before the first field) stops us. 1751590Srgrimes */ 176282950Sbaptstatic int 177282950Sbapttnamatch(char *np) 1781590Srgrimes{ 1791590Srgrimes register char *Np, *Bp; 1801590Srgrimes 1811590Srgrimes Bp = tbuf; 1821590Srgrimes if (*Bp == '#') 1831590Srgrimes return(0); 1841590Srgrimes for (;;) { 1851590Srgrimes for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 1861590Srgrimes continue; 1871590Srgrimes if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 1881590Srgrimes return (1); 1891590Srgrimes while (*Bp && *Bp != ':' && *Bp != '|') 1901590Srgrimes Bp++; 1911590Srgrimes if (*Bp == 0 || *Bp == ':') 1921590Srgrimes return (0); 1931590Srgrimes Bp++; 1941590Srgrimes } 1951590Srgrimes} 1961590Srgrimes 1971590Srgrimes/* 1981590Srgrimes * Skip to the next field. Notice that this is very dumb, not 1991590Srgrimes * knowing about \: escapes or any such. If necessary, :'s can be put 2001590Srgrimes * into the termcap file in octal. 2011590Srgrimes */ 2021590Srgrimesstatic char * 203282950Sbapttskip(register char *bp) 2041590Srgrimes{ 2051590Srgrimes 2061590Srgrimes while (*bp && *bp != ':') 2071590Srgrimes bp++; 2081590Srgrimes if (*bp == ':') 2091590Srgrimes bp++; 2101590Srgrimes return (bp); 2111590Srgrimes} 2121590Srgrimes 2131590Srgrimes/* 2141590Srgrimes * Return the (numeric) option id. 2151590Srgrimes * Numeric options look like 2161590Srgrimes * li#80 2171590Srgrimes * i.e. the option string is separated from the numeric value by 2181590Srgrimes * a # character. If the option is not found we return -1. 2191590Srgrimes * Note that we handle octal numbers beginning with 0. 2201590Srgrimes */ 221282950Sbaptint 222282950Sbapttgetnum(char *id) 2231590Srgrimes{ 2241590Srgrimes register int i, base; 2251590Srgrimes register char *bp = tbuf; 2261590Srgrimes 2271590Srgrimes for (;;) { 2281590Srgrimes bp = tskip(bp); 2291590Srgrimes if (*bp == 0) 2301590Srgrimes return (-1); 2311590Srgrimes if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 2321590Srgrimes continue; 2331590Srgrimes if (*bp == '@') 2341590Srgrimes return(-1); 2351590Srgrimes if (*bp != '#') 2361590Srgrimes continue; 2371590Srgrimes bp++; 2381590Srgrimes base = 10; 2391590Srgrimes if (*bp == '0') 2401590Srgrimes base = 8; 2411590Srgrimes i = 0; 2421590Srgrimes while (isdigit(*bp)) 2431590Srgrimes i *= base, i += *bp++ - '0'; 2441590Srgrimes return (i); 2451590Srgrimes } 2461590Srgrimes} 2471590Srgrimes 2481590Srgrimes/* 2491590Srgrimes * Handle a flag option. 2501590Srgrimes * Flag options are given "naked", i.e. followed by a : or the end 2511590Srgrimes * of the buffer. Return 1 if we find the option, or 0 if it is 2521590Srgrimes * not given. 2531590Srgrimes */ 254282950Sbaptint 255282950Sbapttgetflag(char *id) 2561590Srgrimes{ 2571590Srgrimes register char *bp = tbuf; 2581590Srgrimes 2591590Srgrimes for (;;) { 2601590Srgrimes bp = tskip(bp); 2611590Srgrimes if (!*bp) 2621590Srgrimes return (0); 2631590Srgrimes if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 2641590Srgrimes if (!*bp || *bp == ':') 2651590Srgrimes return (1); 2661590Srgrimes else if (*bp == '@') 2671590Srgrimes return(0); 2681590Srgrimes } 2691590Srgrimes } 2701590Srgrimes} 2711590Srgrimes 2721590Srgrimes/* 2731590Srgrimes * Get a string valued option. 2741590Srgrimes * These are given as 2751590Srgrimes * cl=^Z 2761590Srgrimes * Much decoding is done on the strings, and the strings are 2771590Srgrimes * placed in area, which is a ref parameter which is updated. 2781590Srgrimes * No checking on area overflow. 2791590Srgrimes */ 2801590Srgrimeschar * 281282950Sbapttgetstr(char *id, char **area) 2821590Srgrimes{ 2831590Srgrimes register char *bp = tbuf; 2841590Srgrimes 2851590Srgrimes for (;;) { 2861590Srgrimes bp = tskip(bp); 2871590Srgrimes if (!*bp) 2881590Srgrimes return (0); 2891590Srgrimes if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 2901590Srgrimes continue; 2911590Srgrimes if (*bp == '@') 2921590Srgrimes return(0); 2931590Srgrimes if (*bp != '=') 2941590Srgrimes continue; 2951590Srgrimes bp++; 2961590Srgrimes return (tdecode(bp, area)); 2971590Srgrimes } 2981590Srgrimes} 2991590Srgrimes 3001590Srgrimes/* 3011590Srgrimes * Tdecode does the grung work to decode the 3021590Srgrimes * string capability escapes. 3031590Srgrimes */ 3041590Srgrimesstatic char * 305282950Sbapttdecode(register char *str, char **area) 3061590Srgrimes{ 3071590Srgrimes register char *cp; 3081590Srgrimes register int c; 3091590Srgrimes 3101590Srgrimes cp = *area; 3111590Srgrimes while (c = *str++) { 3121590Srgrimes if (c == ':' && *(cp-1) != '\\') 3131590Srgrimes break; 3141590Srgrimes *cp++ = c; 3151590Srgrimes } 3161590Srgrimes *cp++ = 0; 3171590Srgrimes str = *area; 3181590Srgrimes *area = cp; 3191590Srgrimes return (str); 3201590Srgrimes} 321