checknr.c revision 91389
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 3541568Sarchiestatic const 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 4091389Sdwmalone#if 0 411590Srgrimes#ifndef lint 4291389Sdwmalonestatic char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93"; 431590Srgrimes#endif /* not lint */ 4491389Sdwmalone#endif 451590Srgrimes 4691389Sdwmalone#include <sys/cdefs.h> 4791389Sdwmalone__FBSDID("$FreeBSD: head/usr.bin/checknr/checknr.c 91389 2002-02-27 15:49:07Z dwmalone $"); 4891389Sdwmalone 491590Srgrimes/* 501590Srgrimes * checknr: check an nroff/troff input file for matching macro calls. 511590Srgrimes * we also attempt to match size and font changes, but only the embedded 521590Srgrimes * kind. These must end in \s0 and \fP resp. Maybe more sophistication 531590Srgrimes * later but for now think of these restrictions as contributions to 541590Srgrimes * structured typesetting. 551590Srgrimes */ 561590Srgrimes#include <stdio.h> 5726881Scharnier#include <stdlib.h> 5826881Scharnier#include <string.h> 591590Srgrimes#include <ctype.h> 601590Srgrimes 611590Srgrimes#define MAXSTK 100 /* Stack size */ 621590Srgrimes#define MAXBR 100 /* Max number of bracket pairs known */ 631590Srgrimes#define MAXCMDS 500 /* Max number of commands known */ 641590Srgrimes 6526881Scharniervoid addcmd __P((char *)); 6691389Sdwmalonevoid addmac __P((const char *)); 6791389Sdwmaloneint binsrch __P((const char *)); 6826881Scharniervoid checkknown __P((char *)); 6926881Scharniervoid chkcmd __P((char *, char *)); 7026881Scharniervoid complain __P((int)); 7191389Sdwmaloneint eq __P((const char *, const char *)); 7226881Scharniervoid nomatch __P((char *)); 7326881Scharniervoid pe __P((int)); 7426881Scharniervoid process __P((FILE *)); 7526881Scharniervoid prop __P((int)); 7626881Scharnierstatic void usage __P((void)); 7726881Scharnier 781590Srgrimes/* 791590Srgrimes * The stack on which we remember what we've seen so far. 801590Srgrimes */ 811590Srgrimesstruct stkstr { 821590Srgrimes int opno; /* number of opening bracket */ 831590Srgrimes int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ 841590Srgrimes int parm; /* parm to size, font, etc */ 851590Srgrimes int lno; /* line number the thing came in in */ 861590Srgrimes} stk[MAXSTK]; 871590Srgrimesint stktop; 881590Srgrimes 891590Srgrimes/* 901590Srgrimes * The kinds of opening and closing brackets. 911590Srgrimes */ 921590Srgrimesstruct brstr { 9391389Sdwmalone const char *opbr; 9491389Sdwmalone const char *clbr; 951590Srgrimes} br[MAXBR] = { 961590Srgrimes /* A few bare bones troff commands */ 971590Srgrimes#define SZ 0 9826881Scharnier {"sz", "sz"}, /* also \s */ 991590Srgrimes#define FT 1 10026881Scharnier {"ft", "ft"}, /* also \f */ 1011590Srgrimes /* the -mm package */ 10226881Scharnier {"AL", "LE"}, 10326881Scharnier {"AS", "AE"}, 10426881Scharnier {"BL", "LE"}, 10526881Scharnier {"BS", "BE"}, 10626881Scharnier {"DF", "DE"}, 10726881Scharnier {"DL", "LE"}, 10826881Scharnier {"DS", "DE"}, 10926881Scharnier {"FS", "FE"}, 11026881Scharnier {"ML", "LE"}, 11126881Scharnier {"NS", "NE"}, 11226881Scharnier {"RL", "LE"}, 11326881Scharnier {"VL", "LE"}, 1141590Srgrimes /* the -ms package */ 11526881Scharnier {"AB", "AE"}, 11626881Scharnier {"BD", "DE"}, 11726881Scharnier {"CD", "DE"}, 11826881Scharnier {"DS", "DE"}, 11926881Scharnier {"FS", "FE"}, 12026881Scharnier {"ID", "DE"}, 12126881Scharnier {"KF", "KE"}, 12226881Scharnier {"KS", "KE"}, 12326881Scharnier {"LD", "DE"}, 12426881Scharnier {"LG", "NL"}, 12526881Scharnier {"QS", "QE"}, 12626881Scharnier {"RS", "RE"}, 12726881Scharnier {"SM", "NL"}, 12826881Scharnier {"XA", "XE"}, 12926881Scharnier {"XS", "XE"}, 1301590Srgrimes /* The -me package */ 13126881Scharnier {"(b", ")b"}, 13226881Scharnier {"(c", ")c"}, 13326881Scharnier {"(d", ")d"}, 13426881Scharnier {"(f", ")f"}, 13526881Scharnier {"(l", ")l"}, 13626881Scharnier {"(q", ")q"}, 13726881Scharnier {"(x", ")x"}, 13826881Scharnier {"(z", ")z"}, 1391590Srgrimes /* Things needed by preprocessors */ 14026881Scharnier {"EQ", "EN"}, 14126881Scharnier {"TS", "TE"}, 1421590Srgrimes /* Refer */ 14326881Scharnier {"[", "]"}, 14426881Scharnier {0, 0} 1451590Srgrimes}; 1461590Srgrimes 1471590Srgrimes/* 1481590Srgrimes * All commands known to nroff, plus macro packages. 1491590Srgrimes * Used so we can complain about unrecognized commands. 1501590Srgrimes */ 15191389Sdwmaloneconst char *knowncmds[MAXCMDS] = { 1521590Srgrimes"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t", 1531590Srgrimes"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++", 1541590Srgrimes"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M", 1551590Srgrimes"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB", 1561590Srgrimes"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B", "B1", "B2", 1571590Srgrimes"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT", 1581590Srgrimes"D", "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM", 1591590Srgrimes"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", 1601590Srgrimes"FQ", "FS", "FV", "FX", "H", "HC", "HD", "HM", "HO", "HU", "I", "ID", 1611590Srgrimes"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB", 1621590Srgrimes"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR", 1631590Srgrimes"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P", 1641590Srgrimes"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R", "RA", 1651590Srgrimes"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S", "S0", "S2", "S3", "SA", 1661590Srgrimes"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE", 1671590Srgrimes"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL", 1681590Srgrimes"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[", "[-", "[0", 1691590Srgrimes"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]", "]-", "]<", "]>", 1701590Srgrimes"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b", "ba", "bc", "bd", 1711590Srgrimes"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs", 1721590Srgrimes"ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec", 1731590Srgrimes"ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo", 1741590Srgrimes"fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i", 1751590Srgrimes"ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln", 1761590Srgrimes"lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1", 1771590Srgrimes"n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx", 1781590Srgrimes"of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps", 1791590Srgrimes"q", "r", "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb", 1801590Srgrimes"sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th", 1811590Srgrimes"ti", "tl", "tm", "tp", "tr", "u", "uf", "uh", "ul", "vs", "wh", "xp", 1821590Srgrimes"yr", 0 1831590Srgrimes}; 1841590Srgrimes 1851590Srgrimesint lineno; /* current line number in input file */ 18691389Sdwmaloneconst char *cfilename; /* name of current file */ 1871590Srgrimesint nfiles; /* number of files to process */ 1881590Srgrimesint fflag; /* -f: ignore \f */ 1891590Srgrimesint sflag; /* -s: ignore \s */ 1901590Srgrimesint ncmds; /* size of knowncmds */ 1911590Srgrimesint slot; /* slot in knowncmds found by binsrch */ 1921590Srgrimes 19326881Scharnierint 1941590Srgrimesmain(argc, argv) 1951590Srgrimesint argc; 1961590Srgrimeschar **argv; 1971590Srgrimes{ 1981590Srgrimes FILE *f; 1991590Srgrimes int i; 2001590Srgrimes char *cp; 2011590Srgrimes char b1[4]; 2021590Srgrimes 2031590Srgrimes /* Figure out how many known commands there are */ 2041590Srgrimes while (knowncmds[ncmds]) 2051590Srgrimes ncmds++; 2061590Srgrimes while (argc > 1 && argv[1][0] == '-') { 2071590Srgrimes switch(argv[1][1]) { 2081590Srgrimes 2091590Srgrimes /* -a: add pairs of macros */ 2101590Srgrimes case 'a': 2111590Srgrimes i = strlen(argv[1]) - 2; 2121590Srgrimes if (i % 6 != 0) 2131590Srgrimes usage(); 2141590Srgrimes /* look for empty macro slots */ 2151590Srgrimes for (i=0; br[i].opbr; i++) 2161590Srgrimes ; 2171590Srgrimes for (cp=argv[1]+3; cp[-1]; cp += 6) { 21891389Sdwmalone br[i].opbr = strncpy(malloc(3), cp, 2); 21991389Sdwmalone br[i].clbr = strncpy(malloc(3), cp+3, 2); 2201590Srgrimes addmac(br[i].opbr); /* knows pairs are also known cmds */ 2211590Srgrimes addmac(br[i].clbr); 2221590Srgrimes i++; 2231590Srgrimes } 2241590Srgrimes break; 2251590Srgrimes 2261590Srgrimes /* -c: add known commands */ 2271590Srgrimes case 'c': 2281590Srgrimes i = strlen(argv[1]) - 2; 2291590Srgrimes if (i % 3 != 0) 2301590Srgrimes usage(); 2311590Srgrimes for (cp=argv[1]+3; cp[-1]; cp += 3) { 2321590Srgrimes if (cp[2] && cp[2] != '.') 2331590Srgrimes usage(); 2341590Srgrimes strncpy(b1, cp, 2); 2359376Sasami b1[2] = '\0'; 2361590Srgrimes addmac(b1); 2371590Srgrimes } 2381590Srgrimes break; 2391590Srgrimes 2401590Srgrimes /* -f: ignore font changes */ 2411590Srgrimes case 'f': 2421590Srgrimes fflag = 1; 2431590Srgrimes break; 2441590Srgrimes 2451590Srgrimes /* -s: ignore size changes */ 2461590Srgrimes case 's': 2471590Srgrimes sflag = 1; 2481590Srgrimes break; 2491590Srgrimes default: 2501590Srgrimes usage(); 2511590Srgrimes } 2521590Srgrimes argc--; argv++; 2531590Srgrimes } 2541590Srgrimes 2551590Srgrimes nfiles = argc - 1; 2561590Srgrimes 2571590Srgrimes if (nfiles > 0) { 2581590Srgrimes for (i=1; i<argc; i++) { 2591590Srgrimes cfilename = argv[i]; 2601590Srgrimes f = fopen(cfilename, "r"); 2611590Srgrimes if (f == NULL) 2621590Srgrimes perror(cfilename); 2631590Srgrimes else 2641590Srgrimes process(f); 2651590Srgrimes } 2661590Srgrimes } else { 2671590Srgrimes cfilename = "stdin"; 2681590Srgrimes process(stdin); 2691590Srgrimes } 2701590Srgrimes exit(0); 2711590Srgrimes} 2721590Srgrimes 27326881Scharnierstatic void 2741590Srgrimesusage() 2751590Srgrimes{ 27626881Scharnier fprintf(stderr, 27726881Scharnier "usage: checknr [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [-s] [-f] file\n"); 2781590Srgrimes exit(1); 2791590Srgrimes} 2801590Srgrimes 28126881Scharniervoid 2821590Srgrimesprocess(f) 2831590SrgrimesFILE *f; 2841590Srgrimes{ 28591389Sdwmalone int i, n; 2861590Srgrimes char mac[5]; /* The current macro or nroff command */ 2871590Srgrimes int pl; 28891389Sdwmalone static char line[256]; /* the current line */ 2891590Srgrimes 2901590Srgrimes stktop = -1; 2911590Srgrimes for (lineno = 1; fgets(line, sizeof line, f); lineno++) { 2921590Srgrimes if (line[0] == '.') { 2931590Srgrimes /* 2941590Srgrimes * find and isolate the macro/command name. 2951590Srgrimes */ 2961590Srgrimes strncpy(mac, line+1, 4); 2971590Srgrimes if (isspace(mac[0])) { 2981590Srgrimes pe(lineno); 2991590Srgrimes printf("Empty command\n"); 3001590Srgrimes } else if (isspace(mac[1])) { 3011590Srgrimes mac[1] = 0; 3021590Srgrimes } else if (isspace(mac[2])) { 3031590Srgrimes mac[2] = 0; 3041590Srgrimes } else if (mac[0] != '\\' || mac[1] != '\"') { 3051590Srgrimes pe(lineno); 3061590Srgrimes printf("Command too long\n"); 3071590Srgrimes } 3081590Srgrimes 3091590Srgrimes /* 3101590Srgrimes * Is it a known command? 3111590Srgrimes */ 3121590Srgrimes checkknown(mac); 3131590Srgrimes 3141590Srgrimes /* 3151590Srgrimes * Should we add it? 3161590Srgrimes */ 3171590Srgrimes if (eq(mac, "de")) 3181590Srgrimes addcmd(line); 3191590Srgrimes 3201590Srgrimes chkcmd(line, mac); 3211590Srgrimes } 3221590Srgrimes 3231590Srgrimes /* 3241590Srgrimes * At this point we process the line looking 3251590Srgrimes * for \s and \f. 3261590Srgrimes */ 3271590Srgrimes for (i=0; line[i]; i++) 3281590Srgrimes if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) { 3291590Srgrimes if (!sflag && line[++i]=='s') { 3301590Srgrimes pl = line[++i]; 3311590Srgrimes if (isdigit(pl)) { 3321590Srgrimes n = pl - '0'; 3331590Srgrimes pl = ' '; 3341590Srgrimes } else 3351590Srgrimes n = 0; 3361590Srgrimes while (isdigit(line[++i])) 3371590Srgrimes n = 10 * n + line[i] - '0'; 3381590Srgrimes i--; 3391590Srgrimes if (n == 0) { 3401590Srgrimes if (stk[stktop].opno == SZ) { 3411590Srgrimes stktop--; 3421590Srgrimes } else { 3431590Srgrimes pe(lineno); 3441590Srgrimes printf("unmatched \\s0\n"); 3451590Srgrimes } 3461590Srgrimes } else { 3471590Srgrimes stk[++stktop].opno = SZ; 3481590Srgrimes stk[stktop].pl = pl; 3491590Srgrimes stk[stktop].parm = n; 3501590Srgrimes stk[stktop].lno = lineno; 3511590Srgrimes } 3521590Srgrimes } else if (!fflag && line[i]=='f') { 3531590Srgrimes n = line[++i]; 3541590Srgrimes if (n == 'P') { 3551590Srgrimes if (stk[stktop].opno == FT) { 3561590Srgrimes stktop--; 3571590Srgrimes } else { 3581590Srgrimes pe(lineno); 3591590Srgrimes printf("unmatched \\fP\n"); 3601590Srgrimes } 3611590Srgrimes } else { 3621590Srgrimes stk[++stktop].opno = FT; 3631590Srgrimes stk[stktop].pl = 1; 3641590Srgrimes stk[stktop].parm = n; 3651590Srgrimes stk[stktop].lno = lineno; 3661590Srgrimes } 3671590Srgrimes } 3681590Srgrimes } 3691590Srgrimes } 3701590Srgrimes /* 3711590Srgrimes * We've hit the end and look at all this stuff that hasn't been 3721590Srgrimes * matched yet! Complain, complain. 3731590Srgrimes */ 3741590Srgrimes for (i=stktop; i>=0; i--) { 3751590Srgrimes complain(i); 3761590Srgrimes } 3771590Srgrimes} 3781590Srgrimes 37926881Scharniervoid 3801590Srgrimescomplain(i) 38126881Scharnierint i; 3821590Srgrimes{ 3831590Srgrimes pe(stk[i].lno); 3841590Srgrimes printf("Unmatched "); 3851590Srgrimes prop(i); 3861590Srgrimes printf("\n"); 3871590Srgrimes} 3881590Srgrimes 38926881Scharniervoid 3901590Srgrimesprop(i) 39126881Scharnierint i; 3921590Srgrimes{ 3931590Srgrimes if (stk[i].pl == 0) 3941590Srgrimes printf(".%s", br[stk[i].opno].opbr); 3951590Srgrimes else switch(stk[i].opno) { 3961590Srgrimes case SZ: 3971590Srgrimes printf("\\s%c%d", stk[i].pl, stk[i].parm); 3981590Srgrimes break; 3991590Srgrimes case FT: 4001590Srgrimes printf("\\f%c", stk[i].parm); 4011590Srgrimes break; 4021590Srgrimes default: 4031590Srgrimes printf("Bug: stk[%d].opno = %d = .%s, .%s", 4041590Srgrimes i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr); 4051590Srgrimes } 4061590Srgrimes} 4071590Srgrimes 40826881Scharniervoid 4091590Srgrimeschkcmd(line, mac) 41091389Sdwmalonechar *line __unused; 4111590Srgrimeschar *mac; 4121590Srgrimes{ 41391389Sdwmalone int i; 4141590Srgrimes 4151590Srgrimes /* 4161590Srgrimes * Check to see if it matches top of stack. 4171590Srgrimes */ 4181590Srgrimes if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) 4191590Srgrimes stktop--; /* OK. Pop & forget */ 4201590Srgrimes else { 4211590Srgrimes /* No. Maybe it's an opener */ 4221590Srgrimes for (i=0; br[i].opbr; i++) { 4231590Srgrimes if (eq(mac, br[i].opbr)) { 4241590Srgrimes /* Found. Push it. */ 4251590Srgrimes stktop++; 4261590Srgrimes stk[stktop].opno = i; 4271590Srgrimes stk[stktop].pl = 0; 4281590Srgrimes stk[stktop].parm = 0; 4291590Srgrimes stk[stktop].lno = lineno; 4301590Srgrimes break; 4311590Srgrimes } 4321590Srgrimes /* 4331590Srgrimes * Maybe it's an unmatched closer. 4341590Srgrimes * NOTE: this depends on the fact 4351590Srgrimes * that none of the closers can be 4361590Srgrimes * openers too. 4371590Srgrimes */ 4381590Srgrimes if (eq(mac, br[i].clbr)) { 4391590Srgrimes nomatch(mac); 4401590Srgrimes break; 4411590Srgrimes } 4421590Srgrimes } 4431590Srgrimes } 4441590Srgrimes} 4451590Srgrimes 44626881Scharniervoid 4471590Srgrimesnomatch(mac) 4481590Srgrimeschar *mac; 4491590Srgrimes{ 45091389Sdwmalone int i, j; 4511590Srgrimes 4521590Srgrimes /* 4531590Srgrimes * Look for a match further down on stack 4541590Srgrimes * If we find one, it suggests that the stuff in 4551590Srgrimes * between is supposed to match itself. 4561590Srgrimes */ 4571590Srgrimes for (j=stktop; j>=0; j--) 4581590Srgrimes if (eq(mac,br[stk[j].opno].clbr)) { 4591590Srgrimes /* Found. Make a good diagnostic. */ 4601590Srgrimes if (j == stktop-2) { 4611590Srgrimes /* 4621590Srgrimes * Check for special case \fx..\fR and don't 4631590Srgrimes * complain. 4641590Srgrimes */ 4651590Srgrimes if (stk[j+1].opno==FT && stk[j+1].parm!='R' 4661590Srgrimes && stk[j+2].opno==FT && stk[j+2].parm=='R') { 4671590Srgrimes stktop = j -1; 4681590Srgrimes return; 4691590Srgrimes } 4701590Srgrimes /* 4711590Srgrimes * We have two unmatched frobs. Chances are 4721590Srgrimes * they were intended to match, so we mention 4731590Srgrimes * them together. 4741590Srgrimes */ 4751590Srgrimes pe(stk[j+1].lno); 4761590Srgrimes prop(j+1); 4771590Srgrimes printf(" does not match %d: ", stk[j+2].lno); 4781590Srgrimes prop(j+2); 4791590Srgrimes printf("\n"); 4801590Srgrimes } else for (i=j+1; i <= stktop; i++) { 4811590Srgrimes complain(i); 4821590Srgrimes } 4831590Srgrimes stktop = j-1; 4841590Srgrimes return; 4851590Srgrimes } 4861590Srgrimes /* Didn't find one. Throw this away. */ 4871590Srgrimes pe(lineno); 4881590Srgrimes printf("Unmatched .%s\n", mac); 4891590Srgrimes} 4901590Srgrimes 4911590Srgrimes/* eq: are two strings equal? */ 49226881Scharnierint 4931590Srgrimeseq(s1, s2) 49491389Sdwmaloneconst char *s1, *s2; 4951590Srgrimes{ 4961590Srgrimes return (strcmp(s1, s2) == 0); 4971590Srgrimes} 4981590Srgrimes 4991590Srgrimes/* print the first part of an error message, given the line number */ 50026881Scharniervoid 50191389Sdwmalonepe(linen) 50291389Sdwmaloneint linen; 5031590Srgrimes{ 5041590Srgrimes if (nfiles > 1) 5051590Srgrimes printf("%s: ", cfilename); 50691389Sdwmalone printf("%d: ", linen); 5071590Srgrimes} 5081590Srgrimes 50926881Scharniervoid 5101590Srgrimescheckknown(mac) 5111590Srgrimeschar *mac; 5121590Srgrimes{ 5131590Srgrimes 5141590Srgrimes if (eq(mac, ".")) 5151590Srgrimes return; 5161590Srgrimes if (binsrch(mac) >= 0) 5171590Srgrimes return; 5181590Srgrimes if (mac[0] == '\\' && mac[1] == '"') /* comments */ 5191590Srgrimes return; 5201590Srgrimes 5211590Srgrimes pe(lineno); 5221590Srgrimes printf("Unknown command: .%s\n", mac); 5231590Srgrimes} 5241590Srgrimes 5251590Srgrimes/* 5261590Srgrimes * We have a .de xx line in "line". Add xx to the list of known commands. 5271590Srgrimes */ 52826881Scharniervoid 5291590Srgrimesaddcmd(line) 5301590Srgrimeschar *line; 5311590Srgrimes{ 5321590Srgrimes char *mac; 5331590Srgrimes 5341590Srgrimes /* grab the macro being defined */ 5351590Srgrimes mac = line+4; 5361590Srgrimes while (isspace(*mac)) 5371590Srgrimes mac++; 5381590Srgrimes if (*mac == 0) { 5391590Srgrimes pe(lineno); 5401590Srgrimes printf("illegal define: %s\n", line); 5411590Srgrimes return; 5421590Srgrimes } 5431590Srgrimes mac[2] = 0; 5441590Srgrimes if (isspace(mac[1]) || mac[1] == '\\') 5451590Srgrimes mac[1] = 0; 5461590Srgrimes if (ncmds >= MAXCMDS) { 5471590Srgrimes printf("Only %d known commands allowed\n", MAXCMDS); 5481590Srgrimes exit(1); 5491590Srgrimes } 5501590Srgrimes addmac(mac); 5511590Srgrimes} 5521590Srgrimes 5531590Srgrimes/* 5541590Srgrimes * Add mac to the list. We should really have some kind of tree 5551590Srgrimes * structure here but this is a quick-and-dirty job and I just don't 5561590Srgrimes * have time to mess with it. (I wonder if this will come back to haunt 5571590Srgrimes * me someday?) Anyway, I claim that .de is fairly rare in user 5581590Srgrimes * nroff programs, and the register loop below is pretty fast. 5591590Srgrimes */ 56026881Scharniervoid 5611590Srgrimesaddmac(mac) 56291389Sdwmaloneconst char *mac; 5631590Srgrimes{ 56491389Sdwmalone const char **src, **dest, **loc; 5651590Srgrimes 5661590Srgrimes if (binsrch(mac) >= 0){ /* it's OK to redefine something */ 5671590Srgrimes#ifdef DEBUG 5681590Srgrimes printf("binsrch(%s) -> already in table\n", mac); 56991389Sdwmalone#endif 5701590Srgrimes return; 5711590Srgrimes } 5721590Srgrimes /* binsrch sets slot as a side effect */ 5731590Srgrimes#ifdef DEBUG 5741590Srgrimesprintf("binsrch(%s) -> %d\n", mac, slot); 5751590Srgrimes#endif 5761590Srgrimes loc = &knowncmds[slot]; 5771590Srgrimes src = &knowncmds[ncmds-1]; 5781590Srgrimes dest = src+1; 5791590Srgrimes while (dest > loc) 5801590Srgrimes *dest-- = *src--; 58191389Sdwmalone *loc = strcpy(malloc(3), mac); 5821590Srgrimes ncmds++; 5831590Srgrimes#ifdef DEBUG 5841590Srgrimesprintf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds); 5851590Srgrimes#endif 5861590Srgrimes} 5871590Srgrimes 5881590Srgrimes/* 5891590Srgrimes * Do a binary search in knowncmds for mac. 5901590Srgrimes * If found, return the index. If not, return -1. 5911590Srgrimes */ 59226881Scharnierint 5931590Srgrimesbinsrch(mac) 59491389Sdwmaloneconst char *mac; 5951590Srgrimes{ 59691389Sdwmalone const char *p; /* pointer to current cmd in list */ 59791389Sdwmalone int d; /* difference if any */ 59891389Sdwmalone int mid; /* mid point in binary search */ 59991389Sdwmalone int top, bot; /* boundaries of bin search, inclusive */ 6001590Srgrimes 6011590Srgrimes top = ncmds-1; 6021590Srgrimes bot = 0; 6031590Srgrimes while (top >= bot) { 6041590Srgrimes mid = (top+bot)/2; 6051590Srgrimes p = knowncmds[mid]; 6061590Srgrimes d = p[0] - mac[0]; 6071590Srgrimes if (d == 0) 6081590Srgrimes d = p[1] - mac[1]; 6091590Srgrimes if (d == 0) 6101590Srgrimes return mid; 6111590Srgrimes if (d < 0) 6121590Srgrimes bot = mid + 1; 6131590Srgrimes else 6141590Srgrimes top = mid - 1; 6151590Srgrimes } 6161590Srgrimes slot = bot; /* place it would have gone */ 6171590Srgrimes return -1; 6181590Srgrimes} 619