1*af153cbdSmrg /* $NetBSD: checknr.c,v 1.25 2021/04/13 01:38:04 mrg Exp $ */
2e91ed174Sglass
361f28255Scgd /*
4e91ed174Sglass * Copyright (c) 1980, 1993
5e91ed174Sglass * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
1589aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
325c1befb6Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3498e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
3598e5374cSlukem The Regents of the University of California. All rights reserved.");
3661f28255Scgd #endif /* not lint */
3761f28255Scgd
3861f28255Scgd #ifndef lint
39e91ed174Sglass #if 0
40e91ed174Sglass static char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93";
41e91ed174Sglass #else
42*af153cbdSmrg __RCSID("$NetBSD: checknr.c,v 1.25 2021/04/13 01:38:04 mrg Exp $");
43e91ed174Sglass #endif
4461f28255Scgd #endif /* not lint */
4561f28255Scgd
4661f28255Scgd /*
4761f28255Scgd * checknr: check an nroff/troff input file for matching macro calls.
4861f28255Scgd * we also attempt to match size and font changes, but only the embedded
4961f28255Scgd * kind. These must end in \s0 and \fP resp. Maybe more sophistication
5061f28255Scgd * later but for now think of these restrictions as contributions to
5161f28255Scgd * structured typesetting.
5261f28255Scgd */
5361f28255Scgd #include <ctype.h>
545ca972d9Sxtraeme #include <err.h>
555c1befb6Slukem #include <stdio.h>
565c1befb6Slukem #include <stdlib.h>
575c1befb6Slukem #include <string.h>
5861f28255Scgd
5961f28255Scgd #define MAXSTK 100 /* Stack size */
6061f28255Scgd #define MAXBR 100 /* Max number of bracket pairs known */
6161f28255Scgd #define MAXCMDS 500 /* Max number of commands known */
6261f28255Scgd
6361f28255Scgd /*
6461f28255Scgd * The stack on which we remember what we've seen so far.
6561f28255Scgd */
6606eef689Sdholland static struct stkstr {
6761f28255Scgd int opno; /* number of opening bracket */
6861f28255Scgd int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */
6961f28255Scgd int parm; /* parm to size, font, etc */
7061f28255Scgd int lno; /* line number the thing came in in */
7161f28255Scgd } stk[MAXSTK];
7206eef689Sdholland static int stktop;
7361f28255Scgd
7461f28255Scgd /*
7561f28255Scgd * The kinds of opening and closing brackets.
7661f28255Scgd */
7706eef689Sdholland static struct brstr {
78beced5eaSdholland const char *opbr;
79beced5eaSdholland const char *clbr;
8061f28255Scgd } br[MAXBR] = {
8161f28255Scgd /* A few bare bones troff commands */
8261f28255Scgd #define SZ 0
835c1befb6Slukem { "sz", "sz"}, /* also \s */
8461f28255Scgd #define FT 1
855c1befb6Slukem { "ft", "ft"}, /* also \f */
8661f28255Scgd /* the -mm package */
875c1befb6Slukem {"AL", "LE"},
885c1befb6Slukem {"AS", "AE"},
895c1befb6Slukem {"BL", "LE"},
905c1befb6Slukem {"BS", "BE"},
915c1befb6Slukem {"DF", "DE"},
925c1befb6Slukem {"DL", "LE"},
935c1befb6Slukem {"DS", "DE"},
945c1befb6Slukem {"FS", "FE"},
955c1befb6Slukem {"ML", "LE"},
965c1befb6Slukem {"NS", "NE"},
975c1befb6Slukem {"RL", "LE"},
985c1befb6Slukem {"VL", "LE"},
9961f28255Scgd /* the -ms package */
1005c1befb6Slukem {"AB", "AE"},
1015c1befb6Slukem {"BD", "DE"},
1025c1befb6Slukem {"CD", "DE"},
1035c1befb6Slukem {"DS", "DE"},
1045c1befb6Slukem {"FS", "FE"},
1055c1befb6Slukem {"ID", "DE"},
1065c1befb6Slukem {"KF", "KE"},
1075c1befb6Slukem {"KS", "KE"},
1085c1befb6Slukem {"LD", "DE"},
1095c1befb6Slukem {"LG", "NL"},
1105c1befb6Slukem {"QS", "QE"},
1115c1befb6Slukem {"RS", "RE"},
1125c1befb6Slukem {"SM", "NL"},
1135c1befb6Slukem {"XA", "XE"},
1145c1befb6Slukem {"XS", "XE"},
11561f28255Scgd /* The -me package */
1165c1befb6Slukem {"(b", ")b"},
1175c1befb6Slukem {"(c", ")c"},
1185c1befb6Slukem {"(d", ")d"},
1195c1befb6Slukem {"(f", ")f"},
1205c1befb6Slukem {"(l", ")l"},
1215c1befb6Slukem {"(q", ")q"},
1225c1befb6Slukem {"(x", ")x"},
1235c1befb6Slukem {"(z", ")z"},
1249cf6e5c4Swiz /* The -mdoc package */
1259cf6e5c4Swiz {"Ao", "Ac"},
1269cf6e5c4Swiz {"Bd", "Ed"},
1279cf6e5c4Swiz {"Bk", "Ek"},
1289cf6e5c4Swiz {"Bo", "Bc"},
1299cf6e5c4Swiz {"Do", "Dc"},
1309cf6e5c4Swiz {"Fo", "Fc"},
1319cf6e5c4Swiz {"Oo", "Oc"},
1329cf6e5c4Swiz {"Po", "Pc"},
1339cf6e5c4Swiz {"Qo", "Qc"},
1349cf6e5c4Swiz {"Rs", "Re"},
1359cf6e5c4Swiz {"So", "Sc"},
1369cf6e5c4Swiz {"Xo", "Xc"},
13761f28255Scgd /* Things needed by preprocessors */
1385c1befb6Slukem {"EQ", "EN"},
1395c1befb6Slukem {"TS", "TE"},
14061f28255Scgd /* Refer */
1415c1befb6Slukem {"[", "]"},
142bd02b4a3Swiz {0, 0}
14361f28255Scgd };
14461f28255Scgd
14561f28255Scgd /*
14661f28255Scgd * All commands known to nroff, plus macro packages.
14761f28255Scgd * Used so we can complain about unrecognized commands.
14861f28255Scgd */
14906eef689Sdholland static const char *knowncmds[MAXCMDS] = {
1509cf6e5c4Swiz "$c", "$f", "$h", "$p", "$s", "%A", "%B", "%C", "%D", "%I", "%J", "%N",
1519cf6e5c4Swiz "%O", "%P", "%Q", "%R", "%T", "%V", "(b", "(c", "(d", "(f", "(l", "(q",
1529cf6e5c4Swiz "(t", "(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x",
1539cf6e5c4Swiz ")z", "++", "+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D",
1549cf6e5c4Swiz "@F", "@I", "@M", "@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p",
1559cf6e5c4Swiz "@r", "@t", "@z", "AB", "AE", "AF", "AI", "AL", "AM", "AS", "AT",
1569cf6e5c4Swiz "AU", "AX", "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "B" , "B1",
1579cf6e5c4Swiz "B2", "BD", "BE", "BG", "BL", "BS", "BT", "BX", "Bc", "Bd", "Bf",
1589cf6e5c4Swiz "Bk", "Bl", "Bo", "Bq", "Bsx", "Bx", "C1", "C2", "CD", "CM", "CT",
1599cf6e5c4Swiz "Cd", "Cm", "D" , "D1", "DA", "DE", "DF", "DL", "DS", "DT", "Db", "Dc",
1609cf6e5c4Swiz "Dd", "Dl", "Do", "Dq", "Dt", "Dv", "EC", "EF", "EG", "EH", "EM",
1619cf6e5c4Swiz "EN", "EQ", "EX", "Ec", "Ed", "Ef", "Ek", "El", "Em", "Eo", "Er",
1629cf6e5c4Swiz "Ev", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", "FQ",
1639cf6e5c4Swiz "FS", "FV", "FX", "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Ft", "Fx",
1649cf6e5c4Swiz "H" , "HC", "HD", "HM", "HO", "HU", "I" , "ID", "IE", "IH", "IM",
165ec34356aSwiz "IP", "IX", "IZ", "Ic", "In", "It", "KD", "KE", "KF", "KQ", "KS", "LB",
1669cf6e5c4Swiz "LC", "LD", "LE", "LG", "LI", "LP", "Lb", "Li", "MC", "ME", "MF",
1679cf6e5c4Swiz "MH", "ML", "MR", "MT", "ND", "NE", "NH", "NL", "NP", "NS", "Nd",
1689cf6e5c4Swiz "Nm", "No", "Ns", "Nx", "OF", "OH", "OK", "OP", "Oc", "Oo", "Op",
1699cf6e5c4Swiz "Os", "Ot", "Ox", "P" , "P1", "PF", "PH", "PP", "PT", "PX", "PY",
1709cf6e5c4Swiz "Pa", "Pc", "Pf", "Po", "Pp", "Pq", "QE", "QP", "QS", "Qc", "Ql",
1719cf6e5c4Swiz "Qo", "Qq", "R" , "RA", "RC", "RE", "RL", "RP", "RQ", "RS", "RT",
1729cf6e5c4Swiz "Re", "Rs", "S" , "S0", "S2", "S3", "SA", "SG", "SH", "SK", "SM",
1739cf6e5c4Swiz "SP", "SY", "Sc", "Sh", "Sm", "So", "Sq", "Ss", "St", "Sx", "Sy",
1749cf6e5c4Swiz "T&", "TA", "TB", "TC", "TD", "TE", "TH", "TL", "TM", "TP", "TQ",
1759cf6e5c4Swiz "TR", "TS", "TX", "Tn", "UL", "US", "UX", "Ud", "Ux", "VL", "Va", "Vt",
1769cf6e5c4Swiz "WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "Xc", "Xo",
1779cf6e5c4Swiz "Xr", "[" , "[-", "[0", "[1", "[2", "[3", "[4", "[5", "[<", "[>",
1789cf6e5c4Swiz "[]", "\\{", "\\}", "]" , "]-", "]<", "]>", "][", "ab", "ac", "ad", "af", "am",
1799cf6e5c4Swiz "ar", "as", "b" , "ba", "bc", "bd", "bi", "bl", "bp", "br", "bx",
1809cf6e5c4Swiz "c.", "c2", "cc", "ce", "cf", "ch", "cs", "ct", "cu", "da", "de",
1819cf6e5c4Swiz "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec", "ef", "eh", "el",
1829cf6e5c4Swiz "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo", "fp", "ft",
1839cf6e5c4Swiz "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i" , "ie",
1849cf6e5c4Swiz "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln",
1859cf6e5c4Swiz "lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo",
1869cf6e5c4Swiz "n1", "n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr",
1879cf6e5c4Swiz "ns", "nx", "of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn",
1889cf6e5c4Swiz "po", "pp", "ps", "q" , "r" , "rb", "rd", "re", "rm", "rn", "ro",
1899cf6e5c4Swiz "rr", "rs", "rt", "sb", "sc", "sh", "sk", "so", "sp", "ss", "st",
1909cf6e5c4Swiz "sv", "sz", "ta", "tc", "th", "ti", "tl", "tm", "tp", "tr", "u",
1919cf6e5c4Swiz "uf", "uh", "ul", "vs", "wh", "xp", "yr", 0
19261f28255Scgd };
19361f28255Scgd
19406eef689Sdholland static int lineno; /* current line number in input file */
19506eef689Sdholland static const char *cfilename; /* name of current file */
19606eef689Sdholland static int nfiles; /* number of files to process */
19706eef689Sdholland static int fflag; /* -f: ignore \f */
19806eef689Sdholland static int sflag; /* -s: ignore \s */
19906eef689Sdholland static int ncmds; /* size of knowncmds */
20006eef689Sdholland static int slot; /* slot in knowncmds found by binsrch */
20161f28255Scgd
20206eef689Sdholland static void addcmd(char *);
20306eef689Sdholland static void addmac(const char *);
20406eef689Sdholland static int binsrch(const char *);
20506eef689Sdholland static void checkknown(const char *);
20606eef689Sdholland static void chkcmd(const char *);
20706eef689Sdholland static void complain(int);
208beced5eaSdholland static int eq(const char *, const char *);
20906eef689Sdholland static void nomatch(const char *);
21006eef689Sdholland static void pe(int);
21106eef689Sdholland static void process(FILE *);
21206eef689Sdholland static void prop(int);
2132c7fa373Sjoerg static void usage(void) __dead;
21461f28255Scgd
2155c1befb6Slukem int
main(int argc,char ** argv)216105896cdSwiz main(int argc, char **argv)
21761f28255Scgd {
21861f28255Scgd FILE *f;
21961f28255Scgd int i;
22061f28255Scgd char *cp;
22161f28255Scgd char b1[4];
22261f28255Scgd
22361f28255Scgd /* Figure out how many known commands there are */
22461f28255Scgd while (knowncmds[ncmds])
22561f28255Scgd ncmds++;
22661f28255Scgd while (argc > 1 && argv[1][0] == '-') {
22761f28255Scgd switch(argv[1][1]) {
22861f28255Scgd
22961f28255Scgd /* -a: add pairs of macros */
23061f28255Scgd case 'a':
23161f28255Scgd i = strlen(argv[1]) - 2;
23261f28255Scgd if (i % 6 != 0)
23361f28255Scgd usage();
23461f28255Scgd /* look for empty macro slots */
23561f28255Scgd for (i=0; br[i].opbr; i++)
23661f28255Scgd ;
23761f28255Scgd for (cp=argv[1]+3; cp[-1]; cp += 6) {
238beced5eaSdholland char *tmp;
239beced5eaSdholland
240bd02b4a3Swiz if (i >= MAXBR)
241bd02b4a3Swiz errx(1, "too many pairs");
242beced5eaSdholland if ((tmp = malloc(3)) == NULL)
243bd02b4a3Swiz err(1, "malloc");
244beced5eaSdholland strlcpy(tmp, cp, 3);
245beced5eaSdholland br[i].opbr = tmp;
246beced5eaSdholland if ((tmp = malloc(3)) == NULL)
247bd02b4a3Swiz err(1, "malloc");
248beced5eaSdholland strlcpy(tmp, cp+3, 3);
249beced5eaSdholland br[i].clbr = tmp;
25061f28255Scgd addmac(br[i].opbr); /* knows pairs are also known cmds */
25161f28255Scgd addmac(br[i].clbr);
25261f28255Scgd i++;
25361f28255Scgd }
25461f28255Scgd break;
25561f28255Scgd
25661f28255Scgd /* -c: add known commands */
25761f28255Scgd case 'c':
25861f28255Scgd i = strlen(argv[1]) - 2;
25961f28255Scgd if (i % 3 != 0)
26061f28255Scgd usage();
26161f28255Scgd for (cp=argv[1]+3; cp[-1]; cp += 3) {
26261f28255Scgd if (cp[2] && cp[2] != '.')
26361f28255Scgd usage();
26461f28255Scgd strncpy(b1, cp, 2);
26561f28255Scgd addmac(b1);
26661f28255Scgd }
26761f28255Scgd break;
26861f28255Scgd
26961f28255Scgd /* -f: ignore font changes */
27061f28255Scgd case 'f':
27161f28255Scgd fflag = 1;
27261f28255Scgd break;
27361f28255Scgd
27461f28255Scgd /* -s: ignore size changes */
27561f28255Scgd case 's':
27661f28255Scgd sflag = 1;
27761f28255Scgd break;
27861f28255Scgd default:
27961f28255Scgd usage();
28061f28255Scgd }
28161f28255Scgd argc--; argv++;
28261f28255Scgd }
28361f28255Scgd
28461f28255Scgd nfiles = argc - 1;
28561f28255Scgd
28661f28255Scgd if (nfiles > 0) {
28761f28255Scgd for (i=1; i<argc; i++) {
28861f28255Scgd cfilename = argv[i];
28961f28255Scgd f = fopen(cfilename, "r");
29061f28255Scgd if (f == NULL)
29161f28255Scgd perror(cfilename);
292de2f2d87Swiz else {
29361f28255Scgd process(f);
2949cf6e5c4Swiz fclose(f);
29561f28255Scgd }
296de2f2d87Swiz }
29761f28255Scgd } else {
29861f28255Scgd cfilename = "stdin";
29961f28255Scgd process(stdin);
30061f28255Scgd }
30161f28255Scgd exit(0);
30261f28255Scgd }
30361f28255Scgd
30406eef689Sdholland static void
usage(void)305105896cdSwiz usage(void)
30661f28255Scgd {
30755759098Swiz (void)fprintf(stderr,
30855759098Swiz "usage: %s [-fs] [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] file\n",
30955759098Swiz getprogname());
31061f28255Scgd exit(1);
31161f28255Scgd }
31261f28255Scgd
31306eef689Sdholland static void
process(FILE * f)314105896cdSwiz process(FILE *f)
31561f28255Scgd {
3165c1befb6Slukem int i, n;
3171bbe14d8Swiz char line[256]; /* the current line */
31861f28255Scgd char mac[5]; /* The current macro or nroff command */
31961f28255Scgd int pl;
32061f28255Scgd
32161f28255Scgd stktop = -1;
32261f28255Scgd for (lineno = 1; fgets(line, sizeof line, f); lineno++) {
32361f28255Scgd if (line[0] == '.') {
32461f28255Scgd /*
32561f28255Scgd * find and isolate the macro/command name.
32661f28255Scgd */
32761f28255Scgd strncpy(mac, line+1, 4);
328*af153cbdSmrg mac[4] = '\0';
329bd5b624cSchristos if (isspace((unsigned char)mac[0])) {
33061f28255Scgd pe(lineno);
33161f28255Scgd printf("Empty command\n");
332bd5b624cSchristos } else if (isspace((unsigned char)mac[1])) {
33361f28255Scgd mac[1] = 0;
334bd5b624cSchristos } else if (isspace((unsigned char)mac[2])) {
33561f28255Scgd mac[2] = 0;
33661f28255Scgd } else if (mac[0] != '\\' || mac[1] != '\"') {
33761f28255Scgd pe(lineno);
33861f28255Scgd printf("Command too long\n");
33961f28255Scgd }
34061f28255Scgd
34161f28255Scgd /*
34261f28255Scgd * Is it a known command?
34361f28255Scgd */
34461f28255Scgd checkknown(mac);
34561f28255Scgd
34661f28255Scgd /*
34761f28255Scgd * Should we add it?
34861f28255Scgd */
34961f28255Scgd if (eq(mac, "de"))
35061f28255Scgd addcmd(line);
35161f28255Scgd
352043c758bSdholland chkcmd(mac);
35361f28255Scgd }
35461f28255Scgd
35561f28255Scgd /*
35661f28255Scgd * At this point we process the line looking
35761f28255Scgd * for \s and \f.
35861f28255Scgd */
35961f28255Scgd for (i=0; line[i]; i++)
36061f28255Scgd if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) {
36161f28255Scgd if (!sflag && line[++i]=='s') {
36261f28255Scgd pl = line[++i];
363bd5b624cSchristos if (isdigit((unsigned char)pl)) {
36461f28255Scgd n = pl - '0';
36561f28255Scgd pl = ' ';
36661f28255Scgd } else
36761f28255Scgd n = 0;
368bd5b624cSchristos while (isdigit((unsigned char)line[++i]))
36961f28255Scgd n = 10 * n + line[i] - '0';
37061f28255Scgd i--;
37161f28255Scgd if (n == 0) {
372095e4da2Schristos if (stktop >= 0 &&
373095e4da2Schristos stk[stktop].opno == SZ) {
37461f28255Scgd stktop--;
37561f28255Scgd } else {
37661f28255Scgd pe(lineno);
37761f28255Scgd printf("unmatched \\s0\n");
37861f28255Scgd }
37961f28255Scgd } else {
38061f28255Scgd stk[++stktop].opno = SZ;
38161f28255Scgd stk[stktop].pl = pl;
38261f28255Scgd stk[stktop].parm = n;
38361f28255Scgd stk[stktop].lno = lineno;
38461f28255Scgd }
38561f28255Scgd } else if (!fflag && line[i]=='f') {
38661f28255Scgd n = line[++i];
38761f28255Scgd if (n == 'P') {
3884d5030b7Schristos if (stktop >= 0 &&
3894d5030b7Schristos stk[stktop].opno == FT) {
39061f28255Scgd stktop--;
39161f28255Scgd } else {
39261f28255Scgd pe(lineno);
39361f28255Scgd printf("unmatched \\fP\n");
39461f28255Scgd }
39561f28255Scgd } else {
39661f28255Scgd stk[++stktop].opno = FT;
39761f28255Scgd stk[stktop].pl = 1;
39861f28255Scgd stk[stktop].parm = n;
39961f28255Scgd stk[stktop].lno = lineno;
40061f28255Scgd }
40161f28255Scgd }
40261f28255Scgd }
40361f28255Scgd }
40461f28255Scgd /*
40561f28255Scgd * We've hit the end and look at all this stuff that hasn't been
40661f28255Scgd * matched yet! Complain, complain.
40761f28255Scgd */
40861f28255Scgd for (i=stktop; i>=0; i--) {
40961f28255Scgd complain(i);
41061f28255Scgd }
41161f28255Scgd }
41261f28255Scgd
41306eef689Sdholland static void
complain(int i)414105896cdSwiz complain(int i)
41561f28255Scgd {
41661f28255Scgd pe(stk[i].lno);
41761f28255Scgd printf("Unmatched ");
41861f28255Scgd prop(i);
41961f28255Scgd printf("\n");
42061f28255Scgd }
42161f28255Scgd
42206eef689Sdholland static void
prop(int i)423105896cdSwiz prop(int i)
42461f28255Scgd {
42561f28255Scgd if (stk[i].pl == 0)
42661f28255Scgd printf(".%s", br[stk[i].opno].opbr);
42761f28255Scgd else switch(stk[i].opno) {
42861f28255Scgd case SZ:
42961f28255Scgd printf("\\s%c%d", stk[i].pl, stk[i].parm);
43061f28255Scgd break;
43161f28255Scgd case FT:
43261f28255Scgd printf("\\f%c", stk[i].parm);
43361f28255Scgd break;
43461f28255Scgd default:
43561f28255Scgd printf("Bug: stk[%d].opno = %d = .%s, .%s",
4365c1befb6Slukem i, stk[i].opno, br[stk[i].opno].opbr,
4375c1befb6Slukem br[stk[i].opno].clbr);
43861f28255Scgd }
43961f28255Scgd }
44061f28255Scgd
44106eef689Sdholland static void
chkcmd(const char * mac)442043c758bSdholland chkcmd(const char *mac)
44361f28255Scgd {
4445c1befb6Slukem int i;
44561f28255Scgd
44661f28255Scgd /*
44761f28255Scgd * Check to see if it matches top of stack.
44861f28255Scgd */
44961f28255Scgd if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr))
45061f28255Scgd stktop--; /* OK. Pop & forget */
45161f28255Scgd else {
45261f28255Scgd /* No. Maybe it's an opener */
45361f28255Scgd for (i=0; br[i].opbr; i++) {
45461f28255Scgd if (eq(mac, br[i].opbr)) {
45561f28255Scgd /* Found. Push it. */
45661f28255Scgd stktop++;
45761f28255Scgd stk[stktop].opno = i;
45861f28255Scgd stk[stktop].pl = 0;
45961f28255Scgd stk[stktop].parm = 0;
46061f28255Scgd stk[stktop].lno = lineno;
46161f28255Scgd break;
46261f28255Scgd }
46361f28255Scgd /*
46461f28255Scgd * Maybe it's an unmatched closer.
46561f28255Scgd * NOTE: this depends on the fact
46661f28255Scgd * that none of the closers can be
46761f28255Scgd * openers too.
46861f28255Scgd */
46961f28255Scgd if (eq(mac, br[i].clbr)) {
47061f28255Scgd nomatch(mac);
47161f28255Scgd break;
47261f28255Scgd }
47361f28255Scgd }
47461f28255Scgd }
47561f28255Scgd }
47661f28255Scgd
47706eef689Sdholland static void
nomatch(const char * mac)478043c758bSdholland nomatch(const char *mac)
47961f28255Scgd {
4805c1befb6Slukem int i, j;
48161f28255Scgd
48261f28255Scgd /*
48361f28255Scgd * Look for a match further down on stack
48461f28255Scgd * If we find one, it suggests that the stuff in
48561f28255Scgd * between is supposed to match itself.
48661f28255Scgd */
48761f28255Scgd for (j=stktop; j>=0; j--)
48861f28255Scgd if (eq(mac,br[stk[j].opno].clbr)) {
48961f28255Scgd /* Found. Make a good diagnostic. */
49061f28255Scgd if (j == stktop-2) {
49161f28255Scgd /*
49261f28255Scgd * Check for special case \fx..\fR and don't
49361f28255Scgd * complain.
49461f28255Scgd */
49561f28255Scgd if (stk[j+1].opno==FT && stk[j+1].parm!='R'
49661f28255Scgd && stk[j+2].opno==FT && stk[j+2].parm=='R') {
49761f28255Scgd stktop = j -1;
49861f28255Scgd return;
49961f28255Scgd }
50061f28255Scgd /*
50161f28255Scgd * We have two unmatched frobs. Chances are
50261f28255Scgd * they were intended to match, so we mention
50361f28255Scgd * them together.
50461f28255Scgd */
50561f28255Scgd pe(stk[j+1].lno);
50661f28255Scgd prop(j+1);
50761f28255Scgd printf(" does not match %d: ", stk[j+2].lno);
50861f28255Scgd prop(j+2);
50961f28255Scgd printf("\n");
51061f28255Scgd } else for (i=j+1; i <= stktop; i++) {
51161f28255Scgd complain(i);
51261f28255Scgd }
51361f28255Scgd stktop = j-1;
51461f28255Scgd return;
51561f28255Scgd }
51661f28255Scgd /* Didn't find one. Throw this away. */
51761f28255Scgd pe(lineno);
51861f28255Scgd printf("Unmatched .%s\n", mac);
51961f28255Scgd }
52061f28255Scgd
52161f28255Scgd /* eq: are two strings equal? */
522beced5eaSdholland static int
eq(const char * s1,const char * s2)523beced5eaSdholland eq(const char *s1, const char *s2)
52461f28255Scgd {
525beced5eaSdholland return strcmp(s1, s2) == 0;
52661f28255Scgd }
52761f28255Scgd
52861f28255Scgd /* print the first part of an error message, given the line number */
52906eef689Sdholland static void
pe(int pelineno)5301bbe14d8Swiz pe(int pelineno)
53161f28255Scgd {
53261f28255Scgd if (nfiles > 1)
53361f28255Scgd printf("%s: ", cfilename);
5341bbe14d8Swiz printf("%d: ", pelineno);
53561f28255Scgd }
53661f28255Scgd
53706eef689Sdholland static void
checkknown(const char * mac)538043c758bSdholland checkknown(const char *mac)
53961f28255Scgd {
54061f28255Scgd
54161f28255Scgd if (eq(mac, "."))
54261f28255Scgd return;
54361f28255Scgd if (binsrch(mac) >= 0)
54461f28255Scgd return;
54561f28255Scgd if (mac[0] == '\\' && mac[1] == '"') /* comments */
54661f28255Scgd return;
54761f28255Scgd
54861f28255Scgd pe(lineno);
54961f28255Scgd printf("Unknown command: .%s\n", mac);
55061f28255Scgd }
55161f28255Scgd
55261f28255Scgd /*
55361f28255Scgd * We have a .de xx line in "line". Add xx to the list of known commands.
55461f28255Scgd */
55506eef689Sdholland static void
addcmd(char * line)556105896cdSwiz addcmd(char *line)
55761f28255Scgd {
55861f28255Scgd char *mac;
55961f28255Scgd
56061f28255Scgd /* grab the macro being defined */
56161f28255Scgd mac = line+4;
562bd5b624cSchristos while (isspace((unsigned char)*mac))
56361f28255Scgd mac++;
56461f28255Scgd if (*mac == 0) {
56561f28255Scgd pe(lineno);
56661f28255Scgd printf("illegal define: %s\n", line);
56761f28255Scgd return;
56861f28255Scgd }
56961f28255Scgd mac[2] = 0;
570bd5b624cSchristos if (isspace((unsigned char)mac[1]) || mac[1] == '\\')
57161f28255Scgd mac[1] = 0;
57261f28255Scgd if (ncmds >= MAXCMDS) {
57361f28255Scgd printf("Only %d known commands allowed\n", MAXCMDS);
57461f28255Scgd exit(1);
57561f28255Scgd }
57661f28255Scgd addmac(mac);
57761f28255Scgd }
57861f28255Scgd
57961f28255Scgd /*
58061f28255Scgd * Add mac to the list. We should really have some kind of tree
58161f28255Scgd * structure here but this is a quick-and-dirty job and I just don't
58261f28255Scgd * have time to mess with it. (I wonder if this will come back to haunt
58361f28255Scgd * me someday?) Anyway, I claim that .de is fairly rare in user
58461f28255Scgd * nroff programs, and the register loop below is pretty fast.
58561f28255Scgd */
58606eef689Sdholland static void
addmac(const char * mac)587beced5eaSdholland addmac(const char *mac)
58861f28255Scgd {
589beced5eaSdholland const char **src, **dest, **loc;
59061f28255Scgd
59161f28255Scgd if (binsrch(mac) >= 0){ /* it's OK to redefine something */
59261f28255Scgd #ifdef DEBUG
59361f28255Scgd printf("binsrch(%s) -> already in table\n", mac);
594d594ce93Scgd #endif /* DEBUG */
59561f28255Scgd return;
59661f28255Scgd }
59761f28255Scgd /* binsrch sets slot as a side effect */
59861f28255Scgd #ifdef DEBUG
59961f28255Scgd printf("binsrch(%s) -> %d\n", mac, slot);
60061f28255Scgd #endif
60161f28255Scgd loc = &knowncmds[slot];
60261f28255Scgd src = &knowncmds[ncmds-1];
60361f28255Scgd dest = src+1;
60461f28255Scgd while (dest > loc)
60561f28255Scgd *dest-- = *src--;
606bd02b4a3Swiz if ((*loc = strdup(mac)) == NULL)
607bd02b4a3Swiz err(1, "strdup");
60861f28255Scgd ncmds++;
60961f28255Scgd #ifdef DEBUG
6101bbe14d8Swiz printf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2],
6111bbe14d8Swiz knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1],
6121bbe14d8Swiz knowncmds[slot+2], ncmds);
61361f28255Scgd #endif
61461f28255Scgd }
61561f28255Scgd
61661f28255Scgd /*
61761f28255Scgd * Do a binary search in knowncmds for mac.
61861f28255Scgd * If found, return the index. If not, return -1.
61961f28255Scgd */
62006eef689Sdholland static int
binsrch(const char * mac)621beced5eaSdholland binsrch(const char *mac)
62261f28255Scgd {
623beced5eaSdholland const char *p; /* pointer to current cmd in list */
6245c1befb6Slukem int d; /* difference if any */
6255c1befb6Slukem int mid; /* mid point in binary search */
6265c1befb6Slukem int top, bot; /* boundaries of bin search, inclusive */
62761f28255Scgd
62861f28255Scgd top = ncmds-1;
62961f28255Scgd bot = 0;
63061f28255Scgd while (top >= bot) {
63161f28255Scgd mid = (top+bot)/2;
63261f28255Scgd p = knowncmds[mid];
63361f28255Scgd d = p[0] - mac[0];
63461f28255Scgd if (d == 0)
63561f28255Scgd d = p[1] - mac[1];
63661f28255Scgd if (d == 0)
63761f28255Scgd return mid;
63861f28255Scgd if (d < 0)
63961f28255Scgd bot = mid + 1;
64061f28255Scgd else
64161f28255Scgd top = mid - 1;
64261f28255Scgd }
64361f28255Scgd slot = bot; /* place it would have gone */
64461f28255Scgd return -1;
64561f28255Scgd }
646