xref: /csrg-svn/usr.bin/vgrind/vgrindefs.c (revision 36167)
122066Sdist /*
2*36167Sbostic  * Copyright (c) 1980 The Regents of the University of California.
3*36167Sbostic  * All rights reserved.
4*36167Sbostic  *
5*36167Sbostic  * Redistribution and use in source and binary forms are permitted
6*36167Sbostic  * provided that the above copyright notice and this paragraph are
7*36167Sbostic  * duplicated in all such forms and that any documentation,
8*36167Sbostic  * advertising materials, and other materials related to such
9*36167Sbostic  * distribution and use acknowledge that the software was developed
10*36167Sbostic  * by the University of California, Berkeley.  The name of the
11*36167Sbostic  * University may not be used to endorse or promote products derived
12*36167Sbostic  * from this software without specific prior written permission.
13*36167Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*36167Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*36167Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1622066Sdist  */
1722066Sdist 
1813722Ssam #ifndef lint
19*36167Sbostic static char sccsid[] = "@(#)vgrindefs.c	5.2 (Berkeley) 10/25/88";
20*36167Sbostic #endif /* not lint */
2113722Ssam 
228662Smckusick #define	BUFSIZ	1024
238662Smckusick #define MAXHOP	32	/* max number of tc= indirections */
248662Smckusick 
258662Smckusick #include <ctype.h>
268662Smckusick /*
278662Smckusick  * grindcap - routines for dealing with the language definitions data base
288662Smckusick  *	(code stolen almost totally from termcap)
298662Smckusick  *
308662Smckusick  * BUG:		Should use a "last" pointer in tbuf, so that searching
318662Smckusick  *		for capabilities alphabetically would not be a n**2/2
328662Smckusick  *		process when large numbers of capabilities are given.
338662Smckusick  * Note:	If we add a last pointer now we will screw up the
348662Smckusick  *		tc capability. We really should compile termcap.
358662Smckusick  *
368662Smckusick  * Essentially all the work here is scanning and decoding escapes
378662Smckusick  * in string capabilities.  We don't use stdio because the editor
388662Smckusick  * doesn't, and because living w/o it is not hard.
398662Smckusick  */
408662Smckusick 
418662Smckusick static	char *tbuf;
4217500Sralph static	char *filename;
438662Smckusick static	int hopcount;	/* detect infinite loops in termcap, init 0 */
448662Smckusick char	*tskip();
458662Smckusick char	*tgetstr();
468662Smckusick char	*tdecode();
478662Smckusick char	*getenv();
488662Smckusick 
498662Smckusick /*
508662Smckusick  * Get an entry for terminal name in buffer bp,
518662Smckusick  * from the termcap file.  Parse is very rudimentary;
528662Smckusick  * we just notice escaped newlines.
538662Smckusick  */
5417500Sralph tgetent(bp, name, file)
5517500Sralph 	char *bp, *name, *file;
568662Smckusick {
578662Smckusick 	register char *cp;
588662Smckusick 	register int c;
598662Smckusick 	register int i = 0, cnt = 0;
608662Smckusick 	char ibuf[BUFSIZ];
618662Smckusick 	char *cp2;
628662Smckusick 	int tf;
638662Smckusick 
648662Smckusick 	tbuf = bp;
658662Smckusick 	tf = 0;
6617500Sralph 	filename = file;
678662Smckusick 	tf = open(filename, 0);
688662Smckusick 	if (tf < 0)
698662Smckusick 		return (-1);
708662Smckusick 	for (;;) {
718662Smckusick 		cp = bp;
728662Smckusick 		for (;;) {
738662Smckusick 			if (i == cnt) {
748662Smckusick 				cnt = read(tf, ibuf, BUFSIZ);
758662Smckusick 				if (cnt <= 0) {
768662Smckusick 					close(tf);
778662Smckusick 					return (0);
788662Smckusick 				}
798662Smckusick 				i = 0;
808662Smckusick 			}
818662Smckusick 			c = ibuf[i++];
828662Smckusick 			if (c == '\n') {
838662Smckusick 				if (cp > bp && cp[-1] == '\\'){
848662Smckusick 					cp--;
858662Smckusick 					continue;
868662Smckusick 				}
878662Smckusick 				break;
888662Smckusick 			}
898662Smckusick 			if (cp >= bp+BUFSIZ) {
908662Smckusick 				write(2,"Vgrind entry too long\n", 23);
918662Smckusick 				break;
928662Smckusick 			} else
938662Smckusick 				*cp++ = c;
948662Smckusick 		}
958662Smckusick 		*cp = 0;
968662Smckusick 
978662Smckusick 		/*
988662Smckusick 		 * The real work for the match.
998662Smckusick 		 */
1008662Smckusick 		if (tnamatch(name)) {
1018662Smckusick 			close(tf);
1028662Smckusick 			return(tnchktc());
1038662Smckusick 		}
1048662Smckusick 	}
1058662Smckusick }
1068662Smckusick 
1078662Smckusick /*
1088662Smckusick  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1098662Smckusick  * recursively find xxx and append that entry (minus the names)
1108662Smckusick  * to take the place of the tc=xxx entry. This allows termcap
1118662Smckusick  * entries to say "like an HP2621 but doesn't turn on the labels".
1128662Smckusick  * Note that this works because of the left to right scan.
1138662Smckusick  */
1148662Smckusick tnchktc()
1158662Smckusick {
1168662Smckusick 	register char *p, *q;
1178662Smckusick 	char tcname[16];	/* name of similar terminal */
1188662Smckusick 	char tcbuf[BUFSIZ];
1198662Smckusick 	char *holdtbuf = tbuf;
1208662Smckusick 	int l;
1218662Smckusick 
1228662Smckusick 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
1238662Smckusick 	while (*--p != ':')
1248662Smckusick 		if (p<tbuf) {
1258662Smckusick 			write(2, "Bad vgrind entry\n", 18);
1268662Smckusick 			return (0);
1278662Smckusick 		}
1288662Smckusick 	p++;
1298662Smckusick 	/* p now points to beginning of last field */
1308662Smckusick 	if (p[0] != 't' || p[1] != 'c')
1318662Smckusick 		return(1);
1328662Smckusick 	strcpy(tcname,p+3);
1338662Smckusick 	q = tcname;
1348662Smckusick 	while (q && *q != ':')
1358662Smckusick 		q++;
1368662Smckusick 	*q = 0;
1378662Smckusick 	if (++hopcount > MAXHOP) {
1388662Smckusick 		write(2, "Infinite tc= loop\n", 18);
1398662Smckusick 		return (0);
1408662Smckusick 	}
14117500Sralph 	if (tgetent(tcbuf, tcname, filename) != 1)
1428662Smckusick 		return(0);
1438662Smckusick 	for (q=tcbuf; *q != ':'; q++)
1448662Smckusick 		;
1458662Smckusick 	l = p - holdtbuf + strlen(q);
1468662Smckusick 	if (l > BUFSIZ) {
1478662Smckusick 		write(2, "Vgrind entry too long\n", 23);
1488662Smckusick 		q[BUFSIZ - (p-tbuf)] = 0;
1498662Smckusick 	}
1508662Smckusick 	strcpy(p, q+1);
1518662Smckusick 	tbuf = holdtbuf;
1528662Smckusick 	return(1);
1538662Smckusick }
1548662Smckusick 
1558662Smckusick /*
1568662Smckusick  * Tnamatch deals with name matching.  The first field of the termcap
1578662Smckusick  * entry is a sequence of names separated by |'s, so we compare
1588662Smckusick  * against each such name.  The normal : terminator after the last
1598662Smckusick  * name (before the first field) stops us.
1608662Smckusick  */
1618662Smckusick tnamatch(np)
1628662Smckusick 	char *np;
1638662Smckusick {
1648662Smckusick 	register char *Np, *Bp;
1658662Smckusick 
1668662Smckusick 	Bp = tbuf;
1678662Smckusick 	if (*Bp == '#')
1688662Smckusick 		return(0);
1698662Smckusick 	for (;;) {
1708662Smckusick 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
1718662Smckusick 			continue;
1728662Smckusick 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
1738662Smckusick 			return (1);
1748662Smckusick 		while (*Bp && *Bp != ':' && *Bp != '|')
1758662Smckusick 			Bp++;
1768662Smckusick 		if (*Bp == 0 || *Bp == ':')
1778662Smckusick 			return (0);
1788662Smckusick 		Bp++;
1798662Smckusick 	}
1808662Smckusick }
1818662Smckusick 
1828662Smckusick /*
1838662Smckusick  * Skip to the next field.  Notice that this is very dumb, not
1848662Smckusick  * knowing about \: escapes or any such.  If necessary, :'s can be put
1858662Smckusick  * into the termcap file in octal.
1868662Smckusick  */
1878662Smckusick static char *
1888662Smckusick tskip(bp)
1898662Smckusick 	register char *bp;
1908662Smckusick {
1918662Smckusick 
1928662Smckusick 	while (*bp && *bp != ':')
1938662Smckusick 		bp++;
1948662Smckusick 	if (*bp == ':')
1958662Smckusick 		bp++;
1968662Smckusick 	return (bp);
1978662Smckusick }
1988662Smckusick 
1998662Smckusick /*
2008662Smckusick  * Return the (numeric) option id.
2018662Smckusick  * Numeric options look like
2028662Smckusick  *	li#80
2038662Smckusick  * i.e. the option string is separated from the numeric value by
2048662Smckusick  * a # character.  If the option is not found we return -1.
2058662Smckusick  * Note that we handle octal numbers beginning with 0.
2068662Smckusick  */
2078662Smckusick tgetnum(id)
2088662Smckusick 	char *id;
2098662Smckusick {
2108662Smckusick 	register int i, base;
2118662Smckusick 	register char *bp = tbuf;
2128662Smckusick 
2138662Smckusick 	for (;;) {
2148662Smckusick 		bp = tskip(bp);
2158662Smckusick 		if (*bp == 0)
2168662Smckusick 			return (-1);
2178662Smckusick 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2188662Smckusick 			continue;
2198662Smckusick 		if (*bp == '@')
2208662Smckusick 			return(-1);
2218662Smckusick 		if (*bp != '#')
2228662Smckusick 			continue;
2238662Smckusick 		bp++;
2248662Smckusick 		base = 10;
2258662Smckusick 		if (*bp == '0')
2268662Smckusick 			base = 8;
2278662Smckusick 		i = 0;
2288662Smckusick 		while (isdigit(*bp))
2298662Smckusick 			i *= base, i += *bp++ - '0';
2308662Smckusick 		return (i);
2318662Smckusick 	}
2328662Smckusick }
2338662Smckusick 
2348662Smckusick /*
2358662Smckusick  * Handle a flag option.
2368662Smckusick  * Flag options are given "naked", i.e. followed by a : or the end
2378662Smckusick  * of the buffer.  Return 1 if we find the option, or 0 if it is
2388662Smckusick  * not given.
2398662Smckusick  */
2408662Smckusick tgetflag(id)
2418662Smckusick 	char *id;
2428662Smckusick {
2438662Smckusick 	register char *bp = tbuf;
2448662Smckusick 
2458662Smckusick 	for (;;) {
2468662Smckusick 		bp = tskip(bp);
2478662Smckusick 		if (!*bp)
2488662Smckusick 			return (0);
2498662Smckusick 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
2508662Smckusick 			if (!*bp || *bp == ':')
2518662Smckusick 				return (1);
2528662Smckusick 			else if (*bp == '@')
2538662Smckusick 				return(0);
2548662Smckusick 		}
2558662Smckusick 	}
2568662Smckusick }
2578662Smckusick 
2588662Smckusick /*
2598662Smckusick  * Get a string valued option.
2608662Smckusick  * These are given as
2618662Smckusick  *	cl=^Z
2628662Smckusick  * Much decoding is done on the strings, and the strings are
2638662Smckusick  * placed in area, which is a ref parameter which is updated.
2648662Smckusick  * No checking on area overflow.
2658662Smckusick  */
2668662Smckusick char *
2678662Smckusick tgetstr(id, area)
2688662Smckusick 	char *id, **area;
2698662Smckusick {
2708662Smckusick 	register char *bp = tbuf;
2718662Smckusick 
2728662Smckusick 	for (;;) {
2738662Smckusick 		bp = tskip(bp);
2748662Smckusick 		if (!*bp)
2758662Smckusick 			return (0);
2768662Smckusick 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2778662Smckusick 			continue;
2788662Smckusick 		if (*bp == '@')
2798662Smckusick 			return(0);
2808662Smckusick 		if (*bp != '=')
2818662Smckusick 			continue;
2828662Smckusick 		bp++;
2838662Smckusick 		return (tdecode(bp, area));
2848662Smckusick 	}
2858662Smckusick }
2868662Smckusick 
2878662Smckusick /*
2888662Smckusick  * Tdecode does the grung work to decode the
2898662Smckusick  * string capability escapes.
2908662Smckusick  */
2918662Smckusick static char *
2928662Smckusick tdecode(str, area)
2938662Smckusick 	register char *str;
2948662Smckusick 	char **area;
2958662Smckusick {
2968662Smckusick 	register char *cp;
2978662Smckusick 	register int c;
2988662Smckusick 	int i;
2998662Smckusick 
3008662Smckusick 	cp = *area;
3018662Smckusick 	while (c = *str++) {
3028662Smckusick 	    if (c == ':' && *(cp-1) != '\\')
3038662Smckusick 		break;
3048662Smckusick 	    *cp++ = c;
3058662Smckusick 	}
3068662Smckusick 	*cp++ = 0;
3078662Smckusick 	str = *area;
3088662Smckusick 	*area = cp;
3098662Smckusick 	return (str);
3108662Smckusick }
311