xref: /minix3/usr.bin/checknr/checknr.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: checknr.c,v 1.24 2013/08/12 14:03:18 joerg Exp $	*/
20c745990SThomas Cort 
30c745990SThomas Cort /*
40c745990SThomas Cort  * Copyright (c) 1980, 1993
50c745990SThomas Cort  *	The Regents of the University of California.  All rights reserved.
60c745990SThomas Cort  *
70c745990SThomas Cort  * Redistribution and use in source and binary forms, with or without
80c745990SThomas Cort  * modification, are permitted provided that the following conditions
90c745990SThomas Cort  * are met:
100c745990SThomas Cort  * 1. Redistributions of source code must retain the above copyright
110c745990SThomas Cort  *    notice, this list of conditions and the following disclaimer.
120c745990SThomas Cort  * 2. Redistributions in binary form must reproduce the above copyright
130c745990SThomas Cort  *    notice, this list of conditions and the following disclaimer in the
140c745990SThomas Cort  *    documentation and/or other materials provided with the distribution.
150c745990SThomas Cort  * 3. Neither the name of the University nor the names of its contributors
160c745990SThomas Cort  *    may be used to endorse or promote products derived from this software
170c745990SThomas Cort  *    without specific prior written permission.
180c745990SThomas Cort  *
190c745990SThomas Cort  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
200c745990SThomas Cort  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
210c745990SThomas Cort  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
220c745990SThomas Cort  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
230c745990SThomas Cort  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
240c745990SThomas Cort  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
250c745990SThomas Cort  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
260c745990SThomas Cort  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
270c745990SThomas Cort  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
280c745990SThomas Cort  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
290c745990SThomas Cort  * SUCH DAMAGE.
300c745990SThomas Cort  */
310c745990SThomas Cort 
320c745990SThomas Cort #include <sys/cdefs.h>
330c745990SThomas Cort #ifndef lint
340c745990SThomas Cort __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
350c745990SThomas Cort  The Regents of the University of California.  All rights reserved.");
360c745990SThomas Cort #endif /* not lint */
370c745990SThomas Cort 
380c745990SThomas Cort #ifndef lint
390c745990SThomas Cort #if 0
400c745990SThomas Cort static char sccsid[] = "@(#)checknr.c	8.1 (Berkeley) 6/6/93";
410c745990SThomas Cort #else
42*84d9c625SLionel Sambuc __RCSID("$NetBSD: checknr.c,v 1.24 2013/08/12 14:03:18 joerg Exp $");
430c745990SThomas Cort #endif
440c745990SThomas Cort #endif /* not lint */
450c745990SThomas Cort 
460c745990SThomas Cort /*
470c745990SThomas Cort  * checknr: check an nroff/troff input file for matching macro calls.
480c745990SThomas Cort  * we also attempt to match size and font changes, but only the embedded
490c745990SThomas Cort  * kind.  These must end in \s0 and \fP resp.  Maybe more sophistication
500c745990SThomas Cort  * later but for now think of these restrictions as contributions to
510c745990SThomas Cort  * structured typesetting.
520c745990SThomas Cort  */
530c745990SThomas Cort #include <ctype.h>
540c745990SThomas Cort #include <err.h>
550c745990SThomas Cort #include <stdio.h>
560c745990SThomas Cort #include <stdlib.h>
570c745990SThomas Cort #include <string.h>
580c745990SThomas Cort 
590c745990SThomas Cort #define MAXSTK	100	/* Stack size */
600c745990SThomas Cort #define MAXBR	100	/* Max number of bracket pairs known */
610c745990SThomas Cort #define MAXCMDS	500	/* Max number of commands known */
620c745990SThomas Cort 
630c745990SThomas Cort /*
640c745990SThomas Cort  * The stack on which we remember what we've seen so far.
650c745990SThomas Cort  */
66*84d9c625SLionel Sambuc static struct stkstr {
670c745990SThomas Cort 	int opno;	/* number of opening bracket */
680c745990SThomas Cort 	int pl;		/* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */
690c745990SThomas Cort 	int parm;	/* parm to size, font, etc */
700c745990SThomas Cort 	int lno;	/* line number the thing came in in */
710c745990SThomas Cort } stk[MAXSTK];
72*84d9c625SLionel Sambuc static int stktop;
730c745990SThomas Cort 
740c745990SThomas Cort /*
750c745990SThomas Cort  * The kinds of opening and closing brackets.
760c745990SThomas Cort  */
77*84d9c625SLionel Sambuc static struct brstr {
78*84d9c625SLionel Sambuc 	const char *opbr;
79*84d9c625SLionel Sambuc 	const char *clbr;
800c745990SThomas Cort } br[MAXBR] = {
810c745990SThomas Cort 	/* A few bare bones troff commands */
820c745990SThomas Cort #define SZ	0
830c745990SThomas Cort 	{ "sz",	"sz"},	/* also \s */
840c745990SThomas Cort #define FT	1
850c745990SThomas Cort 	{ "ft",	"ft"},	/* also \f */
860c745990SThomas Cort 	/* the -mm package */
870c745990SThomas Cort 	{"AL",	"LE"},
880c745990SThomas Cort 	{"AS",	"AE"},
890c745990SThomas Cort 	{"BL",	"LE"},
900c745990SThomas Cort 	{"BS",	"BE"},
910c745990SThomas Cort 	{"DF",	"DE"},
920c745990SThomas Cort 	{"DL",	"LE"},
930c745990SThomas Cort 	{"DS",	"DE"},
940c745990SThomas Cort 	{"FS",	"FE"},
950c745990SThomas Cort 	{"ML",	"LE"},
960c745990SThomas Cort 	{"NS",	"NE"},
970c745990SThomas Cort 	{"RL",	"LE"},
980c745990SThomas Cort 	{"VL",	"LE"},
990c745990SThomas Cort 	/* the -ms package */
1000c745990SThomas Cort 	{"AB",	"AE"},
1010c745990SThomas Cort 	{"BD",	"DE"},
1020c745990SThomas Cort 	{"CD",	"DE"},
1030c745990SThomas Cort 	{"DS",	"DE"},
1040c745990SThomas Cort 	{"FS",	"FE"},
1050c745990SThomas Cort 	{"ID",	"DE"},
1060c745990SThomas Cort 	{"KF",	"KE"},
1070c745990SThomas Cort 	{"KS",	"KE"},
1080c745990SThomas Cort 	{"LD",	"DE"},
1090c745990SThomas Cort 	{"LG",	"NL"},
1100c745990SThomas Cort 	{"QS",	"QE"},
1110c745990SThomas Cort 	{"RS",	"RE"},
1120c745990SThomas Cort 	{"SM",	"NL"},
1130c745990SThomas Cort 	{"XA",	"XE"},
1140c745990SThomas Cort 	{"XS",	"XE"},
1150c745990SThomas Cort 	/* The -me package */
1160c745990SThomas Cort 	{"(b",	")b"},
1170c745990SThomas Cort 	{"(c",	")c"},
1180c745990SThomas Cort 	{"(d",	")d"},
1190c745990SThomas Cort 	{"(f",	")f"},
1200c745990SThomas Cort 	{"(l",	")l"},
1210c745990SThomas Cort 	{"(q",	")q"},
1220c745990SThomas Cort 	{"(x",	")x"},
1230c745990SThomas Cort 	{"(z",	")z"},
1240c745990SThomas Cort 	/* The -mdoc package */
1250c745990SThomas Cort 	{"Ao",  "Ac"},
1260c745990SThomas Cort 	{"Bd",  "Ed"},
1270c745990SThomas Cort 	{"Bk",  "Ek"},
1280c745990SThomas Cort 	{"Bo",  "Bc"},
1290c745990SThomas Cort 	{"Do",  "Dc"},
1300c745990SThomas Cort 	{"Fo",  "Fc"},
1310c745990SThomas Cort 	{"Oo",  "Oc"},
1320c745990SThomas Cort 	{"Po",  "Pc"},
1330c745990SThomas Cort 	{"Qo",  "Qc"},
1340c745990SThomas Cort 	{"Rs",  "Re"},
1350c745990SThomas Cort 	{"So",  "Sc"},
1360c745990SThomas Cort 	{"Xo",  "Xc"},
1370c745990SThomas Cort 	/* Things needed by preprocessors */
1380c745990SThomas Cort 	{"EQ",	"EN"},
1390c745990SThomas Cort 	{"TS",	"TE"},
1400c745990SThomas Cort 	/* Refer */
1410c745990SThomas Cort 	{"[",	"]"},
1420c745990SThomas Cort 	{0,	0}
1430c745990SThomas Cort };
1440c745990SThomas Cort 
1450c745990SThomas Cort /*
1460c745990SThomas Cort  * All commands known to nroff, plus macro packages.
1470c745990SThomas Cort  * Used so we can complain about unrecognized commands.
1480c745990SThomas Cort  */
149*84d9c625SLionel Sambuc static const char *knowncmds[MAXCMDS] = {
1500c745990SThomas Cort "$c", "$f", "$h", "$p", "$s", "%A", "%B", "%C", "%D", "%I", "%J", "%N",
1510c745990SThomas Cort "%O", "%P", "%Q", "%R", "%T", "%V", "(b", "(c", "(d", "(f", "(l", "(q",
1520c745990SThomas Cort "(t", "(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x",
1530c745990SThomas Cort ")z", "++", "+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D",
1540c745990SThomas Cort "@F", "@I", "@M", "@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p",
1550c745990SThomas Cort "@r", "@t", "@z", "AB", "AE", "AF", "AI", "AL", "AM", "AS", "AT",
1560c745990SThomas Cort "AU", "AX", "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "B" ,  "B1",
1570c745990SThomas Cort "B2", "BD", "BE", "BG", "BL", "BS", "BT", "BX", "Bc", "Bd", "Bf",
1580c745990SThomas Cort "Bk", "Bl", "Bo", "Bq", "Bsx", "Bx", "C1", "C2", "CD", "CM", "CT",
1590c745990SThomas Cort "Cd", "Cm", "D" , "D1", "DA", "DE", "DF", "DL", "DS", "DT", "Db", "Dc",
1600c745990SThomas Cort "Dd", "Dl", "Do", "Dq", "Dt", "Dv", "EC", "EF", "EG", "EH", "EM",
1610c745990SThomas Cort "EN", "EQ", "EX", "Ec", "Ed", "Ef", "Ek", "El", "Em", "Eo", "Er",
1620c745990SThomas Cort "Ev", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", "FQ",
1630c745990SThomas Cort "FS", "FV", "FX", "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Ft", "Fx",
1640c745990SThomas Cort "H" , "HC", "HD", "HM", "HO", "HU", "I" , "ID", "IE", "IH", "IM",
1650c745990SThomas Cort "IP", "IX", "IZ", "Ic", "In", "It", "KD", "KE", "KF", "KQ", "KS", "LB",
1660c745990SThomas Cort "LC", "LD", "LE", "LG", "LI", "LP", "Lb", "Li", "MC", "ME", "MF",
1670c745990SThomas Cort "MH", "ML", "MR", "MT", "ND", "NE", "NH", "NL", "NP", "NS", "Nd",
1680c745990SThomas Cort "Nm", "No", "Ns", "Nx", "OF", "OH", "OK", "OP", "Oc", "Oo", "Op",
1690c745990SThomas Cort "Os", "Ot", "Ox", "P" , "P1", "PF", "PH", "PP", "PT", "PX", "PY",
1700c745990SThomas Cort "Pa", "Pc", "Pf", "Po", "Pp", "Pq", "QE", "QP", "QS", "Qc", "Ql",
1710c745990SThomas Cort "Qo", "Qq", "R" , "RA", "RC", "RE", "RL", "RP", "RQ", "RS", "RT",
1720c745990SThomas Cort "Re", "Rs", "S" , "S0", "S2", "S3", "SA", "SG", "SH", "SK", "SM",
1730c745990SThomas Cort "SP", "SY", "Sc", "Sh", "Sm", "So", "Sq", "Ss", "St", "Sx", "Sy",
1740c745990SThomas Cort "T&", "TA", "TB", "TC", "TD", "TE", "TH", "TL", "TM", "TP", "TQ",
1750c745990SThomas Cort "TR", "TS", "TX", "Tn", "UL", "US", "UX", "Ud", "Ux", "VL", "Va", "Vt",
1760c745990SThomas Cort "WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "Xc", "Xo",
1770c745990SThomas Cort "Xr", "[" , "[-", "[0", "[1", "[2", "[3", "[4", "[5", "[<", "[>",
1780c745990SThomas Cort "[]", "\\{", "\\}", "]" , "]-", "]<", "]>", "][", "ab", "ac", "ad", "af", "am",
1790c745990SThomas Cort "ar", "as", "b" , "ba", "bc", "bd", "bi", "bl", "bp", "br", "bx",
1800c745990SThomas Cort "c.", "c2", "cc", "ce", "cf", "ch", "cs", "ct", "cu", "da", "de",
1810c745990SThomas Cort "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec", "ef", "eh", "el",
1820c745990SThomas Cort "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo", "fp", "ft",
1830c745990SThomas Cort "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i" , "ie",
1840c745990SThomas Cort "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln",
1850c745990SThomas Cort "lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo",
1860c745990SThomas Cort "n1", "n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr",
1870c745990SThomas Cort "ns", "nx", "of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn",
1880c745990SThomas Cort "po", "pp", "ps", "q" , "r" , "rb", "rd", "re", "rm", "rn", "ro",
1890c745990SThomas Cort "rr", "rs", "rt", "sb", "sc", "sh", "sk", "so", "sp", "ss", "st",
1900c745990SThomas Cort "sv", "sz", "ta", "tc", "th", "ti", "tl", "tm", "tp", "tr", "u",
1910c745990SThomas Cort "uf", "uh", "ul", "vs", "wh", "xp", "yr", 0
1920c745990SThomas Cort };
1930c745990SThomas Cort 
194*84d9c625SLionel Sambuc static int lineno;		/* current line number in input file */
195*84d9c625SLionel Sambuc static const char *cfilename;	/* name of current file */
196*84d9c625SLionel Sambuc static int nfiles;		/* number of files to process */
197*84d9c625SLionel Sambuc static int fflag;		/* -f: ignore \f */
198*84d9c625SLionel Sambuc static int sflag;		/* -s: ignore \s */
199*84d9c625SLionel Sambuc static int ncmds;		/* size of knowncmds */
200*84d9c625SLionel Sambuc static int slot;		/* slot in knowncmds found by binsrch */
2010c745990SThomas Cort 
202*84d9c625SLionel Sambuc static void addcmd(char *);
203*84d9c625SLionel Sambuc static void addmac(const char *);
204*84d9c625SLionel Sambuc static int binsrch(const char *);
205*84d9c625SLionel Sambuc static void checkknown(const char *);
206*84d9c625SLionel Sambuc static void chkcmd(const char *);
207*84d9c625SLionel Sambuc static void complain(int);
208*84d9c625SLionel Sambuc static int eq(const char *, const char *);
209*84d9c625SLionel Sambuc static void nomatch(const char *);
210*84d9c625SLionel Sambuc static void pe(int);
211*84d9c625SLionel Sambuc static void process(FILE *);
212*84d9c625SLionel Sambuc static void prop(int);
213*84d9c625SLionel Sambuc static void usage(void) __dead;
2140c745990SThomas Cort 
2150c745990SThomas Cort int
main(int argc,char ** argv)2160c745990SThomas Cort main(int argc, char **argv)
2170c745990SThomas Cort {
2180c745990SThomas Cort 	FILE *f;
2190c745990SThomas Cort 	int i;
2200c745990SThomas Cort 	char *cp;
2210c745990SThomas Cort 	char b1[4];
2220c745990SThomas Cort 
2230c745990SThomas Cort 	/* Figure out how many known commands there are */
2240c745990SThomas Cort 	while (knowncmds[ncmds])
2250c745990SThomas Cort 		ncmds++;
2260c745990SThomas Cort 	while (argc > 1 && argv[1][0] == '-') {
2270c745990SThomas Cort 		switch(argv[1][1]) {
2280c745990SThomas Cort 
2290c745990SThomas Cort 		/* -a: add pairs of macros */
2300c745990SThomas Cort 		case 'a':
2310c745990SThomas Cort 			i = strlen(argv[1]) - 2;
2320c745990SThomas Cort 			if (i % 6 != 0)
2330c745990SThomas Cort 				usage();
2340c745990SThomas Cort 			/* look for empty macro slots */
2350c745990SThomas Cort 			for (i=0; br[i].opbr; i++)
2360c745990SThomas Cort 				;
2370c745990SThomas Cort 			for (cp=argv[1]+3; cp[-1]; cp += 6) {
238*84d9c625SLionel Sambuc 				char *tmp;
239*84d9c625SLionel Sambuc 
2400c745990SThomas Cort 				if (i >= MAXBR)
2410c745990SThomas Cort 					errx(1, "too many pairs");
242*84d9c625SLionel Sambuc 				if ((tmp = malloc(3)) == NULL)
2430c745990SThomas Cort 					err(1, "malloc");
244*84d9c625SLionel Sambuc 				strlcpy(tmp, cp, 3);
245*84d9c625SLionel Sambuc 				br[i].opbr = tmp;
246*84d9c625SLionel Sambuc 				if ((tmp = malloc(3)) == NULL)
2470c745990SThomas Cort 					err(1, "malloc");
248*84d9c625SLionel Sambuc 				strlcpy(tmp, cp+3, 3);
249*84d9c625SLionel Sambuc 				br[i].clbr = tmp;
2500c745990SThomas Cort 				addmac(br[i].opbr);	/* knows pairs are also known cmds */
2510c745990SThomas Cort 				addmac(br[i].clbr);
2520c745990SThomas Cort 				i++;
2530c745990SThomas Cort 			}
2540c745990SThomas Cort 			break;
2550c745990SThomas Cort 
2560c745990SThomas Cort 		/* -c: add known commands */
2570c745990SThomas Cort 		case 'c':
2580c745990SThomas Cort 			i = strlen(argv[1]) - 2;
2590c745990SThomas Cort 			if (i % 3 != 0)
2600c745990SThomas Cort 				usage();
2610c745990SThomas Cort 			for (cp=argv[1]+3; cp[-1]; cp += 3) {
2620c745990SThomas Cort 				if (cp[2] && cp[2] != '.')
2630c745990SThomas Cort 					usage();
2640c745990SThomas Cort 				strncpy(b1, cp, 2);
2650c745990SThomas Cort 				addmac(b1);
2660c745990SThomas Cort 			}
2670c745990SThomas Cort 			break;
2680c745990SThomas Cort 
2690c745990SThomas Cort 		/* -f: ignore font changes */
2700c745990SThomas Cort 		case 'f':
2710c745990SThomas Cort 			fflag = 1;
2720c745990SThomas Cort 			break;
2730c745990SThomas Cort 
2740c745990SThomas Cort 		/* -s: ignore size changes */
2750c745990SThomas Cort 		case 's':
2760c745990SThomas Cort 			sflag = 1;
2770c745990SThomas Cort 			break;
2780c745990SThomas Cort 		default:
2790c745990SThomas Cort 			usage();
2800c745990SThomas Cort 		}
2810c745990SThomas Cort 		argc--; argv++;
2820c745990SThomas Cort 	}
2830c745990SThomas Cort 
2840c745990SThomas Cort 	nfiles = argc - 1;
2850c745990SThomas Cort 
2860c745990SThomas Cort 	if (nfiles > 0) {
2870c745990SThomas Cort 		for (i=1; i<argc; i++) {
2880c745990SThomas Cort 			cfilename = argv[i];
2890c745990SThomas Cort 			f = fopen(cfilename, "r");
2900c745990SThomas Cort 			if (f == NULL)
2910c745990SThomas Cort 				perror(cfilename);
2920c745990SThomas Cort 			else {
2930c745990SThomas Cort 				process(f);
2940c745990SThomas Cort 				fclose(f);
2950c745990SThomas Cort 			}
2960c745990SThomas Cort 		}
2970c745990SThomas Cort 	} else {
2980c745990SThomas Cort 		cfilename = "stdin";
2990c745990SThomas Cort 		process(stdin);
3000c745990SThomas Cort 	}
3010c745990SThomas Cort 	exit(0);
3020c745990SThomas Cort }
3030c745990SThomas Cort 
304*84d9c625SLionel Sambuc static void
usage(void)3050c745990SThomas Cort usage(void)
3060c745990SThomas Cort {
3070c745990SThomas Cort 	(void)fprintf(stderr,
3080c745990SThomas Cort 	    "usage: %s [-fs] [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] file\n",
3090c745990SThomas Cort 	    getprogname());
3100c745990SThomas Cort 	exit(1);
3110c745990SThomas Cort }
3120c745990SThomas Cort 
313*84d9c625SLionel Sambuc static void
process(FILE * f)3140c745990SThomas Cort process(FILE *f)
3150c745990SThomas Cort {
3160c745990SThomas Cort 	int i, n;
3170c745990SThomas Cort 	char line[256];	/* the current line */
3180c745990SThomas Cort 	char mac[5];	/* The current macro or nroff command */
3190c745990SThomas Cort 	int pl;
3200c745990SThomas Cort 
3210c745990SThomas Cort 	stktop = -1;
3220c745990SThomas Cort 	for (lineno = 1; fgets(line, sizeof line, f); lineno++) {
3230c745990SThomas Cort 		if (line[0] == '.') {
3240c745990SThomas Cort 			/*
3250c745990SThomas Cort 			 * find and isolate the macro/command name.
3260c745990SThomas Cort 			 */
3270c745990SThomas Cort 			strncpy(mac, line+1, 4);
3280c745990SThomas Cort 			if (isspace((unsigned char)mac[0])) {
3290c745990SThomas Cort 				pe(lineno);
3300c745990SThomas Cort 				printf("Empty command\n");
3310c745990SThomas Cort 			} else if (isspace((unsigned char)mac[1])) {
3320c745990SThomas Cort 				mac[1] = 0;
3330c745990SThomas Cort 			} else if (isspace((unsigned char)mac[2])) {
3340c745990SThomas Cort 				mac[2] = 0;
3350c745990SThomas Cort 			} else if (mac[0] != '\\' || mac[1] != '\"') {
3360c745990SThomas Cort 				pe(lineno);
3370c745990SThomas Cort 				printf("Command too long\n");
3380c745990SThomas Cort 			}
3390c745990SThomas Cort 
3400c745990SThomas Cort 			/*
3410c745990SThomas Cort 			 * Is it a known command?
3420c745990SThomas Cort 			 */
3430c745990SThomas Cort 			checkknown(mac);
3440c745990SThomas Cort 
3450c745990SThomas Cort 			/*
3460c745990SThomas Cort 			 * Should we add it?
3470c745990SThomas Cort 			 */
3480c745990SThomas Cort 			if (eq(mac, "de"))
3490c745990SThomas Cort 				addcmd(line);
3500c745990SThomas Cort 
351*84d9c625SLionel Sambuc 			chkcmd(mac);
3520c745990SThomas Cort 		}
3530c745990SThomas Cort 
3540c745990SThomas Cort 		/*
3550c745990SThomas Cort 		 * At this point we process the line looking
3560c745990SThomas Cort 		 * for \s and \f.
3570c745990SThomas Cort 		 */
3580c745990SThomas Cort 		for (i=0; line[i]; i++)
3590c745990SThomas Cort 			if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) {
3600c745990SThomas Cort 				if (!sflag && line[++i]=='s') {
3610c745990SThomas Cort 					pl = line[++i];
3620c745990SThomas Cort 					if (isdigit((unsigned char)pl)) {
3630c745990SThomas Cort 						n = pl - '0';
3640c745990SThomas Cort 						pl = ' ';
3650c745990SThomas Cort 					} else
3660c745990SThomas Cort 						n = 0;
3670c745990SThomas Cort 					while (isdigit((unsigned char)line[++i]))
3680c745990SThomas Cort 						n = 10 * n + line[i] - '0';
3690c745990SThomas Cort 					i--;
3700c745990SThomas Cort 					if (n == 0) {
3710c745990SThomas Cort 						if (stktop >= 0 &&
3720c745990SThomas Cort 						    stk[stktop].opno == SZ) {
3730c745990SThomas Cort 							stktop--;
3740c745990SThomas Cort 						} else {
3750c745990SThomas Cort 							pe(lineno);
3760c745990SThomas Cort 							printf("unmatched \\s0\n");
3770c745990SThomas Cort 						}
3780c745990SThomas Cort 					} else {
3790c745990SThomas Cort 						stk[++stktop].opno = SZ;
3800c745990SThomas Cort 						stk[stktop].pl = pl;
3810c745990SThomas Cort 						stk[stktop].parm = n;
3820c745990SThomas Cort 						stk[stktop].lno = lineno;
3830c745990SThomas Cort 					}
3840c745990SThomas Cort 				} else if (!fflag && line[i]=='f') {
3850c745990SThomas Cort 					n = line[++i];
3860c745990SThomas Cort 					if (n == 'P') {
3870c745990SThomas Cort 						if (stktop >= 0 &&
3880c745990SThomas Cort 						    stk[stktop].opno == FT) {
3890c745990SThomas Cort 							stktop--;
3900c745990SThomas Cort 						} else {
3910c745990SThomas Cort 							pe(lineno);
3920c745990SThomas Cort 							printf("unmatched \\fP\n");
3930c745990SThomas Cort 						}
3940c745990SThomas Cort 					} else {
3950c745990SThomas Cort 						stk[++stktop].opno = FT;
3960c745990SThomas Cort 						stk[stktop].pl = 1;
3970c745990SThomas Cort 						stk[stktop].parm = n;
3980c745990SThomas Cort 						stk[stktop].lno = lineno;
3990c745990SThomas Cort 					}
4000c745990SThomas Cort 				}
4010c745990SThomas Cort 			}
4020c745990SThomas Cort 	}
4030c745990SThomas Cort 	/*
4040c745990SThomas Cort 	 * We've hit the end and look at all this stuff that hasn't been
4050c745990SThomas Cort 	 * matched yet!  Complain, complain.
4060c745990SThomas Cort 	 */
4070c745990SThomas Cort 	for (i=stktop; i>=0; i--) {
4080c745990SThomas Cort 		complain(i);
4090c745990SThomas Cort 	}
4100c745990SThomas Cort }
4110c745990SThomas Cort 
412*84d9c625SLionel Sambuc static void
complain(int i)4130c745990SThomas Cort complain(int i)
4140c745990SThomas Cort {
4150c745990SThomas Cort 	pe(stk[i].lno);
4160c745990SThomas Cort 	printf("Unmatched ");
4170c745990SThomas Cort 	prop(i);
4180c745990SThomas Cort 	printf("\n");
4190c745990SThomas Cort }
4200c745990SThomas Cort 
421*84d9c625SLionel Sambuc static void
prop(int i)4220c745990SThomas Cort prop(int i)
4230c745990SThomas Cort {
4240c745990SThomas Cort 	if (stk[i].pl == 0)
4250c745990SThomas Cort 		printf(".%s", br[stk[i].opno].opbr);
4260c745990SThomas Cort 	else switch(stk[i].opno) {
4270c745990SThomas Cort 	case SZ:
4280c745990SThomas Cort 		printf("\\s%c%d", stk[i].pl, stk[i].parm);
4290c745990SThomas Cort 		break;
4300c745990SThomas Cort 	case FT:
4310c745990SThomas Cort 		printf("\\f%c", stk[i].parm);
4320c745990SThomas Cort 		break;
4330c745990SThomas Cort 	default:
4340c745990SThomas Cort 		printf("Bug: stk[%d].opno = %d = .%s, .%s",
4350c745990SThomas Cort 			i, stk[i].opno, br[stk[i].opno].opbr,
4360c745990SThomas Cort 			br[stk[i].opno].clbr);
4370c745990SThomas Cort 	}
4380c745990SThomas Cort }
4390c745990SThomas Cort 
440*84d9c625SLionel Sambuc static void
chkcmd(const char * mac)441*84d9c625SLionel Sambuc chkcmd(const char *mac)
4420c745990SThomas Cort {
4430c745990SThomas Cort 	int i;
4440c745990SThomas Cort 
4450c745990SThomas Cort 	/*
4460c745990SThomas Cort 	 * Check to see if it matches top of stack.
4470c745990SThomas Cort 	 */
4480c745990SThomas Cort 	if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr))
4490c745990SThomas Cort 		stktop--;	/* OK. Pop & forget */
4500c745990SThomas Cort 	else {
4510c745990SThomas Cort 		/* No. Maybe it's an opener */
4520c745990SThomas Cort 		for (i=0; br[i].opbr; i++) {
4530c745990SThomas Cort 			if (eq(mac, br[i].opbr)) {
4540c745990SThomas Cort 				/* Found. Push it. */
4550c745990SThomas Cort 				stktop++;
4560c745990SThomas Cort 				stk[stktop].opno = i;
4570c745990SThomas Cort 				stk[stktop].pl = 0;
4580c745990SThomas Cort 				stk[stktop].parm = 0;
4590c745990SThomas Cort 				stk[stktop].lno = lineno;
4600c745990SThomas Cort 				break;
4610c745990SThomas Cort 			}
4620c745990SThomas Cort 			/*
4630c745990SThomas Cort 			 * Maybe it's an unmatched closer.
4640c745990SThomas Cort 			 * NOTE: this depends on the fact
4650c745990SThomas Cort 			 * that none of the closers can be
4660c745990SThomas Cort 			 * openers too.
4670c745990SThomas Cort 			 */
4680c745990SThomas Cort 			if (eq(mac, br[i].clbr)) {
4690c745990SThomas Cort 				nomatch(mac);
4700c745990SThomas Cort 				break;
4710c745990SThomas Cort 			}
4720c745990SThomas Cort 		}
4730c745990SThomas Cort 	}
4740c745990SThomas Cort }
4750c745990SThomas Cort 
476*84d9c625SLionel Sambuc static void
nomatch(const char * mac)477*84d9c625SLionel Sambuc nomatch(const char *mac)
4780c745990SThomas Cort {
4790c745990SThomas Cort 	int i, j;
4800c745990SThomas Cort 
4810c745990SThomas Cort 	/*
4820c745990SThomas Cort 	 * Look for a match further down on stack
4830c745990SThomas Cort 	 * If we find one, it suggests that the stuff in
4840c745990SThomas Cort 	 * between is supposed to match itself.
4850c745990SThomas Cort 	 */
4860c745990SThomas Cort 	for (j=stktop; j>=0; j--)
4870c745990SThomas Cort 		if (eq(mac,br[stk[j].opno].clbr)) {
4880c745990SThomas Cort 			/* Found.  Make a good diagnostic. */
4890c745990SThomas Cort 			if (j == stktop-2) {
4900c745990SThomas Cort 				/*
4910c745990SThomas Cort 				 * Check for special case \fx..\fR and don't
4920c745990SThomas Cort 				 * complain.
4930c745990SThomas Cort 				 */
4940c745990SThomas Cort 				if (stk[j+1].opno==FT && stk[j+1].parm!='R'
4950c745990SThomas Cort 				 && stk[j+2].opno==FT && stk[j+2].parm=='R') {
4960c745990SThomas Cort 					stktop = j -1;
4970c745990SThomas Cort 					return;
4980c745990SThomas Cort 				}
4990c745990SThomas Cort 				/*
5000c745990SThomas Cort 				 * We have two unmatched frobs.  Chances are
5010c745990SThomas Cort 				 * they were intended to match, so we mention
5020c745990SThomas Cort 				 * them together.
5030c745990SThomas Cort 				 */
5040c745990SThomas Cort 				pe(stk[j+1].lno);
5050c745990SThomas Cort 				prop(j+1);
5060c745990SThomas Cort 				printf(" does not match %d: ", stk[j+2].lno);
5070c745990SThomas Cort 				prop(j+2);
5080c745990SThomas Cort 				printf("\n");
5090c745990SThomas Cort 			} else for (i=j+1; i <= stktop; i++) {
5100c745990SThomas Cort 				complain(i);
5110c745990SThomas Cort 			}
5120c745990SThomas Cort 			stktop = j-1;
5130c745990SThomas Cort 			return;
5140c745990SThomas Cort 		}
5150c745990SThomas Cort 	/* Didn't find one.  Throw this away. */
5160c745990SThomas Cort 	pe(lineno);
5170c745990SThomas Cort 	printf("Unmatched .%s\n", mac);
5180c745990SThomas Cort }
5190c745990SThomas Cort 
5200c745990SThomas Cort /* eq: are two strings equal? */
521*84d9c625SLionel Sambuc static int
eq(const char * s1,const char * s2)522*84d9c625SLionel Sambuc eq(const char *s1, const char *s2)
5230c745990SThomas Cort {
524*84d9c625SLionel Sambuc 	return strcmp(s1, s2) == 0;
5250c745990SThomas Cort }
5260c745990SThomas Cort 
5270c745990SThomas Cort /* print the first part of an error message, given the line number */
528*84d9c625SLionel Sambuc static void
pe(int pelineno)5290c745990SThomas Cort pe(int pelineno)
5300c745990SThomas Cort {
5310c745990SThomas Cort 	if (nfiles > 1)
5320c745990SThomas Cort 		printf("%s: ", cfilename);
5330c745990SThomas Cort 	printf("%d: ", pelineno);
5340c745990SThomas Cort }
5350c745990SThomas Cort 
536*84d9c625SLionel Sambuc static void
checkknown(const char * mac)537*84d9c625SLionel Sambuc checkknown(const char *mac)
5380c745990SThomas Cort {
5390c745990SThomas Cort 
5400c745990SThomas Cort 	if (eq(mac, "."))
5410c745990SThomas Cort 		return;
5420c745990SThomas Cort 	if (binsrch(mac) >= 0)
5430c745990SThomas Cort 		return;
5440c745990SThomas Cort 	if (mac[0] == '\\' && mac[1] == '"')	/* comments */
5450c745990SThomas Cort 		return;
5460c745990SThomas Cort 
5470c745990SThomas Cort 	pe(lineno);
5480c745990SThomas Cort 	printf("Unknown command: .%s\n", mac);
5490c745990SThomas Cort }
5500c745990SThomas Cort 
5510c745990SThomas Cort /*
5520c745990SThomas Cort  * We have a .de xx line in "line".  Add xx to the list of known commands.
5530c745990SThomas Cort  */
554*84d9c625SLionel Sambuc static void
addcmd(char * line)5550c745990SThomas Cort addcmd(char *line)
5560c745990SThomas Cort {
5570c745990SThomas Cort 	char *mac;
5580c745990SThomas Cort 
5590c745990SThomas Cort 	/* grab the macro being defined */
5600c745990SThomas Cort 	mac = line+4;
5610c745990SThomas Cort 	while (isspace((unsigned char)*mac))
5620c745990SThomas Cort 		mac++;
5630c745990SThomas Cort 	if (*mac == 0) {
5640c745990SThomas Cort 		pe(lineno);
5650c745990SThomas Cort 		printf("illegal define: %s\n", line);
5660c745990SThomas Cort 		return;
5670c745990SThomas Cort 	}
5680c745990SThomas Cort 	mac[2] = 0;
5690c745990SThomas Cort 	if (isspace((unsigned char)mac[1]) || mac[1] == '\\')
5700c745990SThomas Cort 		mac[1] = 0;
5710c745990SThomas Cort 	if (ncmds >= MAXCMDS) {
5720c745990SThomas Cort 		printf("Only %d known commands allowed\n", MAXCMDS);
5730c745990SThomas Cort 		exit(1);
5740c745990SThomas Cort 	}
5750c745990SThomas Cort 	addmac(mac);
5760c745990SThomas Cort }
5770c745990SThomas Cort 
5780c745990SThomas Cort /*
5790c745990SThomas Cort  * Add mac to the list.  We should really have some kind of tree
5800c745990SThomas Cort  * structure here but this is a quick-and-dirty job and I just don't
5810c745990SThomas Cort  * have time to mess with it.  (I wonder if this will come back to haunt
5820c745990SThomas Cort  * me someday?)  Anyway, I claim that .de is fairly rare in user
5830c745990SThomas Cort  * nroff programs, and the register loop below is pretty fast.
5840c745990SThomas Cort  */
585*84d9c625SLionel Sambuc static void
addmac(const char * mac)586*84d9c625SLionel Sambuc addmac(const char *mac)
5870c745990SThomas Cort {
588*84d9c625SLionel Sambuc 	const char **src, **dest, **loc;
5890c745990SThomas Cort 
5900c745990SThomas Cort 	if (binsrch(mac) >= 0){	/* it's OK to redefine something */
5910c745990SThomas Cort #ifdef DEBUG
5920c745990SThomas Cort 		printf("binsrch(%s) -> already in table\n", mac);
5930c745990SThomas Cort #endif /* DEBUG */
5940c745990SThomas Cort 		return;
5950c745990SThomas Cort 	}
5960c745990SThomas Cort 	/* binsrch sets slot as a side effect */
5970c745990SThomas Cort #ifdef DEBUG
5980c745990SThomas Cort 	printf("binsrch(%s) -> %d\n", mac, slot);
5990c745990SThomas Cort #endif
6000c745990SThomas Cort 	loc = &knowncmds[slot];
6010c745990SThomas Cort 	src = &knowncmds[ncmds-1];
6020c745990SThomas Cort 	dest = src+1;
6030c745990SThomas Cort 	while (dest > loc)
6040c745990SThomas Cort 		*dest-- = *src--;
6050c745990SThomas Cort 	if ((*loc = strdup(mac)) == NULL)
6060c745990SThomas Cort 		err(1, "strdup");
6070c745990SThomas Cort 	ncmds++;
6080c745990SThomas Cort #ifdef DEBUG
6090c745990SThomas Cort 	printf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2],
6100c745990SThomas Cort 	    knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1],
6110c745990SThomas Cort 	    knowncmds[slot+2], ncmds);
6120c745990SThomas Cort #endif
6130c745990SThomas Cort }
6140c745990SThomas Cort 
6150c745990SThomas Cort /*
6160c745990SThomas Cort  * Do a binary search in knowncmds for mac.
6170c745990SThomas Cort  * If found, return the index.  If not, return -1.
6180c745990SThomas Cort  */
619*84d9c625SLionel Sambuc static int
binsrch(const char * mac)620*84d9c625SLionel Sambuc binsrch(const char *mac)
6210c745990SThomas Cort {
622*84d9c625SLionel Sambuc 	const char *p;	/* pointer to current cmd in list */
6230c745990SThomas Cort 	int d;		/* difference if any */
6240c745990SThomas Cort 	int mid;	/* mid point in binary search */
6250c745990SThomas Cort 	int top, bot;	/* boundaries of bin search, inclusive */
6260c745990SThomas Cort 
6270c745990SThomas Cort 	top = ncmds-1;
6280c745990SThomas Cort 	bot = 0;
6290c745990SThomas Cort 	while (top >= bot) {
6300c745990SThomas Cort 		mid = (top+bot)/2;
6310c745990SThomas Cort 		p = knowncmds[mid];
6320c745990SThomas Cort 		d = p[0] - mac[0];
6330c745990SThomas Cort 		if (d == 0)
6340c745990SThomas Cort 			d = p[1] - mac[1];
6350c745990SThomas Cort 		if (d == 0)
6360c745990SThomas Cort 			return mid;
6370c745990SThomas Cort 		if (d < 0)
6380c745990SThomas Cort 			bot = mid + 1;
6390c745990SThomas Cort 		else
6400c745990SThomas Cort 			top = mid - 1;
6410c745990SThomas Cort 	}
6420c745990SThomas Cort 	slot = bot;	/* place it would have gone */
6430c745990SThomas Cort 	return -1;
6440c745990SThomas Cort }
645