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