checknr.c revision 282438
11541Srgrimes/* 21541Srgrimes * Copyright (c) 1980, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 4. Neither the name of the University nor the names of its contributors 141541Srgrimes * may be used to endorse or promote products derived from this software 151541Srgrimes * without specific prior written permission. 161541Srgrimes * 171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271541Srgrimes * SUCH DAMAGE. 281541Srgrimes */ 291541Srgrimes 301541Srgrimes#ifndef lint 311541Srgrimesstatic const char copyright[] = 321541Srgrimes"@(#) Copyright (c) 1980, 1993\n\ 3322521Sdyson The Regents of the University of California. All rights reserved.\n"; 3429353Speter#endif /* not lint */ 351541Srgrimes 3622521Sdyson#if 0 3722521Sdyson#ifndef lint 3822521Sdysonstatic char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93"; 3922521Sdyson#endif /* not lint */ 4022521Sdyson#endif 4122521Sdyson 4222521Sdyson#include <sys/cdefs.h> 4322521Sdyson__FBSDID("$FreeBSD: head/usr.bin/checknr/checknr.c 282438 2015-05-04 22:18:58Z bapt $"); 4422521Sdyson 4522521Sdyson/* 4622521Sdyson * checknr: check an nroff/troff input file for matching macro calls. 4722521Sdyson * we also attempt to match size and font changes, but only the embedded 4822521Sdyson * kind. These must end in \s0 and \fP resp. Maybe more sophistication 4922521Sdyson * later but for now think of these restrictions as contributions to 5022521Sdyson * structured typesetting. 5122521Sdyson */ 5222521Sdyson#include <err.h> 5322521Sdyson#include <stdio.h> 5422521Sdyson#include <stdlib.h> 5522521Sdyson#include <string.h> 5622521Sdyson#include <ctype.h> 5722521Sdyson 5822521Sdyson#define MAXSTK 100 /* Stack size */ 5922521Sdyson#define MAXBR 100 /* Max number of bracket pairs known */ 6022521Sdyson#define MAXCMDS 500 /* Max number of commands known */ 611541Srgrimes 621541Srgrimesstatic void addcmd(char *); 631541Srgrimesstatic void addmac(const char *); 641541Srgrimesstatic int binsrch(const char *); 651541Srgrimesstatic void checkknown(const char *); 661541Srgrimesstatic void chkcmd(const char *, const char *); 6722521Sdysonstatic void complain(int); 6828732Sphkstatic int eq(const char *, const char *); 6928732Sphkstatic void nomatch(const char *); 7028732Sphkstatic void pe(int); 7128732Sphkstatic void process(FILE *); 7228732Sphkstatic void prop(int); 7328732Sphkstatic void usage(void); 7428732Sphk 7528732Sphk/* 7628732Sphk * The stack on which we remember what we've seen so far. 7728732Sphk */ 7828732Sphkstatic struct stkstr { 7928732Sphk int opno; /* number of opening bracket */ 8022521Sdyson int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ 8122521Sdyson int parm; /* parm to size, font, etc */ 8222521Sdyson int lno; /* line number */ 831541Srgrimes} stk[MAXSTK]; 841541Srgrimesstatic int stktop; 851541Srgrimes 861541Srgrimes/* 871541Srgrimes * The kinds of opening and closing brackets. 881541Srgrimes */ 891541Srgrimesstatic struct brstr { 9022521Sdyson const char *opbr; 9122521Sdyson const char *clbr; 9222521Sdyson} br[MAXBR] = { 9322521Sdyson /* A few bare bones troff commands */ 9422521Sdyson#define SZ 0 9522521Sdyson {"sz", "sz"}, /* also \s */ 9622521Sdyson#define FT 1 9722521Sdyson {"ft", "ft"}, /* also \f */ 9822521Sdyson /* the -mm package */ 9922521Sdyson {"AL", "LE"}, 10022521Sdyson {"AS", "AE"}, 10122521Sdyson {"BL", "LE"}, 10222521Sdyson {"BS", "BE"}, 10322521Sdyson {"DF", "DE"}, 10422521Sdyson {"DL", "LE"}, 1051541Srgrimes {"DS", "DE"}, 1061541Srgrimes {"FS", "FE"}, 1071541Srgrimes {"ML", "LE"}, 1081541Srgrimes {"NS", "NE"}, 1091541Srgrimes {"RL", "LE"}, 1101541Srgrimes {"VL", "LE"}, 1111541Srgrimes /* the -ms package */ 11222521Sdyson {"AB", "AE"}, 11322521Sdyson {"BD", "DE"}, 11422521Sdyson {"CD", "DE"}, 1151541Srgrimes {"DS", "DE"}, 1161541Srgrimes {"FS", "FE"}, 1171541Srgrimes {"ID", "DE"}, 1181541Srgrimes {"KF", "KE"}, 1191541Srgrimes {"KS", "KE"}, 1201541Srgrimes {"LD", "DE"}, 1211541Srgrimes {"LG", "NL"}, 12222521Sdyson {"QS", "QE"}, 12322521Sdyson {"RS", "RE"}, 12422521Sdyson {"SM", "NL"}, 1251541Srgrimes {"XA", "XE"}, 1261541Srgrimes {"XS", "XE"}, 1271541Srgrimes /* The -me package */ 1281541Srgrimes {"(b", ")b"}, 1291541Srgrimes {"(c", ")c"}, 1301541Srgrimes {"(d", ")d"}, 1311541Srgrimes {"(f", ")f"}, 13222521Sdyson {"(l", ")l"}, 13322521Sdyson {"(q", ")q"}, 13422521Sdyson {"(x", ")x"}, 1351541Srgrimes {"(z", ")z"}, 1361541Srgrimes /* Things needed by preprocessors */ 1371541Srgrimes {"EQ", "EN"}, 1381541Srgrimes {"TS", "TE"}, 1391541Srgrimes /* Refer */ 1401541Srgrimes {"[", "]"}, 1411541Srgrimes {0, 0} 14222521Sdyson}; 14322521Sdyson 14422521Sdyson/* 1451541Srgrimes * All commands known to nroff, plus macro packages. 1461541Srgrimes * Used so we can complain about unrecognized commands. 1471541Srgrimes */ 1481541Srgrimesstatic const char *knowncmds[MAXCMDS] = { 1491541Srgrimes"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t", 1501541Srgrimes"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++", 1511541Srgrimes"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M", 15222521Sdyson"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB", 15322521Sdyson"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B", "B1", "B2", 15422521Sdyson"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT", 1551541Srgrimes"D", "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM", 1561541Srgrimes"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", 1571541Srgrimes"FQ", "FS", "FV", "FX", "H", "HC", "HD", "HM", "HO", "HU", "I", "ID", 1581541Srgrimes"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB", 1591541Srgrimes"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR", 1601541Srgrimes"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P", 1611541Srgrimes"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R", "RA", 16222521Sdyson"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S", "S0", "S2", "S3", "SA", 16322521Sdyson"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE", 16422521Sdyson"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL", 1651541Srgrimes"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[", "[-", "[0", 1661541Srgrimes"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]", "]-", "]<", "]>", 1671541Srgrimes"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b", "ba", "bc", "bd", 1681541Srgrimes"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", 1691541Srgrimes"chop", "cs", "ct", "cu", "da", "de", "di", "dl", "dn", "do", "ds", 1701541Srgrimes"dt", "dw", "dy", "ec", "ef", "eh", "el", "em", "eo", "ep", "ev", 1711541Srgrimes"evc", "ex", "fallback", "fc", "feature", "fi", "fl", "flig", "fo", 17222521Sdyson"fp", "ft", "ftr", "fz", "fzoom", "hc", "he", "hidechar", "hl", "hp", 17322521Sdyson"ht", "hw", "hx", "hy", "hylang", "i", "ie", "if", "ig", "in", "ip", 17422521Sdyson"it", "ix", "kern", "kernafter", "kernbefore", "kernpair", "lc", "lg", 1751541Srgrimes"lhang", "lc_ctype", "li", "ll", "ln", "lo", "lp", "ls", "lt", "m1", 1761541Srgrimes"m2", "m3", "m4", "mc", "mk", "mo", "n1", "n2", "na", "ne", "nf", "nh", 1771541Srgrimes"nl", "nm", "nn", "np", "nr", "ns", "nx", "of", "oh", "os", "pa", 1781541Srgrimes"papersize", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps", "q", 1791541Srgrimes"r", "rb", "rd", "re", "recursionlimit", "return", "rhang", "rm", 1801541Srgrimes"rn", "ro", "rr", "rs", "rt", "sb", "sc", "sh", "shift", "sk", "so", 1811541Srgrimes"sp", "ss", "st", "sv", "sz", "ta", "tc", "th", "ti", "tl", "tm", "tp", 18222521Sdyson"tr", "track", "u", "uf", "uh", "ul", "vs", "wh", "xflag", "xp", "yr", 18322521Sdyson0 18422521Sdyson}; 18522521Sdyson 18622521Sdysonstatic int lineno; /* current line number in input file */ 18722521Sdysonstatic const char *cfilename; /* name of current file */ 18822521Sdysonstatic int nfiles; /* number of files to process */ 18922521Sdysonstatic int fflag; /* -f: ignore \f */ 19022521Sdysonstatic int sflag; /* -s: ignore \s */ 19122521Sdysonstatic int ncmds; /* size of knowncmds */ 19222521Sdysonstatic int slot; /* slot in knowncmds found by binsrch */ 19322521Sdyson 19422521Sdysonint 1951541Srgrimesmain(int argc, char **argv) 1961541Srgrimes{ 19722521Sdyson FILE *f; 1981541Srgrimes int i; 1991541Srgrimes char *cp; 2001541Srgrimes char b1[4]; 2011541Srgrimes 2021541Srgrimes /* Figure out how many known commands there are */ 2031541Srgrimes while (knowncmds[ncmds]) 20422521Sdyson ncmds++; 20529353Speter while (argc > 1 && argv[1][0] == '-') { 20622521Sdyson switch(argv[1][1]) { 20729353Speter 2081541Srgrimes /* -a: add pairs of macros */ 20929353Speter case 'a': 2101541Srgrimes i = strlen(argv[1]) - 2; 2111541Srgrimes if (i % 6 != 0) 2121541Srgrimes usage(); 2131541Srgrimes /* look for empty macro slots */ 21422521Sdyson for (i=0; br[i].opbr; i++) 21522521Sdyson ; 21622521Sdyson for (cp=argv[1]+3; cp[-1]; cp += 6) { 21722521Sdyson br[i].opbr = strncpy(malloc(3), cp, 2); 21822521Sdyson br[i].clbr = strncpy(malloc(3), cp+3, 2); 21922521Sdyson addmac(br[i].opbr); /* knows pairs are also known cmds */ 22022521Sdyson addmac(br[i].clbr); 22122521Sdyson i++; 22222521Sdyson } 22322521Sdyson break; 22422521Sdyson 2251541Srgrimes /* -c: add known commands */ 2261541Srgrimes case 'c': 2271541Srgrimes i = strlen(argv[1]) - 2; 2281541Srgrimes if (i % 3 != 0) 2291541Srgrimes usage(); 2301541Srgrimes for (cp=argv[1]+3; cp[-1]; cp += 3) { 2311541Srgrimes if (cp[2] && cp[2] != '.') 23222521Sdyson usage(); 23322521Sdyson strncpy(b1, cp, 2); 23422521Sdyson b1[2] = '\0'; 2351541Srgrimes addmac(b1); 2361541Srgrimes } 2371541Srgrimes break; 2381541Srgrimes 2391541Srgrimes /* -f: ignore font changes */ 2401541Srgrimes case 'f': 2411541Srgrimes fflag = 1; 24222521Sdyson break; 24322521Sdyson 24422521Sdyson /* -s: ignore size changes */ 24522521Sdyson case 's': 2461541Srgrimes sflag = 1; 2471541Srgrimes break; 2481541Srgrimes default: 2491541Srgrimes usage(); 2501541Srgrimes } 2511541Srgrimes argc--; argv++; 2521541Srgrimes } 25322521Sdyson 25422521Sdyson nfiles = argc - 1; 25522521Sdyson 25622521Sdyson if (nfiles > 0) { 2571541Srgrimes for (i = 1; i < argc; i++) { 2581541Srgrimes cfilename = argv[i]; 2591541Srgrimes f = fopen(cfilename, "r"); 2601541Srgrimes if (f == NULL) 2611541Srgrimes warn("%s", cfilename); 2621541Srgrimes else { 26322521Sdyson process(f); 26422521Sdyson fclose(f); 26522521Sdyson } 26622521Sdyson } 2679842Sdg } else { 2689842Sdg cfilename = "stdin"; 2699842Sdg process(stdin); 2709842Sdg } 2719842Sdg exit(0); 2721541Srgrimes} 27322521Sdyson 27422521Sdysonstatic void 27522521Sdysonusage(void) 27622521Sdyson{ 27722521Sdyson fprintf(stderr, 27822521Sdyson "usage: checknr [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [-s] [-f] file\n"); 2791541Srgrimes exit(1); 2801541Srgrimes} 2811541Srgrimes 2821541Srgrimesstatic void 2831541Srgrimesprocess(FILE *f) 2841541Srgrimes{ 2851541Srgrimes int i, n; 2861541Srgrimes char mac[5]; /* The current macro or nroff command */ 2871541Srgrimes int pl; 28822521Sdyson static char line[256]; /* the current line */ 28922521Sdyson 29022521Sdyson stktop = -1; 29122521Sdyson for (lineno = 1; fgets(line, sizeof line, f); lineno++) { 2921541Srgrimes if (line[0] == '.') { 2931541Srgrimes /* 2941541Srgrimes * find and isolate the macro/command name. 2951541Srgrimes */ 2961541Srgrimes strncpy(mac, line+1, 4); 2971541Srgrimes if (isspace(mac[0])) { 2981541Srgrimes pe(lineno); 29922521Sdyson printf("Empty command\n"); 30022521Sdyson } else if (isspace(mac[1])) { 30122521Sdyson mac[1] = 0; 30222521Sdyson } else if (isspace(mac[2])) { 3031541Srgrimes mac[2] = 0; 3041541Srgrimes } else if (mac[0] != '\\' || mac[1] != '\"') { 3051541Srgrimes pe(lineno); 3061541Srgrimes printf("Command too long\n"); 3071541Srgrimes } 3081541Srgrimes 30922521Sdyson /* 31022521Sdyson * Is it a known command? 31122521Sdyson */ 31222521Sdyson checkknown(mac); 31322521Sdyson 31422521Sdyson /* 31522521Sdyson * Should we add it? 31622521Sdyson */ 3171541Srgrimes if (eq(mac, "de")) 3181541Srgrimes addcmd(line); 3191541Srgrimes 3201541Srgrimes chkcmd(line, mac); 3211541Srgrimes } 3221541Srgrimes 3231541Srgrimes /* 3241541Srgrimes * At this point we process the line looking 32522521Sdyson * for \s and \f. 32622521Sdyson */ 32722521Sdyson for (i = 0; line[i]; i++) 3281541Srgrimes if (line[i] == '\\' && (i == 0 || line[i-1] != '\\')) { 3291541Srgrimes if (!sflag && line[++i] == 's') { 3301541Srgrimes pl = line[++i]; 3311541Srgrimes if (isdigit(pl)) { 3323167Sdfr n = pl - '0'; 33322521Sdyson pl = ' '; 33422521Sdyson } else 3351541Srgrimes n = 0; 3361541Srgrimes while (isdigit(line[++i])) 33722521Sdyson n = 10 * n + line[i] - '0'; 33822521Sdyson i--; 33922521Sdyson if (n == 0) { 3401541Srgrimes if (stk[stktop].opno == SZ) { 3411541Srgrimes stktop--; 3421541Srgrimes } else { 3431541Srgrimes pe(lineno); 3441541Srgrimes printf("unmatched \\s0\n"); 3451541Srgrimes } 34622521Sdyson } else { 34722521Sdyson stk[++stktop].opno = SZ; 34822521Sdyson stk[stktop].pl = pl; 3491541Srgrimes stk[stktop].parm = n; 3501541Srgrimes stk[stktop].lno = lineno; 3511541Srgrimes } 3521541Srgrimes } else if (!fflag && line[i] == 'f') { 3531541Srgrimes n = line[++i]; 35422521Sdyson if (n == 'P') { 35522521Sdyson if (stk[stktop].opno == FT) { 35622521Sdyson stktop--; 3571541Srgrimes } else { 3581541Srgrimes pe(lineno); 35922521Sdyson printf("unmatched \\fP\n"); 3601541Srgrimes } 3611541Srgrimes } else { 36222521Sdyson stk[++stktop].opno = FT; 36322521Sdyson stk[stktop].pl = 1; 36422521Sdyson stk[stktop].parm = n; 3651541Srgrimes stk[stktop].lno = lineno; 3661541Srgrimes } 36722521Sdyson } 3681541Srgrimes } 3691541Srgrimes } 37022521Sdyson /* 37122521Sdyson * We've hit the end and look at all this stuff that hasn't been 37222521Sdyson * matched yet! Complain, complain. 3731541Srgrimes */ 3741541Srgrimes for (i = stktop; i >= 0; i--) { 37522521Sdyson complain(i); 37622521Sdyson } 3771541Srgrimes} 3781541Srgrimes 37922521Sdysonstatic void 38022521Sdysoncomplain(int i) 38122521Sdyson{ 3821541Srgrimes pe(stk[i].lno); 3831541Srgrimes printf("Unmatched "); 38422521Sdyson prop(i); 38522521Sdyson printf("\n"); 3861541Srgrimes} 3871541Srgrimes 38822521Sdysonstatic void 38922521Sdysonprop(int i) 39022521Sdyson{ 39122521Sdyson if (stk[i].pl == 0) 3921541Srgrimes printf(".%s", br[stk[i].opno].opbr); 3931541Srgrimes else switch (stk[i].opno) { 3941541Srgrimes case SZ: 3951541Srgrimes printf("\\s%c%d", stk[i].pl, stk[i].parm); 3961541Srgrimes break; 3971541Srgrimes case FT: 39810551Sdyson printf("\\f%c", stk[i].parm); 3991541Srgrimes break; 4001541Srgrimes default: 40122521Sdyson printf("Bug: stk[%d].opno = %d = .%s, .%s", 40222521Sdyson i, stk[i].opno, br[stk[i].opno].opbr, 40322521Sdyson br[stk[i].opno].clbr); 4041541Srgrimes } 4051541Srgrimes} 4061541Srgrimes 4071541Srgrimesstatic void 40822521Sdysonchkcmd(const char *line __unused, const char *mac) 40922521Sdyson{ 41022521Sdyson int i; 4111541Srgrimes 4121541Srgrimes /* 4131541Srgrimes * Check to see if it matches top of stack. 4141541Srgrimes */ 41522521Sdyson if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) 41622521Sdyson stktop--; /* OK. Pop & forget */ 41722521Sdyson else { 4181541Srgrimes /* No. Maybe it's an opener */ 4191541Srgrimes for (i=0; br[i].opbr; i++) { 4201541Srgrimes if (eq(mac, br[i].opbr)) { 4211541Srgrimes /* Found. Push it. */ 42222521Sdyson stktop++; 42322521Sdyson stk[stktop].opno = i; 42422521Sdyson stk[stktop].pl = 0; 4251541Srgrimes stk[stktop].parm = 0; 4261541Srgrimes stk[stktop].lno = lineno; 4271541Srgrimes break; 42822521Sdyson } 4291541Srgrimes /* 4301541Srgrimes * Maybe it's an unmatched closer. 43122521Sdyson * NOTE: this depends on the fact 43222521Sdyson * that none of the closers can be 43322521Sdyson * openers too. 4341541Srgrimes */ 4351541Srgrimes if (eq(mac, br[i].clbr)) { 4361541Srgrimes nomatch(mac); 4371541Srgrimes break; 4381541Srgrimes } 4391541Srgrimes } 4401541Srgrimes } 4411541Srgrimes} 44222521Sdyson 44322521Sdysonstatic void 44422521Sdysonnomatch(const char *mac) 4451541Srgrimes{ 4461541Srgrimes int i, j; 4471541Srgrimes 4481541Srgrimes /* 4491541Srgrimes * Look for a match further down on stack 4501541Srgrimes * If we find one, it suggests that the stuff in 4511541Srgrimes * between is supposed to match itself. 45222521Sdyson */ 45322521Sdyson for (j=stktop; j>=0; j--) 45422521Sdyson if (eq(mac,br[stk[j].opno].clbr)) { 4551541Srgrimes /* Found. Make a good diagnostic. */ 4561541Srgrimes if (j == stktop-2) { 4571541Srgrimes /* 4581541Srgrimes * Check for special case \fx..\fR and don't 4591541Srgrimes * complain. 4601541Srgrimes */ 4611541Srgrimes if (stk[j+1].opno==FT && stk[j+1].parm!='R' 46222521Sdyson && stk[j+2].opno==FT && stk[j+2].parm=='R') { 46322521Sdyson stktop = j -1; 46422521Sdyson return; 4651541Srgrimes } 4661541Srgrimes /* 4671541Srgrimes * We have two unmatched frobs. Chances are 4681541Srgrimes * they were intended to match, so we mention 4691541Srgrimes * them together. 47022521Sdyson */ 47122521Sdyson pe(stk[j+1].lno); 47222521Sdyson prop(j+1); 4731541Srgrimes printf(" does not match %d: ", stk[j+2].lno); 4741541Srgrimes prop(j+2); 4751541Srgrimes printf("\n"); 4761541Srgrimes } else for (i=j+1; i <= stktop; i++) { 4771541Srgrimes complain(i); 4781541Srgrimes } 47922521Sdyson stktop = j-1; 48022521Sdyson return; 48122521Sdyson } 4821541Srgrimes /* Didn't find one. Throw this away. */ 4831541Srgrimes pe(lineno); 4841541Srgrimes printf("Unmatched .%s\n", mac); 4851541Srgrimes} 4861541Srgrimes 4871541Srgrimes/* eq: are two strings equal? */ 4881541Srgrimesstatic int 4891541Srgrimeseq(const char *s1, const char *s2) 49022521Sdyson{ 49122521Sdyson return (strcmp(s1, s2) == 0); 49222521Sdyson} 4931541Srgrimes 4941541Srgrimes/* print the first part of an error message, given the line number */ 4951541Srgrimesstatic void 4961541Srgrimespe(int linen) 4971541Srgrimes{ 4981541Srgrimes if (nfiles > 1) 4991541Srgrimes printf("%s: ", cfilename); 50010551Sdyson printf("%d: ", linen); 50110551Sdyson} 50210551Sdyson 50310551Sdysonstatic void 50410551Sdysoncheckknown(const char *mac) 50512767Sdyson{ 50610551Sdyson 50710551Sdyson if (eq(mac, ".")) 50810551Sdyson return; 50910551Sdyson if (binsrch(mac) >= 0) 51010551Sdyson return; 51110551Sdyson if (mac[0] == '\\' && mac[1] == '"') /* comments */ 51210551Sdyson return; 51310551Sdyson 51412767Sdyson pe(lineno); 51510551Sdyson printf("Unknown command: .%s\n", mac); 51611704Sdyson} 51722521Sdyson 5181541Srgrimes/* 51922521Sdyson * We have a .de xx line in "line". Add xx to the list of known commands. 5201541Srgrimes */ 5211541Srgrimesstatic void 5221541Srgrimesaddcmd(char *line) 523{ 524 char *mac; 525 526 /* grab the macro being defined */ 527 mac = line+4; 528 while (isspace(*mac)) 529 mac++; 530 if (*mac == 0) { 531 pe(lineno); 532 printf("illegal define: %s\n", line); 533 return; 534 } 535 mac[2] = 0; 536 if (isspace(mac[1]) || mac[1] == '\\') 537 mac[1] = 0; 538 if (ncmds >= MAXCMDS) { 539 printf("Only %d known commands allowed\n", MAXCMDS); 540 exit(1); 541 } 542 addmac(mac); 543} 544 545/* 546 * Add mac to the list. We should really have some kind of tree 547 * structure here but this is a quick-and-dirty job and I just don't 548 * have time to mess with it. (I wonder if this will come back to haunt 549 * me someday?) Anyway, I claim that .de is fairly rare in user 550 * nroff programs, and the register loop below is pretty fast. 551 */ 552static void 553addmac(const char *mac) 554{ 555 const char **src, **dest, **loc; 556 557 if (binsrch(mac) >= 0){ /* it's OK to redefine something */ 558#ifdef DEBUG 559 printf("binsrch(%s) -> already in table\n", mac); 560#endif 561 return; 562 } 563 /* binsrch sets slot as a side effect */ 564#ifdef DEBUG 565printf("binsrch(%s) -> %d\n", mac, slot); 566#endif 567 loc = &knowncmds[slot]; 568 src = &knowncmds[ncmds-1]; 569 dest = src+1; 570 while (dest > loc) 571 *dest-- = *src--; 572 *loc = strcpy(malloc(3), mac); 573 ncmds++; 574#ifdef DEBUG 575 printf("after: %s %s %s %s %s, %d cmds\n", 576 knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], 577 knowncmds[slot+1], knowncmds[slot+2], ncmds); 578#endif 579} 580 581/* 582 * Do a binary search in knowncmds for mac. 583 * If found, return the index. If not, return -1. 584 */ 585static int 586binsrch(const char *mac) 587{ 588 const char *p; /* pointer to current cmd in list */ 589 int d; /* difference if any */ 590 int mid; /* mid point in binary search */ 591 int top, bot; /* boundaries of bin search, inclusive */ 592 593 top = ncmds-1; 594 bot = 0; 595 while (top >= bot) { 596 mid = (top+bot)/2; 597 p = knowncmds[mid]; 598 d = p[0] - mac[0]; 599 if (d == 0) 600 d = p[1] - mac[1]; 601 if (d == 0) 602 return (mid); 603 if (d < 0) 604 bot = mid + 1; 605 else 606 top = mid - 1; 607 } 608 slot = bot; /* place it would have gone */ 609 return (-1); 610} 611