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 * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3141568Sarchiestatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1980, 1993\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 341590Srgrimes#endif /* not lint */ 351590Srgrimes 3691389Sdwmalone#if 0 371590Srgrimes#ifndef lint 3891389Sdwmalonestatic char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93"; 391590Srgrimes#endif /* not lint */ 4091389Sdwmalone#endif 411590Srgrimes 4291389Sdwmalone#include <sys/cdefs.h> 4391389Sdwmalone__FBSDID("$FreeBSD$"); 4491389Sdwmalone 451590Srgrimes/* 461590Srgrimes * checknr: check an nroff/troff input file for matching macro calls. 471590Srgrimes * we also attempt to match size and font changes, but only the embedded 481590Srgrimes * kind. These must end in \s0 and \fP resp. Maybe more sophistication 491590Srgrimes * later but for now think of these restrictions as contributions to 501590Srgrimes * structured typesetting. 511590Srgrimes */ 52132178Stjr#include <err.h> 53282439Sbapt#define _WITH_GETLINE 541590Srgrimes#include <stdio.h> 5526881Scharnier#include <stdlib.h> 5626881Scharnier#include <string.h> 571590Srgrimes#include <ctype.h> 581590Srgrimes 591590Srgrimes#define MAXSTK 100 /* Stack size */ 601590Srgrimes#define MAXBR 100 /* Max number of bracket pairs known */ 61282467Sbapt#define MAXCMDS 600 /* Max number of commands known */ 621590Srgrimes 63227234Sedstatic void addcmd(char *); 64227234Sedstatic void addmac(const char *); 65227234Sedstatic int binsrch(const char *); 66227234Sedstatic void checkknown(const char *); 67227234Sedstatic void chkcmd(const char *, const char *); 68227234Sedstatic void complain(int); 69227234Sedstatic int eq(const char *, const char *); 70227234Sedstatic void nomatch(const char *); 71227234Sedstatic void pe(int); 72227234Sedstatic void process(FILE *); 73227234Sedstatic void prop(int); 7492920Simpstatic void usage(void); 7526881Scharnier 761590Srgrimes/* 771590Srgrimes * The stack on which we remember what we've seen so far. 781590Srgrimes */ 79227234Sedstatic struct stkstr { 801590Srgrimes int opno; /* number of opening bracket */ 811590Srgrimes int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ 821590Srgrimes int parm; /* parm to size, font, etc */ 83219126Sbrucec int lno; /* line number */ 841590Srgrimes} stk[MAXSTK]; 85227234Sedstatic int stktop; 861590Srgrimes 871590Srgrimes/* 881590Srgrimes * The kinds of opening and closing brackets. 891590Srgrimes */ 90227234Sedstatic struct brstr { 9191389Sdwmalone const char *opbr; 9291389Sdwmalone const char *clbr; 931590Srgrimes} br[MAXBR] = { 941590Srgrimes /* A few bare bones troff commands */ 951590Srgrimes#define SZ 0 9626881Scharnier {"sz", "sz"}, /* also \s */ 971590Srgrimes#define FT 1 9826881Scharnier {"ft", "ft"}, /* also \f */ 991590Srgrimes /* the -mm package */ 10026881Scharnier {"AL", "LE"}, 10126881Scharnier {"AS", "AE"}, 10226881Scharnier {"BL", "LE"}, 10326881Scharnier {"BS", "BE"}, 10426881Scharnier {"DF", "DE"}, 10526881Scharnier {"DL", "LE"}, 10626881Scharnier {"DS", "DE"}, 10726881Scharnier {"FS", "FE"}, 10826881Scharnier {"ML", "LE"}, 10926881Scharnier {"NS", "NE"}, 11026881Scharnier {"RL", "LE"}, 11126881Scharnier {"VL", "LE"}, 1121590Srgrimes /* the -ms package */ 11326881Scharnier {"AB", "AE"}, 11426881Scharnier {"BD", "DE"}, 11526881Scharnier {"CD", "DE"}, 11626881Scharnier {"DS", "DE"}, 11726881Scharnier {"FS", "FE"}, 11826881Scharnier {"ID", "DE"}, 11926881Scharnier {"KF", "KE"}, 12026881Scharnier {"KS", "KE"}, 12126881Scharnier {"LD", "DE"}, 12226881Scharnier {"LG", "NL"}, 12326881Scharnier {"QS", "QE"}, 12426881Scharnier {"RS", "RE"}, 12526881Scharnier {"SM", "NL"}, 12626881Scharnier {"XA", "XE"}, 12726881Scharnier {"XS", "XE"}, 1281590Srgrimes /* The -me package */ 12926881Scharnier {"(b", ")b"}, 13026881Scharnier {"(c", ")c"}, 13126881Scharnier {"(d", ")d"}, 13226881Scharnier {"(f", ")f"}, 13326881Scharnier {"(l", ")l"}, 13426881Scharnier {"(q", ")q"}, 13526881Scharnier {"(x", ")x"}, 13626881Scharnier {"(z", ")z"}, 137282467Sbapt /* The -mdoc package */ 138282467Sbapt {"Ao", "Ac"}, 139282467Sbapt {"Bd", "Ed"}, 140282467Sbapt {"Bk", "Ek"}, 141282467Sbapt {"Bo", "Bc"}, 142282467Sbapt {"Do", "Dc"}, 143282467Sbapt {"Fo", "Fc"}, 144282467Sbapt {"Oo", "Oc"}, 145282467Sbapt {"Po", "Pc"}, 146282467Sbapt {"Qo", "Qc"}, 147282467Sbapt {"Rs", "Re"}, 148282467Sbapt {"So", "Sc"}, 149282467Sbapt {"Xo", "Xc"}, 1501590Srgrimes /* Things needed by preprocessors */ 15126881Scharnier {"EQ", "EN"}, 15226881Scharnier {"TS", "TE"}, 1531590Srgrimes /* Refer */ 15426881Scharnier {"[", "]"}, 15526881Scharnier {0, 0} 1561590Srgrimes}; 1571590Srgrimes 1581590Srgrimes/* 1591590Srgrimes * All commands known to nroff, plus macro packages. 1601590Srgrimes * Used so we can complain about unrecognized commands. 1611590Srgrimes */ 162227234Sedstatic const char *knowncmds[MAXCMDS] = { 163282467Sbapt"$c", "$f", "$h", "$p", "$s", "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", 164282467Sbapt"%P", "%Q", "%R", "%T", "%V", "(b", "(c", "(d", "(f", "(l", "(q", "(t", "(x", 165282467Sbapt"(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++", "+c", "1C", 166282467Sbapt"1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M", "@c", "@e", "@f", 167282467Sbapt"@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB", "AE", "AF", "AI", "AL", 168282467Sbapt"AM", "AS", "AT", "AU", "AX", "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", 169282467Sbapt"B", "B" , "B1", "B2", "BD", "BE", "BG", "BL", "BS", "BT", "BX", "Bc", "Bd", 170282467Sbapt"Bf", "Bk", "Bl", "Bo", "Bq", "Bsx", "Bx", "C1", "C2", "CD", "CM", "CT", "Cd", 171282467Sbapt"Cm", "D", "D" , "D1", "DA", "DE", "DF", "DL", "DS", "DT", "Db", "Dc", "Dd", 172282467Sbapt"Dl", "Do", "Dq", "Dt", "Dv", "EC", "EF", "EG", "EH", "EM", "EN", "EQ", "EX", 173282467Sbapt"Ec", "Ed", "Ef", "Ek", "El", "Em", "Eo", "Er", "Ev", "FA", "FD", "FE", "FG", 174282467Sbapt"FJ", "FK", "FL", "FN", "FO", "FQ", "FS", "FV", "FX", "Fa", "Fc", "Fd", "Fl", 175282467Sbapt"Fn", "Fo", "Ft", "Fx", "H", "H" , "HC", "HD", "HM", "HO", "HU", "I", "I" , 176282467Sbapt"ID", "IE", "IH", "IM", "IP", "IX", "IZ", "Ic", "In", "It", "KD", "KE", "KF", 177282467Sbapt"KQ", "KS", "LB", "LC", "LD", "LE", "LG", "LI", "LP", "Lb", "Li", "MC", "ME", 178282467Sbapt"MF", "MH", "ML", "MR", "MT", "ND", "NE", "NH", "NL", "NP", "NS", "Nd", "Nm", 179282467Sbapt"No", "Ns", "Nx", "OF", "OH", "OK", "OP", "Oc", "Oo", "Op", "Os", "Ot", "Ox", 180282467Sbapt"P", "P" , "P1", "PF", "PH", "PP", "PT", "PX", "PY", "Pa", "Pc", "Pf", "Po", 181282467Sbapt"Pp", "Pq", "QE", "QP", "QS", "Qc", "Ql", "Qo", "Qq", "R", "R" , "RA", "RC", 182282467Sbapt"RE", "RL", "RP", "RQ", "RS", "RT", "Re", "Rs", "S", "S" , "S0", "S2", "S3", 183282467Sbapt"SA", "SG", "SH", "SK", "SM", "SP", "SY", "Sc", "Sh", "Sm", "So", "Sq", "Ss", 184282467Sbapt"St", "Sx", "Sy", "T&", "TA", "TB", "TC", "TD", "TE", "TH", "TL", "TM", "TP", 185282467Sbapt"TQ", "TR", "TS", "TX", "Tn", "UL", "US", "UX", "Ud", "Ux", "VL", "Va", "Vt", 186282467Sbapt"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "Xc", "Xo", "Xr", "[", 187282467Sbapt"[" , "[-", "[0", "[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "\\{", "\\}", 188282467Sbapt"]", "]" , "]-", "]<", "]>", "][", "ab", "ac", "ad", "af", "am", "ar", "as", 189282467Sbapt"b", "b" , "ba", "bc", "bd", "bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", 190282467Sbapt"ce", "cf", "ch", "chop", "cs", "ct", "cu", "da", "de", "di", "dl", "dn", "do", 191282467Sbapt"ds", "dt", "dw", "dy", "ec", "ef", "eh", "el", "em", "eo", "ep", "ev", "evc", 192282467Sbapt"ex", "fallback", "fc", "feature", "fi", "fl", "flig", "fo", "fp", "ft", "ftr", 193282467Sbapt"fz", "fzoom", "hc", "he", "hidechar", "hl", "hp", "ht", "hw", "hx", "hy", 194282467Sbapt"hylang", "i", "i" , "ie", "if", "ig", "in", "ip", "it", "ix", "kern", 195282467Sbapt"kernafter", "kernbefore", "kernpair", "lc", "lc_ctype", "lg", "lhang", "li", 196282467Sbapt"ll", "ln", "lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", 197282467Sbapt"n1", "n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx", 198282467Sbapt"of", "oh", "os", "pa", "papersize", "pc", "pi", "pl", "pm", "pn", "po", "pp", 199282467Sbapt"ps", "q", "q" , "r", "r" , "rb", "rd", "re", "recursionlimit", "return", 200282467Sbapt"rhang", "rm", "rn", "ro", "rr", "rs", "rt", "sb", "sc", "sh", "shift", "sk", 201282467Sbapt"so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th", "ti", "tl", "tm", "tp", 202282467Sbapt"tr", "track", "u", "uf", "uh", "ul", "vs", "wh", "xflag", "xp", "yr", 203282438Sbapt0 2041590Srgrimes}; 2051590Srgrimes 206227234Sedstatic int lineno; /* current line number in input file */ 207227234Sedstatic const char *cfilename; /* name of current file */ 208227234Sedstatic int nfiles; /* number of files to process */ 209227234Sedstatic int fflag; /* -f: ignore \f */ 210227234Sedstatic int sflag; /* -s: ignore \s */ 211227234Sedstatic int ncmds; /* size of knowncmds */ 212227234Sedstatic int slot; /* slot in knowncmds found by binsrch */ 2131590Srgrimes 21426881Scharnierint 215100813Sdwmalonemain(int argc, char **argv) 2161590Srgrimes{ 2171590Srgrimes FILE *f; 2181590Srgrimes int i; 2191590Srgrimes char *cp; 2201590Srgrimes char b1[4]; 2211590Srgrimes 2221590Srgrimes /* Figure out how many known commands there are */ 2231590Srgrimes while (knowncmds[ncmds]) 2241590Srgrimes ncmds++; 2251590Srgrimes while (argc > 1 && argv[1][0] == '-') { 2261590Srgrimes switch(argv[1][1]) { 2271590Srgrimes 2281590Srgrimes /* -a: add pairs of macros */ 2291590Srgrimes case 'a': 2301590Srgrimes i = strlen(argv[1]) - 2; 2311590Srgrimes if (i % 6 != 0) 2321590Srgrimes usage(); 2331590Srgrimes /* look for empty macro slots */ 2341590Srgrimes for (i=0; br[i].opbr; i++) 2351590Srgrimes ; 2361590Srgrimes for (cp=argv[1]+3; cp[-1]; cp += 6) { 237282466Sbapt char *tmp; 238282466Sbapt 239282466Sbapt if (i >= MAXBR) 240282466Sbapt errx(1, "too many pairs"); 241282466Sbapt if ((tmp = malloc(3)) == NULL) 242282466Sbapt err(1, "malloc"); 243282466Sbapt strlcpy(tmp, cp, 3); 244282466Sbapt br[i].opbr = tmp; 245282466Sbapt if ((tmp = malloc(3)) == NULL) 246282466Sbapt err(1, "malloc"); 247282466Sbapt strlcpy(tmp, cp+3, 3); 248282466Sbapt br[i].clbr = tmp; 2491590Srgrimes addmac(br[i].opbr); /* knows pairs are also known cmds */ 2501590Srgrimes addmac(br[i].clbr); 2511590Srgrimes i++; 2521590Srgrimes } 2531590Srgrimes break; 2541590Srgrimes 2551590Srgrimes /* -c: add known commands */ 2561590Srgrimes case 'c': 2571590Srgrimes i = strlen(argv[1]) - 2; 2581590Srgrimes if (i % 3 != 0) 2591590Srgrimes usage(); 2601590Srgrimes for (cp=argv[1]+3; cp[-1]; cp += 3) { 2611590Srgrimes if (cp[2] && cp[2] != '.') 2621590Srgrimes usage(); 2631590Srgrimes strncpy(b1, cp, 2); 2649376Sasami b1[2] = '\0'; 2651590Srgrimes addmac(b1); 2661590Srgrimes } 2671590Srgrimes break; 2681590Srgrimes 2691590Srgrimes /* -f: ignore font changes */ 2701590Srgrimes case 'f': 2711590Srgrimes fflag = 1; 2721590Srgrimes break; 2731590Srgrimes 2741590Srgrimes /* -s: ignore size changes */ 2751590Srgrimes case 's': 2761590Srgrimes sflag = 1; 2771590Srgrimes break; 2781590Srgrimes default: 2791590Srgrimes usage(); 2801590Srgrimes } 2811590Srgrimes argc--; argv++; 2821590Srgrimes } 2831590Srgrimes 2841590Srgrimes nfiles = argc - 1; 2851590Srgrimes 2861590Srgrimes if (nfiles > 0) { 287282437Sbapt for (i = 1; i < argc; i++) { 2881590Srgrimes cfilename = argv[i]; 2891590Srgrimes f = fopen(cfilename, "r"); 2901590Srgrimes if (f == NULL) 291132178Stjr warn("%s", cfilename); 292115601Stjr else { 2931590Srgrimes process(f); 294115601Stjr fclose(f); 295115601Stjr } 2961590Srgrimes } 2971590Srgrimes } else { 2981590Srgrimes cfilename = "stdin"; 2991590Srgrimes process(stdin); 3001590Srgrimes } 3011590Srgrimes exit(0); 3021590Srgrimes} 3031590Srgrimes 30426881Scharnierstatic void 305100813Sdwmaloneusage(void) 3061590Srgrimes{ 30726881Scharnier fprintf(stderr, 30826881Scharnier "usage: checknr [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [-s] [-f] file\n"); 3091590Srgrimes exit(1); 3101590Srgrimes} 3111590Srgrimes 312227234Sedstatic void 313100813Sdwmaloneprocess(FILE *f) 3141590Srgrimes{ 31591389Sdwmalone int i, n; 316282609Sbapt char mac[64]; /* The current macro or nroff command */ 317282439Sbapt char *line; 318282439Sbapt size_t linecap; 3191590Srgrimes int pl; 3201590Srgrimes 321282439Sbapt line = NULL; 322282439Sbapt linecap = 0; 3231590Srgrimes stktop = -1; 324282439Sbapt for (lineno = 1; getline(&line, &linecap, f) > 0; lineno++) { 3251590Srgrimes if (line[0] == '.') { 3261590Srgrimes /* 3271590Srgrimes * find and isolate the macro/command name. 3281590Srgrimes */ 3291590Srgrimes strncpy(mac, line+1, 4); 3301590Srgrimes if (isspace(mac[0])) { 3311590Srgrimes pe(lineno); 3321590Srgrimes printf("Empty command\n"); 3331590Srgrimes } else if (isspace(mac[1])) { 3341590Srgrimes mac[1] = 0; 3351590Srgrimes } else if (isspace(mac[2])) { 3361590Srgrimes mac[2] = 0; 3371590Srgrimes } else if (mac[0] != '\\' || mac[1] != '\"') { 3381590Srgrimes pe(lineno); 3391590Srgrimes printf("Command too long\n"); 3401590Srgrimes } 3411590Srgrimes 3421590Srgrimes /* 3431590Srgrimes * Is it a known command? 3441590Srgrimes */ 3451590Srgrimes checkknown(mac); 3461590Srgrimes 3471590Srgrimes /* 3481590Srgrimes * Should we add it? 3491590Srgrimes */ 3501590Srgrimes if (eq(mac, "de")) 3511590Srgrimes addcmd(line); 3521590Srgrimes 3531590Srgrimes chkcmd(line, mac); 3541590Srgrimes } 3551590Srgrimes 3561590Srgrimes /* 3571590Srgrimes * At this point we process the line looking 3581590Srgrimes * for \s and \f. 3591590Srgrimes */ 360282437Sbapt for (i = 0; line[i]; i++) 361282437Sbapt if (line[i] == '\\' && (i == 0 || line[i-1] != '\\')) { 362282437Sbapt if (!sflag && line[++i] == 's') { 3631590Srgrimes pl = line[++i]; 3641590Srgrimes if (isdigit(pl)) { 3651590Srgrimes n = pl - '0'; 3661590Srgrimes pl = ' '; 3671590Srgrimes } else 3681590Srgrimes n = 0; 3691590Srgrimes while (isdigit(line[++i])) 3701590Srgrimes n = 10 * n + line[i] - '0'; 3711590Srgrimes i--; 3721590Srgrimes if (n == 0) { 373282464Sbapt if (stktop >= 0 && 374282464Sbapt stk[stktop].opno == SZ) { 3751590Srgrimes stktop--; 3761590Srgrimes } else { 3771590Srgrimes pe(lineno); 3781590Srgrimes printf("unmatched \\s0\n"); 3791590Srgrimes } 3801590Srgrimes } else { 3811590Srgrimes stk[++stktop].opno = SZ; 3821590Srgrimes stk[stktop].pl = pl; 3831590Srgrimes stk[stktop].parm = n; 3841590Srgrimes stk[stktop].lno = lineno; 3851590Srgrimes } 386282437Sbapt } else if (!fflag && line[i] == 'f') { 3871590Srgrimes n = line[++i]; 3881590Srgrimes if (n == 'P') { 389282464Sbapt if (stktop >= 0 && 390282464Sbapt stk[stktop].opno == FT) { 3911590Srgrimes stktop--; 3921590Srgrimes } else { 3931590Srgrimes pe(lineno); 3941590Srgrimes printf("unmatched \\fP\n"); 3951590Srgrimes } 3961590Srgrimes } else { 3971590Srgrimes stk[++stktop].opno = FT; 3981590Srgrimes stk[stktop].pl = 1; 3991590Srgrimes stk[stktop].parm = n; 4001590Srgrimes stk[stktop].lno = lineno; 4011590Srgrimes } 4021590Srgrimes } 4031590Srgrimes } 4041590Srgrimes } 405282439Sbapt free(line); 4061590Srgrimes /* 4071590Srgrimes * We've hit the end and look at all this stuff that hasn't been 4081590Srgrimes * matched yet! Complain, complain. 4091590Srgrimes */ 410282437Sbapt for (i = stktop; i >= 0; i--) { 4111590Srgrimes complain(i); 4121590Srgrimes } 4131590Srgrimes} 4141590Srgrimes 415227234Sedstatic void 416100813Sdwmalonecomplain(int i) 4171590Srgrimes{ 4181590Srgrimes pe(stk[i].lno); 4191590Srgrimes printf("Unmatched "); 4201590Srgrimes prop(i); 4211590Srgrimes printf("\n"); 4221590Srgrimes} 4231590Srgrimes 424227234Sedstatic void 425100813Sdwmaloneprop(int i) 4261590Srgrimes{ 4271590Srgrimes if (stk[i].pl == 0) 4281590Srgrimes printf(".%s", br[stk[i].opno].opbr); 429282462Sbapt else switch(stk[i].opno) { 4301590Srgrimes case SZ: 4311590Srgrimes printf("\\s%c%d", stk[i].pl, stk[i].parm); 4321590Srgrimes break; 4331590Srgrimes case FT: 4341590Srgrimes printf("\\f%c", stk[i].parm); 4351590Srgrimes break; 4361590Srgrimes default: 4371590Srgrimes printf("Bug: stk[%d].opno = %d = .%s, .%s", 438282437Sbapt i, stk[i].opno, br[stk[i].opno].opbr, 439282437Sbapt br[stk[i].opno].clbr); 4401590Srgrimes } 4411590Srgrimes} 4421590Srgrimes 443227234Sedstatic void 444100813Sdwmalonechkcmd(const char *line __unused, const char *mac) 4451590Srgrimes{ 44691389Sdwmalone int i; 4471590Srgrimes 4481590Srgrimes /* 4491590Srgrimes * Check to see if it matches top of stack. 4501590Srgrimes */ 4511590Srgrimes if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) 4521590Srgrimes stktop--; /* OK. Pop & forget */ 4531590Srgrimes else { 4541590Srgrimes /* No. Maybe it's an opener */ 4551590Srgrimes for (i=0; br[i].opbr; i++) { 4561590Srgrimes if (eq(mac, br[i].opbr)) { 4571590Srgrimes /* Found. Push it. */ 4581590Srgrimes stktop++; 4591590Srgrimes stk[stktop].opno = i; 4601590Srgrimes stk[stktop].pl = 0; 4611590Srgrimes stk[stktop].parm = 0; 4621590Srgrimes stk[stktop].lno = lineno; 4631590Srgrimes break; 4641590Srgrimes } 4651590Srgrimes /* 4661590Srgrimes * Maybe it's an unmatched closer. 4671590Srgrimes * NOTE: this depends on the fact 4681590Srgrimes * that none of the closers can be 4691590Srgrimes * openers too. 4701590Srgrimes */ 4711590Srgrimes if (eq(mac, br[i].clbr)) { 4721590Srgrimes nomatch(mac); 4731590Srgrimes break; 4741590Srgrimes } 4751590Srgrimes } 4761590Srgrimes } 4771590Srgrimes} 4781590Srgrimes 479227234Sedstatic void 480100813Sdwmalonenomatch(const char *mac) 4811590Srgrimes{ 48291389Sdwmalone int i, j; 4831590Srgrimes 4841590Srgrimes /* 4851590Srgrimes * Look for a match further down on stack 4861590Srgrimes * If we find one, it suggests that the stuff in 4871590Srgrimes * between is supposed to match itself. 4881590Srgrimes */ 4891590Srgrimes for (j=stktop; j>=0; j--) 4901590Srgrimes if (eq(mac,br[stk[j].opno].clbr)) { 4911590Srgrimes /* Found. Make a good diagnostic. */ 4921590Srgrimes if (j == stktop-2) { 4931590Srgrimes /* 4941590Srgrimes * Check for special case \fx..\fR and don't 4951590Srgrimes * complain. 4961590Srgrimes */ 4971590Srgrimes if (stk[j+1].opno==FT && stk[j+1].parm!='R' 4981590Srgrimes && stk[j+2].opno==FT && stk[j+2].parm=='R') { 4991590Srgrimes stktop = j -1; 5001590Srgrimes return; 5011590Srgrimes } 5021590Srgrimes /* 5031590Srgrimes * We have two unmatched frobs. Chances are 5041590Srgrimes * they were intended to match, so we mention 5051590Srgrimes * them together. 5061590Srgrimes */ 5071590Srgrimes pe(stk[j+1].lno); 5081590Srgrimes prop(j+1); 5091590Srgrimes printf(" does not match %d: ", stk[j+2].lno); 5101590Srgrimes prop(j+2); 5111590Srgrimes printf("\n"); 5121590Srgrimes } else for (i=j+1; i <= stktop; i++) { 5131590Srgrimes complain(i); 5141590Srgrimes } 5151590Srgrimes stktop = j-1; 5161590Srgrimes return; 5171590Srgrimes } 5181590Srgrimes /* Didn't find one. Throw this away. */ 5191590Srgrimes pe(lineno); 5201590Srgrimes printf("Unmatched .%s\n", mac); 5211590Srgrimes} 5221590Srgrimes 5231590Srgrimes/* eq: are two strings equal? */ 524227234Sedstatic int 525100813Sdwmaloneeq(const char *s1, const char *s2) 5261590Srgrimes{ 5271590Srgrimes return (strcmp(s1, s2) == 0); 5281590Srgrimes} 5291590Srgrimes 5301590Srgrimes/* print the first part of an error message, given the line number */ 531227234Sedstatic void 532100813Sdwmalonepe(int linen) 5331590Srgrimes{ 5341590Srgrimes if (nfiles > 1) 5351590Srgrimes printf("%s: ", cfilename); 53691389Sdwmalone printf("%d: ", linen); 5371590Srgrimes} 5381590Srgrimes 539227234Sedstatic void 540100813Sdwmalonecheckknown(const char *mac) 5411590Srgrimes{ 5421590Srgrimes 5431590Srgrimes if (eq(mac, ".")) 5441590Srgrimes return; 5451590Srgrimes if (binsrch(mac) >= 0) 5461590Srgrimes return; 5471590Srgrimes if (mac[0] == '\\' && mac[1] == '"') /* comments */ 5481590Srgrimes return; 5491590Srgrimes 5501590Srgrimes pe(lineno); 5511590Srgrimes printf("Unknown command: .%s\n", mac); 5521590Srgrimes} 5531590Srgrimes 5541590Srgrimes/* 5551590Srgrimes * We have a .de xx line in "line". Add xx to the list of known commands. 5561590Srgrimes */ 557227234Sedstatic void 558100813Sdwmaloneaddcmd(char *line) 5591590Srgrimes{ 5601590Srgrimes char *mac; 5611590Srgrimes 5621590Srgrimes /* grab the macro being defined */ 5631590Srgrimes mac = line+4; 5641590Srgrimes while (isspace(*mac)) 5651590Srgrimes mac++; 5661590Srgrimes if (*mac == 0) { 5671590Srgrimes pe(lineno); 5681590Srgrimes printf("illegal define: %s\n", line); 5691590Srgrimes return; 5701590Srgrimes } 5711590Srgrimes mac[2] = 0; 5721590Srgrimes if (isspace(mac[1]) || mac[1] == '\\') 5731590Srgrimes mac[1] = 0; 5741590Srgrimes if (ncmds >= MAXCMDS) { 5751590Srgrimes printf("Only %d known commands allowed\n", MAXCMDS); 5761590Srgrimes exit(1); 5771590Srgrimes } 5781590Srgrimes addmac(mac); 5791590Srgrimes} 5801590Srgrimes 5811590Srgrimes/* 5821590Srgrimes * Add mac to the list. We should really have some kind of tree 5831590Srgrimes * structure here but this is a quick-and-dirty job and I just don't 5841590Srgrimes * have time to mess with it. (I wonder if this will come back to haunt 5851590Srgrimes * me someday?) Anyway, I claim that .de is fairly rare in user 5861590Srgrimes * nroff programs, and the register loop below is pretty fast. 5871590Srgrimes */ 588227234Sedstatic void 589100813Sdwmaloneaddmac(const char *mac) 5901590Srgrimes{ 59191389Sdwmalone const char **src, **dest, **loc; 5921590Srgrimes 5931590Srgrimes if (binsrch(mac) >= 0){ /* it's OK to redefine something */ 5941590Srgrimes#ifdef DEBUG 5951590Srgrimes printf("binsrch(%s) -> already in table\n", mac); 59691389Sdwmalone#endif 5971590Srgrimes return; 5981590Srgrimes } 5991590Srgrimes /* binsrch sets slot as a side effect */ 6001590Srgrimes#ifdef DEBUG 601282462Sbapt printf("binsrch(%s) -> %d\n", mac, slot); 6021590Srgrimes#endif 6031590Srgrimes loc = &knowncmds[slot]; 6041590Srgrimes src = &knowncmds[ncmds-1]; 6051590Srgrimes dest = src+1; 6061590Srgrimes while (dest > loc) 6071590Srgrimes *dest-- = *src--; 608282463Sbapt if ((*loc = strdup(mac)) == NULL) 609282463Sbapt err(1, "strdup"); 6101590Srgrimes ncmds++; 6111590Srgrimes#ifdef DEBUG 612282437Sbapt printf("after: %s %s %s %s %s, %d cmds\n", 613282437Sbapt knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], 614282437Sbapt knowncmds[slot+1], knowncmds[slot+2], ncmds); 6151590Srgrimes#endif 6161590Srgrimes} 6171590Srgrimes 6181590Srgrimes/* 6191590Srgrimes * Do a binary search in knowncmds for mac. 6201590Srgrimes * If found, return the index. If not, return -1. 6211590Srgrimes */ 622227234Sedstatic int 623100813Sdwmalonebinsrch(const char *mac) 6241590Srgrimes{ 62591389Sdwmalone const char *p; /* pointer to current cmd in list */ 62691389Sdwmalone int d; /* difference if any */ 62791389Sdwmalone int mid; /* mid point in binary search */ 62891389Sdwmalone int top, bot; /* boundaries of bin search, inclusive */ 6291590Srgrimes 6301590Srgrimes top = ncmds-1; 6311590Srgrimes bot = 0; 6321590Srgrimes while (top >= bot) { 6331590Srgrimes mid = (top+bot)/2; 6341590Srgrimes p = knowncmds[mid]; 6351590Srgrimes d = p[0] - mac[0]; 6361590Srgrimes if (d == 0) 6371590Srgrimes d = p[1] - mac[1]; 6381590Srgrimes if (d == 0) 639282437Sbapt return (mid); 6401590Srgrimes if (d < 0) 6411590Srgrimes bot = mid + 1; 6421590Srgrimes else 6431590Srgrimes top = mid - 1; 6441590Srgrimes } 6451590Srgrimes slot = bot; /* place it would have gone */ 646282437Sbapt return (-1); 6471590Srgrimes} 648