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