checknr.c revision 1590
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
351590Srgrimesstatic char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1980, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381590Srgrimes#endif /* not lint */
391590Srgrimes
401590Srgrimes#ifndef lint
411590Srgrimesstatic char sccsid[] = "@(#)checknr.c	8.1 (Berkeley) 6/6/93";
421590Srgrimes#endif /* not lint */
431590Srgrimes
441590Srgrimes/*
451590Srgrimes * checknr: check an nroff/troff input file for matching macro calls.
461590Srgrimes * we also attempt to match size and font changes, but only the embedded
471590Srgrimes * kind.  These must end in \s0 and \fP resp.  Maybe more sophistication
481590Srgrimes * later but for now think of these restrictions as contributions to
491590Srgrimes * structured typesetting.
501590Srgrimes */
511590Srgrimes#include <stdio.h>
521590Srgrimes#include <ctype.h>
531590Srgrimes
541590Srgrimes#define MAXSTK	100	/* Stack size */
551590Srgrimes#define MAXBR	100	/* Max number of bracket pairs known */
561590Srgrimes#define MAXCMDS	500	/* Max number of commands known */
571590Srgrimes
581590Srgrimes/*
591590Srgrimes * The stack on which we remember what we've seen so far.
601590Srgrimes */
611590Srgrimesstruct stkstr {
621590Srgrimes	int opno;	/* number of opening bracket */
631590Srgrimes	int pl;		/* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */
641590Srgrimes	int parm;	/* parm to size, font, etc */
651590Srgrimes	int lno;	/* line number the thing came in in */
661590Srgrimes} stk[MAXSTK];
671590Srgrimesint stktop;
681590Srgrimes
691590Srgrimes/*
701590Srgrimes * The kinds of opening and closing brackets.
711590Srgrimes */
721590Srgrimesstruct brstr {
731590Srgrimes	char *opbr;
741590Srgrimes	char *clbr;
751590Srgrimes} br[MAXBR] = {
761590Srgrimes	/* A few bare bones troff commands */
771590Srgrimes#define SZ	0
781590Srgrimes	"sz",	"sz",	/* also \s */
791590Srgrimes#define FT	1
801590Srgrimes	"ft",	"ft",	/* also \f */
811590Srgrimes	/* the -mm package */
821590Srgrimes	"AL",	"LE",
831590Srgrimes	"AS",	"AE",
841590Srgrimes	"BL",	"LE",
851590Srgrimes	"BS",	"BE",
861590Srgrimes	"DF",	"DE",
871590Srgrimes	"DL",	"LE",
881590Srgrimes	"DS",	"DE",
891590Srgrimes	"FS",	"FE",
901590Srgrimes	"ML",	"LE",
911590Srgrimes	"NS",	"NE",
921590Srgrimes	"RL",	"LE",
931590Srgrimes	"VL",	"LE",
941590Srgrimes	/* the -ms package */
951590Srgrimes	"AB",	"AE",
961590Srgrimes	"BD",	"DE",
971590Srgrimes	"CD",	"DE",
981590Srgrimes	"DS",	"DE",
991590Srgrimes	"FS",	"FE",
1001590Srgrimes	"ID",	"DE",
1011590Srgrimes	"KF",	"KE",
1021590Srgrimes	"KS",	"KE",
1031590Srgrimes	"LD",	"DE",
1041590Srgrimes	"LG",	"NL",
1051590Srgrimes	"QS",	"QE",
1061590Srgrimes	"RS",	"RE",
1071590Srgrimes	"SM",	"NL",
1081590Srgrimes	"XA",	"XE",
1091590Srgrimes	"XS",	"XE",
1101590Srgrimes	/* The -me package */
1111590Srgrimes	"(b",	")b",
1121590Srgrimes	"(c",	")c",
1131590Srgrimes	"(d",	")d",
1141590Srgrimes	"(f",	")f",
1151590Srgrimes	"(l",	")l",
1161590Srgrimes	"(q",	")q",
1171590Srgrimes	"(x",	")x",
1181590Srgrimes	"(z",	")z",
1191590Srgrimes	/* Things needed by preprocessors */
1201590Srgrimes	"EQ",	"EN",
1211590Srgrimes	"TS",	"TE",
1221590Srgrimes	/* Refer */
1231590Srgrimes	"[",	"]",
1241590Srgrimes	0,	0
1251590Srgrimes};
1261590Srgrimes
1271590Srgrimes/*
1281590Srgrimes * All commands known to nroff, plus macro packages.
1291590Srgrimes * Used so we can complain about unrecognized commands.
1301590Srgrimes */
1311590Srgrimeschar *knowncmds[MAXCMDS] = {
1321590Srgrimes"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t",
1331590Srgrimes"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++",
1341590Srgrimes"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M",
1351590Srgrimes"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB",
1361590Srgrimes"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B",  "B1", "B2",
1371590Srgrimes"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT",
1381590Srgrimes"D",  "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM",
1391590Srgrimes"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO",
1401590Srgrimes"FQ", "FS", "FV", "FX", "H",  "HC", "HD", "HM", "HO", "HU", "I",  "ID",
1411590Srgrimes"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB",
1421590Srgrimes"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR",
1431590Srgrimes"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P",
1441590Srgrimes"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R",  "RA",
1451590Srgrimes"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S",  "S0", "S2", "S3", "SA",
1461590Srgrimes"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE",
1471590Srgrimes"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL",
1481590Srgrimes"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[",  "[-", "[0",
1491590Srgrimes"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]",  "]-", "]<", "]>",
1501590Srgrimes"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b",  "ba", "bc", "bd",
1511590Srgrimes"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs",
1521590Srgrimes"ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec",
1531590Srgrimes"ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo",
1541590Srgrimes"fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i",
1551590Srgrimes"ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln",
1561590Srgrimes"lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1",
1571590Srgrimes"n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx",
1581590Srgrimes"of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps",
1591590Srgrimes"q",  "r",  "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb",
1601590Srgrimes"sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th",
1611590Srgrimes"ti", "tl", "tm", "tp", "tr", "u",  "uf", "uh", "ul", "vs", "wh", "xp",
1621590Srgrimes"yr", 0
1631590Srgrimes};
1641590Srgrimes
1651590Srgrimesint	lineno;		/* current line number in input file */
1661590Srgrimeschar	line[256];	/* the current line */
1671590Srgrimeschar	*cfilename;	/* name of current file */
1681590Srgrimesint	nfiles;		/* number of files to process */
1691590Srgrimesint	fflag;		/* -f: ignore \f */
1701590Srgrimesint	sflag;		/* -s: ignore \s */
1711590Srgrimesint	ncmds;		/* size of knowncmds */
1721590Srgrimesint	slot;		/* slot in knowncmds found by binsrch */
1731590Srgrimes
1741590Srgrimeschar	*malloc();
1751590Srgrimes
1761590Srgrimesmain(argc, argv)
1771590Srgrimesint argc;
1781590Srgrimeschar **argv;
1791590Srgrimes{
1801590Srgrimes	FILE *f;
1811590Srgrimes	int i;
1821590Srgrimes	char *cp;
1831590Srgrimes	char b1[4];
1841590Srgrimes
1851590Srgrimes	/* Figure out how many known commands there are */
1861590Srgrimes	while (knowncmds[ncmds])
1871590Srgrimes		ncmds++;
1881590Srgrimes	while (argc > 1 && argv[1][0] == '-') {
1891590Srgrimes		switch(argv[1][1]) {
1901590Srgrimes
1911590Srgrimes		/* -a: add pairs of macros */
1921590Srgrimes		case 'a':
1931590Srgrimes			i = strlen(argv[1]) - 2;
1941590Srgrimes			if (i % 6 != 0)
1951590Srgrimes				usage();
1961590Srgrimes			/* look for empty macro slots */
1971590Srgrimes			for (i=0; br[i].opbr; i++)
1981590Srgrimes				;
1991590Srgrimes			for (cp=argv[1]+3; cp[-1]; cp += 6) {
2001590Srgrimes				br[i].opbr = malloc(3);
2011590Srgrimes				strncpy(br[i].opbr, cp, 2);
2021590Srgrimes				br[i].clbr = malloc(3);
2031590Srgrimes				strncpy(br[i].clbr, cp+3, 2);
2041590Srgrimes				addmac(br[i].opbr);	/* knows pairs are also known cmds */
2051590Srgrimes				addmac(br[i].clbr);
2061590Srgrimes				i++;
2071590Srgrimes			}
2081590Srgrimes			break;
2091590Srgrimes
2101590Srgrimes		/* -c: add known commands */
2111590Srgrimes		case 'c':
2121590Srgrimes			i = strlen(argv[1]) - 2;
2131590Srgrimes			if (i % 3 != 0)
2141590Srgrimes				usage();
2151590Srgrimes			for (cp=argv[1]+3; cp[-1]; cp += 3) {
2161590Srgrimes				if (cp[2] && cp[2] != '.')
2171590Srgrimes					usage();
2181590Srgrimes				strncpy(b1, cp, 2);
2191590Srgrimes				addmac(b1);
2201590Srgrimes			}
2211590Srgrimes			break;
2221590Srgrimes
2231590Srgrimes		/* -f: ignore font changes */
2241590Srgrimes		case 'f':
2251590Srgrimes			fflag = 1;
2261590Srgrimes			break;
2271590Srgrimes
2281590Srgrimes		/* -s: ignore size changes */
2291590Srgrimes		case 's':
2301590Srgrimes			sflag = 1;
2311590Srgrimes			break;
2321590Srgrimes		default:
2331590Srgrimes			usage();
2341590Srgrimes		}
2351590Srgrimes		argc--; argv++;
2361590Srgrimes	}
2371590Srgrimes
2381590Srgrimes	nfiles = argc - 1;
2391590Srgrimes
2401590Srgrimes	if (nfiles > 0) {
2411590Srgrimes		for (i=1; i<argc; i++) {
2421590Srgrimes			cfilename = argv[i];
2431590Srgrimes			f = fopen(cfilename, "r");
2441590Srgrimes			if (f == NULL)
2451590Srgrimes				perror(cfilename);
2461590Srgrimes			else
2471590Srgrimes				process(f);
2481590Srgrimes		}
2491590Srgrimes	} else {
2501590Srgrimes		cfilename = "stdin";
2511590Srgrimes		process(stdin);
2521590Srgrimes	}
2531590Srgrimes	exit(0);
2541590Srgrimes}
2551590Srgrimes
2561590Srgrimesusage()
2571590Srgrimes{
2581590Srgrimes	printf("Usage: checknr -s -f -a.xx.yy.xx.yy... -c.xx.xx.xx...\n");
2591590Srgrimes	exit(1);
2601590Srgrimes}
2611590Srgrimes
2621590Srgrimesprocess(f)
2631590SrgrimesFILE *f;
2641590Srgrimes{
2651590Srgrimes	register int i, n;
2661590Srgrimes	char mac[5];	/* The current macro or nroff command */
2671590Srgrimes	int pl;
2681590Srgrimes
2691590Srgrimes	stktop = -1;
2701590Srgrimes	for (lineno = 1; fgets(line, sizeof line, f); lineno++) {
2711590Srgrimes		if (line[0] == '.') {
2721590Srgrimes			/*
2731590Srgrimes			 * find and isolate the macro/command name.
2741590Srgrimes			 */
2751590Srgrimes			strncpy(mac, line+1, 4);
2761590Srgrimes			if (isspace(mac[0])) {
2771590Srgrimes				pe(lineno);
2781590Srgrimes				printf("Empty command\n");
2791590Srgrimes			} else if (isspace(mac[1])) {
2801590Srgrimes				mac[1] = 0;
2811590Srgrimes			} else if (isspace(mac[2])) {
2821590Srgrimes				mac[2] = 0;
2831590Srgrimes			} else if (mac[0] != '\\' || mac[1] != '\"') {
2841590Srgrimes				pe(lineno);
2851590Srgrimes				printf("Command too long\n");
2861590Srgrimes			}
2871590Srgrimes
2881590Srgrimes			/*
2891590Srgrimes			 * Is it a known command?
2901590Srgrimes			 */
2911590Srgrimes			checkknown(mac);
2921590Srgrimes
2931590Srgrimes			/*
2941590Srgrimes			 * Should we add it?
2951590Srgrimes			 */
2961590Srgrimes			if (eq(mac, "de"))
2971590Srgrimes				addcmd(line);
2981590Srgrimes
2991590Srgrimes			chkcmd(line, mac);
3001590Srgrimes		}
3011590Srgrimes
3021590Srgrimes		/*
3031590Srgrimes		 * At this point we process the line looking
3041590Srgrimes		 * for \s and \f.
3051590Srgrimes		 */
3061590Srgrimes		for (i=0; line[i]; i++)
3071590Srgrimes			if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) {
3081590Srgrimes				if (!sflag && line[++i]=='s') {
3091590Srgrimes					pl = line[++i];
3101590Srgrimes					if (isdigit(pl)) {
3111590Srgrimes						n = pl - '0';
3121590Srgrimes						pl = ' ';
3131590Srgrimes					} else
3141590Srgrimes						n = 0;
3151590Srgrimes					while (isdigit(line[++i]))
3161590Srgrimes						n = 10 * n + line[i] - '0';
3171590Srgrimes					i--;
3181590Srgrimes					if (n == 0) {
3191590Srgrimes						if (stk[stktop].opno == SZ) {
3201590Srgrimes							stktop--;
3211590Srgrimes						} else {
3221590Srgrimes							pe(lineno);
3231590Srgrimes							printf("unmatched \\s0\n");
3241590Srgrimes						}
3251590Srgrimes					} else {
3261590Srgrimes						stk[++stktop].opno = SZ;
3271590Srgrimes						stk[stktop].pl = pl;
3281590Srgrimes						stk[stktop].parm = n;
3291590Srgrimes						stk[stktop].lno = lineno;
3301590Srgrimes					}
3311590Srgrimes				} else if (!fflag && line[i]=='f') {
3321590Srgrimes					n = line[++i];
3331590Srgrimes					if (n == 'P') {
3341590Srgrimes						if (stk[stktop].opno == FT) {
3351590Srgrimes							stktop--;
3361590Srgrimes						} else {
3371590Srgrimes							pe(lineno);
3381590Srgrimes							printf("unmatched \\fP\n");
3391590Srgrimes						}
3401590Srgrimes					} else {
3411590Srgrimes						stk[++stktop].opno = FT;
3421590Srgrimes						stk[stktop].pl = 1;
3431590Srgrimes						stk[stktop].parm = n;
3441590Srgrimes						stk[stktop].lno = lineno;
3451590Srgrimes					}
3461590Srgrimes				}
3471590Srgrimes			}
3481590Srgrimes	}
3491590Srgrimes	/*
3501590Srgrimes	 * We've hit the end and look at all this stuff that hasn't been
3511590Srgrimes	 * matched yet!  Complain, complain.
3521590Srgrimes	 */
3531590Srgrimes	for (i=stktop; i>=0; i--) {
3541590Srgrimes		complain(i);
3551590Srgrimes	}
3561590Srgrimes}
3571590Srgrimes
3581590Srgrimescomplain(i)
3591590Srgrimes{
3601590Srgrimes	pe(stk[i].lno);
3611590Srgrimes	printf("Unmatched ");
3621590Srgrimes	prop(i);
3631590Srgrimes	printf("\n");
3641590Srgrimes}
3651590Srgrimes
3661590Srgrimesprop(i)
3671590Srgrimes{
3681590Srgrimes	if (stk[i].pl == 0)
3691590Srgrimes		printf(".%s", br[stk[i].opno].opbr);
3701590Srgrimes	else switch(stk[i].opno) {
3711590Srgrimes	case SZ:
3721590Srgrimes		printf("\\s%c%d", stk[i].pl, stk[i].parm);
3731590Srgrimes		break;
3741590Srgrimes	case FT:
3751590Srgrimes		printf("\\f%c", stk[i].parm);
3761590Srgrimes		break;
3771590Srgrimes	default:
3781590Srgrimes		printf("Bug: stk[%d].opno = %d = .%s, .%s",
3791590Srgrimes			i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr);
3801590Srgrimes	}
3811590Srgrimes}
3821590Srgrimes
3831590Srgrimeschkcmd(line, mac)
3841590Srgrimeschar *line;
3851590Srgrimeschar *mac;
3861590Srgrimes{
3871590Srgrimes	register int i, n;
3881590Srgrimes
3891590Srgrimes	/*
3901590Srgrimes	 * Check to see if it matches top of stack.
3911590Srgrimes	 */
3921590Srgrimes	if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr))
3931590Srgrimes		stktop--;	/* OK. Pop & forget */
3941590Srgrimes	else {
3951590Srgrimes		/* No. Maybe it's an opener */
3961590Srgrimes		for (i=0; br[i].opbr; i++) {
3971590Srgrimes			if (eq(mac, br[i].opbr)) {
3981590Srgrimes				/* Found. Push it. */
3991590Srgrimes				stktop++;
4001590Srgrimes				stk[stktop].opno = i;
4011590Srgrimes				stk[stktop].pl = 0;
4021590Srgrimes				stk[stktop].parm = 0;
4031590Srgrimes				stk[stktop].lno = lineno;
4041590Srgrimes				break;
4051590Srgrimes			}
4061590Srgrimes			/*
4071590Srgrimes			 * Maybe it's an unmatched closer.
4081590Srgrimes			 * NOTE: this depends on the fact
4091590Srgrimes			 * that none of the closers can be
4101590Srgrimes			 * openers too.
4111590Srgrimes			 */
4121590Srgrimes			if (eq(mac, br[i].clbr)) {
4131590Srgrimes				nomatch(mac);
4141590Srgrimes				break;
4151590Srgrimes			}
4161590Srgrimes		}
4171590Srgrimes	}
4181590Srgrimes}
4191590Srgrimes
4201590Srgrimesnomatch(mac)
4211590Srgrimeschar *mac;
4221590Srgrimes{
4231590Srgrimes	register int i, j;
4241590Srgrimes
4251590Srgrimes	/*
4261590Srgrimes	 * Look for a match further down on stack
4271590Srgrimes	 * If we find one, it suggests that the stuff in
4281590Srgrimes	 * between is supposed to match itself.
4291590Srgrimes	 */
4301590Srgrimes	for (j=stktop; j>=0; j--)
4311590Srgrimes		if (eq(mac,br[stk[j].opno].clbr)) {
4321590Srgrimes			/* Found.  Make a good diagnostic. */
4331590Srgrimes			if (j == stktop-2) {
4341590Srgrimes				/*
4351590Srgrimes				 * Check for special case \fx..\fR and don't
4361590Srgrimes				 * complain.
4371590Srgrimes				 */
4381590Srgrimes				if (stk[j+1].opno==FT && stk[j+1].parm!='R'
4391590Srgrimes				 && stk[j+2].opno==FT && stk[j+2].parm=='R') {
4401590Srgrimes					stktop = j -1;
4411590Srgrimes					return;
4421590Srgrimes				}
4431590Srgrimes				/*
4441590Srgrimes				 * We have two unmatched frobs.  Chances are
4451590Srgrimes				 * they were intended to match, so we mention
4461590Srgrimes				 * them together.
4471590Srgrimes				 */
4481590Srgrimes				pe(stk[j+1].lno);
4491590Srgrimes				prop(j+1);
4501590Srgrimes				printf(" does not match %d: ", stk[j+2].lno);
4511590Srgrimes				prop(j+2);
4521590Srgrimes				printf("\n");
4531590Srgrimes			} else for (i=j+1; i <= stktop; i++) {
4541590Srgrimes				complain(i);
4551590Srgrimes			}
4561590Srgrimes			stktop = j-1;
4571590Srgrimes			return;
4581590Srgrimes		}
4591590Srgrimes	/* Didn't find one.  Throw this away. */
4601590Srgrimes	pe(lineno);
4611590Srgrimes	printf("Unmatched .%s\n", mac);
4621590Srgrimes}
4631590Srgrimes
4641590Srgrimes/* eq: are two strings equal? */
4651590Srgrimeseq(s1, s2)
4661590Srgrimeschar *s1, *s2;
4671590Srgrimes{
4681590Srgrimes	return (strcmp(s1, s2) == 0);
4691590Srgrimes}
4701590Srgrimes
4711590Srgrimes/* print the first part of an error message, given the line number */
4721590Srgrimespe(lineno)
4731590Srgrimesint lineno;
4741590Srgrimes{
4751590Srgrimes	if (nfiles > 1)
4761590Srgrimes		printf("%s: ", cfilename);
4771590Srgrimes	printf("%d: ", lineno);
4781590Srgrimes}
4791590Srgrimes
4801590Srgrimescheckknown(mac)
4811590Srgrimeschar *mac;
4821590Srgrimes{
4831590Srgrimes
4841590Srgrimes	if (eq(mac, "."))
4851590Srgrimes		return;
4861590Srgrimes	if (binsrch(mac) >= 0)
4871590Srgrimes		return;
4881590Srgrimes	if (mac[0] == '\\' && mac[1] == '"')	/* comments */
4891590Srgrimes		return;
4901590Srgrimes
4911590Srgrimes	pe(lineno);
4921590Srgrimes	printf("Unknown command: .%s\n", mac);
4931590Srgrimes}
4941590Srgrimes
4951590Srgrimes/*
4961590Srgrimes * We have a .de xx line in "line".  Add xx to the list of known commands.
4971590Srgrimes */
4981590Srgrimesaddcmd(line)
4991590Srgrimeschar *line;
5001590Srgrimes{
5011590Srgrimes	char *mac;
5021590Srgrimes
5031590Srgrimes	/* grab the macro being defined */
5041590Srgrimes	mac = line+4;
5051590Srgrimes	while (isspace(*mac))
5061590Srgrimes		mac++;
5071590Srgrimes	if (*mac == 0) {
5081590Srgrimes		pe(lineno);
5091590Srgrimes		printf("illegal define: %s\n", line);
5101590Srgrimes		return;
5111590Srgrimes	}
5121590Srgrimes	mac[2] = 0;
5131590Srgrimes	if (isspace(mac[1]) || mac[1] == '\\')
5141590Srgrimes		mac[1] = 0;
5151590Srgrimes	if (ncmds >= MAXCMDS) {
5161590Srgrimes		printf("Only %d known commands allowed\n", MAXCMDS);
5171590Srgrimes		exit(1);
5181590Srgrimes	}
5191590Srgrimes	addmac(mac);
5201590Srgrimes}
5211590Srgrimes
5221590Srgrimes/*
5231590Srgrimes * Add mac to the list.  We should really have some kind of tree
5241590Srgrimes * structure here but this is a quick-and-dirty job and I just don't
5251590Srgrimes * have time to mess with it.  (I wonder if this will come back to haunt
5261590Srgrimes * me someday?)  Anyway, I claim that .de is fairly rare in user
5271590Srgrimes * nroff programs, and the register loop below is pretty fast.
5281590Srgrimes */
5291590Srgrimesaddmac(mac)
5301590Srgrimeschar *mac;
5311590Srgrimes{
5321590Srgrimes	register char **src, **dest, **loc;
5331590Srgrimes
5341590Srgrimes	if (binsrch(mac) >= 0){	/* it's OK to redefine something */
5351590Srgrimes#ifdef DEBUG
5361590Srgrimes		printf("binsrch(%s) -> already in table\n", mac);
5371590Srgrimes#endif DEBUG
5381590Srgrimes		return;
5391590Srgrimes	}
5401590Srgrimes	/* binsrch sets slot as a side effect */
5411590Srgrimes#ifdef DEBUG
5421590Srgrimesprintf("binsrch(%s) -> %d\n", mac, slot);
5431590Srgrimes#endif
5441590Srgrimes	loc = &knowncmds[slot];
5451590Srgrimes	src = &knowncmds[ncmds-1];
5461590Srgrimes	dest = src+1;
5471590Srgrimes	while (dest > loc)
5481590Srgrimes		*dest-- = *src--;
5491590Srgrimes	*loc = malloc(3);
5501590Srgrimes	strcpy(*loc, mac);
5511590Srgrimes	ncmds++;
5521590Srgrimes#ifdef DEBUG
5531590Srgrimesprintf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds);
5541590Srgrimes#endif
5551590Srgrimes}
5561590Srgrimes
5571590Srgrimes/*
5581590Srgrimes * Do a binary search in knowncmds for mac.
5591590Srgrimes * If found, return the index.  If not, return -1.
5601590Srgrimes */
5611590Srgrimesbinsrch(mac)
5621590Srgrimeschar *mac;
5631590Srgrimes{
5641590Srgrimes	register char *p;	/* pointer to current cmd in list */
5651590Srgrimes	register int d;		/* difference if any */
5661590Srgrimes	register int mid;	/* mid point in binary search */
5671590Srgrimes	register int top, bot;	/* boundaries of bin search, inclusive */
5681590Srgrimes
5691590Srgrimes	top = ncmds-1;
5701590Srgrimes	bot = 0;
5711590Srgrimes	while (top >= bot) {
5721590Srgrimes		mid = (top+bot)/2;
5731590Srgrimes		p = knowncmds[mid];
5741590Srgrimes		d = p[0] - mac[0];
5751590Srgrimes		if (d == 0)
5761590Srgrimes			d = p[1] - mac[1];
5771590Srgrimes		if (d == 0)
5781590Srgrimes			return mid;
5791590Srgrimes		if (d < 0)
5801590Srgrimes			bot = mid + 1;
5811590Srgrimes		else
5821590Srgrimes			top = mid - 1;
5831590Srgrimes	}
5841590Srgrimes	slot = bot;	/* place it would have gone */
5851590Srgrimes	return -1;
5861590Srgrimes}
587