1259412Sluigi/* 2260368Sluigi * Copyright (c) 1980, 1993 3259412Sluigi * The Regents of the University of California. All rights reserved. 4259412Sluigi * 5259412Sluigi * Redistribution and use in source and binary forms, with or without 6259412Sluigi * modification, are permitted provided that the following conditions 7259412Sluigi * are met: 8259412Sluigi * 1. Redistributions of source code must retain the above copyright 9259412Sluigi * notice, this list of conditions and the following disclaimer. 10259412Sluigi * 2. Redistributions in binary form must reproduce the above copyright 11259412Sluigi * notice, this list of conditions and the following disclaimer in the 12259412Sluigi * documentation and/or other materials provided with the distribution. 13259412Sluigi * 4. Neither the name of the University nor the names of its contributors 14259412Sluigi * may be used to endorse or promote products derived from this software 15259412Sluigi * without specific prior written permission. 16259412Sluigi * 17259412Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18259412Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19259412Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20259412Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21259412Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22259412Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23259412Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24259412Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25259412Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26259412Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27285349Sluigi * SUCH DAMAGE. 28285349Sluigi */ 29259412Sluigi 30259412Sluigi#ifndef lint 31259412Sluigistatic const char copyright[] = 32259412Sluigi"@(#) Copyright (c) 1980, 1993\n\ 33259412Sluigi The Regents of the University of California. All rights reserved.\n"; 34261909Sluigi#endif /* not lint */ 35259412Sluigi 36259412Sluigi#if 0 37261909Sluigi#ifndef lint 38259412Sluigistatic char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93"; 39259412Sluigi#endif /* not lint */ 40259412Sluigi#endif 41259412Sluigi 42259412Sluigi#include <sys/cdefs.h> 43259412Sluigi__FBSDID("$FreeBSD$"); 44259412Sluigi 45259412Sluigi/* 46259412Sluigi * checknr: check an nroff/troff input file for matching macro calls. 47259412Sluigi * we also attempt to match size and font changes, but only the embedded 48259412Sluigi * kind. These must end in \s0 and \fP resp. Maybe more sophistication 49259412Sluigi * later but for now think of these restrictions as contributions to 50259412Sluigi * structured typesetting. 51259412Sluigi */ 52259412Sluigi#include <err.h> 53259412Sluigi#include <stdio.h> 54259412Sluigi#include <stdlib.h> 55270063Sluigi#include <string.h> 56270063Sluigi#include <ctype.h> 57270063Sluigi 58259412Sluigi#define MAXSTK 100 /* Stack size */ 59261909Sluigi#define MAXBR 100 /* Max number of bracket pairs known */ 60261909Sluigi#define MAXCMDS 500 /* Max number of commands known */ 61259412Sluigi 62259412Sluigistatic void addcmd(char *); 63259412Sluigistatic void addmac(const char *); 64259412Sluigistatic int binsrch(const char *); 65259412Sluigistatic void checkknown(const char *); 66259412Sluigistatic void chkcmd(const char *, const char *); 67259412Sluigistatic void complain(int); 68259412Sluigistatic int eq(const char *, const char *); 69267180Sluigistatic void nomatch(const char *); 70267180Sluigistatic void pe(int); 71261909Sluigistatic void process(FILE *); 72261909Sluigistatic void prop(int); 73261909Sluigistatic void usage(void); 74261909Sluigi 75261909Sluigi/* 76261909Sluigi * The stack on which we remember what we've seen so far. 77261909Sluigi */ 78261909Sluigistatic struct stkstr { 79261909Sluigi int opno; /* number of opening bracket */ 80261909Sluigi int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ 81261909Sluigi int parm; /* parm to size, font, etc */ 82261909Sluigi int lno; /* line number */ 83261909Sluigi} stk[MAXSTK]; 84261909Sluigistatic int stktop; 85261909Sluigi 86261909Sluigi/* 87261909Sluigi * The kinds of opening and closing brackets. 88261909Sluigi */ 89267180Sluigistatic struct brstr { 90267180Sluigi const char *opbr; 91261909Sluigi const char *clbr; 92261909Sluigi} br[MAXBR] = { 93261909Sluigi /* A few bare bones troff commands */ 94261909Sluigi#define SZ 0 95261909Sluigi {"sz", "sz"}, /* also \s */ 96261909Sluigi#define FT 1 97261909Sluigi {"ft", "ft"}, /* also \f */ 98261909Sluigi /* the -mm package */ 99270063Sluigi {"AL", "LE"}, 100261909Sluigi {"AS", "AE"}, 101261909Sluigi {"BL", "LE"}, 102261909Sluigi {"BS", "BE"}, 103261909Sluigi {"DF", "DE"}, 104261909Sluigi {"DL", "LE"}, 105261909Sluigi {"DS", "DE"}, 106261909Sluigi {"FS", "FE"}, 107261909Sluigi {"ML", "LE"}, 108267180Sluigi {"NS", "NE"}, 109267180Sluigi {"RL", "LE"}, 110261909Sluigi {"VL", "LE"}, 111261909Sluigi /* the -ms package */ 112262238Sluigi {"AB", "AE"}, 113261909Sluigi {"BD", "DE"}, 114261909Sluigi {"CD", "DE"}, 115261909Sluigi {"DS", "DE"}, 116261909Sluigi {"FS", "FE"}, 117261909Sluigi {"ID", "DE"}, 118261909Sluigi {"KF", "KE"}, 119261909Sluigi {"KS", "KE"}, 120261909Sluigi {"LD", "DE"}, 121261909Sluigi {"LG", "NL"}, 122262238Sluigi {"QS", "QE"}, 123262238Sluigi {"RS", "RE"}, 124262238Sluigi {"SM", "NL"}, 125262238Sluigi {"XA", "XE"}, 126262238Sluigi {"XS", "XE"}, 127262238Sluigi /* The -me package */ 128262238Sluigi {"(b", ")b"}, 129261909Sluigi {"(c", ")c"}, 130261909Sluigi {"(d", ")d"}, 131267180Sluigi {"(f", ")f"}, 132267180Sluigi {"(l", ")l"}, 133261909Sluigi {"(q", ")q"}, 134261909Sluigi {"(x", ")x"}, 135261909Sluigi {"(z", ")z"}, 136261909Sluigi /* Things needed by preprocessors */ 137261909Sluigi {"EQ", "EN"}, 138261909Sluigi {"TS", "TE"}, 139261909Sluigi /* Refer */ 140261909Sluigi {"[", "]"}, 141261909Sluigi {0, 0} 142261909Sluigi}; 143261909Sluigi 144261909Sluigi/* 145261909Sluigi * All commands known to nroff, plus macro packages. 146261909Sluigi * Used so we can complain about unrecognized commands. 147261909Sluigi */ 148259412Sluigistatic const char *knowncmds[MAXCMDS] = { 149259412Sluigi"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t", 150259412Sluigi"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++", 151259412Sluigi"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M", 152259412Sluigi"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB", 153285349Sluigi"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B", "B1", "B2", 154259412Sluigi"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT", 155285349Sluigi"D", "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM", 156259412Sluigi"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", 157259412Sluigi"FQ", "FS", "FV", "FX", "H", "HC", "HD", "HM", "HO", "HU", "I", "ID", 158259412Sluigi"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB", 159259412Sluigi"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR", 160259412Sluigi"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P", 161259412Sluigi"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R", "RA", 162259412Sluigi"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S", "S0", "S2", "S3", "SA", 163259412Sluigi"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE", 164259412Sluigi"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL", 165259412Sluigi"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[", "[-", "[0", 166259412Sluigi"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]", "]-", "]<", "]>", 167259412Sluigi"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b", "ba", "bc", "bd", 168259412Sluigi"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs", 169259412Sluigi"ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec", 170259412Sluigi"ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo", 171259412Sluigi"fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i", 172259412Sluigi"ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln", 173259412Sluigi"lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1", 174259412Sluigi"n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx", 175259412Sluigi"of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps", 176259412Sluigi"q", "r", "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb", 177260368Sluigi"sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th", 178259412Sluigi"ti", "tl", "tm", "tp", "tr", "u", "uf", "uh", "ul", "vs", "wh", "xp", 179259412Sluigi"yr", 0 180259412Sluigi}; 181259412Sluigi 182261909Sluigistatic int lineno; /* current line number in input file */ 183259412Sluigistatic const char *cfilename; /* name of current file */ 184259412Sluigistatic int nfiles; /* number of files to process */ 185260368Sluigistatic int fflag; /* -f: ignore \f */ 186259412Sluigistatic int sflag; /* -s: ignore \s */ 187260368Sluigistatic int ncmds; /* size of knowncmds */ 188285349Sluigistatic int slot; /* slot in knowncmds found by binsrch */ 189260368Sluigi 190259412Sluigiint 191260368Sluigimain(int argc, char **argv) 192260368Sluigi{ 193259412Sluigi FILE *f; 194260368Sluigi int i; 195259412Sluigi char *cp; 196259412Sluigi char b1[4]; 197259412Sluigi 198260368Sluigi /* Figure out how many known commands there are */ 199261909Sluigi while (knowncmds[ncmds]) 200261909Sluigi ncmds++; 201259412Sluigi while (argc > 1 && argv[1][0] == '-') { 202259412Sluigi switch(argv[1][1]) { 203259412Sluigi 204259412Sluigi /* -a: add pairs of macros */ 205259412Sluigi case 'a': 206259412Sluigi i = strlen(argv[1]) - 2; 207259412Sluigi if (i % 6 != 0) 208259412Sluigi usage(); 209275358Shselasky /* look for empty macro slots */ 210259412Sluigi for (i=0; br[i].opbr; i++) 211259412Sluigi ; 212259412Sluigi for (cp=argv[1]+3; cp[-1]; cp += 6) { 213259412Sluigi br[i].opbr = strncpy(malloc(3), cp, 2); 214259412Sluigi br[i].clbr = strncpy(malloc(3), cp+3, 2); 215259412Sluigi addmac(br[i].opbr); /* knows pairs are also known cmds */ 216259412Sluigi addmac(br[i].clbr); 217259412Sluigi i++; 218259412Sluigi } 219259412Sluigi break; 220259412Sluigi 221267180Sluigi /* -c: add known commands */ 222267180Sluigi case 'c': 223267180Sluigi i = strlen(argv[1]) - 2; 224267180Sluigi if (i % 3 != 0) 225267180Sluigi usage(); 226259412Sluigi for (cp=argv[1]+3; cp[-1]; cp += 3) { 227270063Sluigi if (cp[2] && cp[2] != '.') 228267180Sluigi usage(); 229270063Sluigi strncpy(b1, cp, 2); 230267180Sluigi b1[2] = '\0'; 231267180Sluigi addmac(b1); 232267180Sluigi } 233267180Sluigi break; 234267180Sluigi 235267180Sluigi /* -f: ignore font changes */ 236267180Sluigi case 'f': 237267180Sluigi fflag = 1; 238267180Sluigi break; 239267180Sluigi 240267180Sluigi /* -s: ignore size changes */ 241267180Sluigi case 's': 242267180Sluigi sflag = 1; 243267180Sluigi break; 244270063Sluigi default: 245275358Shselasky usage(); 246259412Sluigi } 247259412Sluigi argc--; argv++; 248260368Sluigi } 249259412Sluigi 250259412Sluigi nfiles = argc - 1; 251259412Sluigi 252260368Sluigi if (nfiles > 0) { 253267170Sluigi for (i=1; i<argc; i++) { 254267170Sluigi cfilename = argv[i]; 255267170Sluigi f = fopen(cfilename, "r"); 256267170Sluigi if (f == NULL) 257267170Sluigi warn("%s", cfilename); 258267170Sluigi else { 259267170Sluigi process(f); 260267170Sluigi fclose(f); 261259412Sluigi } 262259412Sluigi } 263259412Sluigi } else { 264259412Sluigi cfilename = "stdin"; 265259412Sluigi process(stdin); 266259412Sluigi } 267259412Sluigi exit(0); 268267180Sluigi} 269259412Sluigi 270259412Sluigistatic void 271259412Sluigiusage(void) 272260368Sluigi{ 273259412Sluigi fprintf(stderr, 274259412Sluigi "usage: checknr [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [-s] [-f] file\n"); 275259412Sluigi exit(1); 276267180Sluigi} 277261909Sluigi 278261909Sluigistatic void 279259412Sluigiprocess(FILE *f) 280259412Sluigi{ 281260368Sluigi int i, n; 282267180Sluigi char mac[5]; /* The current macro or nroff command */ 283270063Sluigi int pl; 284259412Sluigi static char line[256]; /* the current line */ 285259412Sluigi 286261909Sluigi stktop = -1; 287270063Sluigi for (lineno = 1; fgets(line, sizeof line, f); lineno++) { 288261909Sluigi if (line[0] == '.') { 289259412Sluigi /* 290259412Sluigi * find and isolate the macro/command name. 291259412Sluigi */ 292267180Sluigi strncpy(mac, line+1, 4); 293267180Sluigi if (isspace(mac[0])) { 294259412Sluigi pe(lineno); 295259412Sluigi printf("Empty command\n"); 296259412Sluigi } else if (isspace(mac[1])) { 297259412Sluigi mac[1] = 0; 298260368Sluigi } else if (isspace(mac[2])) { 299267180Sluigi mac[2] = 0; 300267180Sluigi } else if (mac[0] != '\\' || mac[1] != '\"') { 301259412Sluigi pe(lineno); 302259412Sluigi printf("Command too long\n"); 303259412Sluigi } 304259412Sluigi 305260368Sluigi /* 306267180Sluigi * Is it a known command? 307267180Sluigi */ 308259412Sluigi checkknown(mac); 309259412Sluigi 310259412Sluigi /* 311259412Sluigi * Should we add it? 312259412Sluigi */ 313260368Sluigi if (eq(mac, "de")) 314267180Sluigi addcmd(line); 315267180Sluigi 316259412Sluigi chkcmd(line, mac); 317259412Sluigi } 318259412Sluigi 319259412Sluigi /* 320270063Sluigi * At this point we process the line looking 321270063Sluigi * for \s and \f. 322270063Sluigi */ 323270063Sluigi for (i=0; line[i]; i++) 324270063Sluigi if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) { 325260368Sluigi if (!sflag && line[++i]=='s') { 326270063Sluigi pl = line[++i]; 327270063Sluigi if (isdigit(pl)) { 328270063Sluigi n = pl - '0'; 329270063Sluigi pl = ' '; 330270063Sluigi } else 331270063Sluigi n = 0; 332259412Sluigi while (isdigit(line[++i])) 333270063Sluigi n = 10 * n + line[i] - '0'; 334270063Sluigi i--; 335270063Sluigi if (n == 0) { 336270063Sluigi if (stk[stktop].opno == SZ) { 337270063Sluigi stktop--; 338270063Sluigi } else { 339270063Sluigi pe(lineno); 340270063Sluigi printf("unmatched \\s0\n"); 341270063Sluigi } 342270063Sluigi } else { 343270063Sluigi stk[++stktop].opno = SZ; 344270063Sluigi stk[stktop].pl = pl; 345270063Sluigi stk[stktop].parm = n; 346270063Sluigi stk[stktop].lno = lineno; 347270063Sluigi } 348270063Sluigi } else if (!fflag && line[i]=='f') { 349270063Sluigi n = line[++i]; 350270063Sluigi if (n == 'P') { 351270063Sluigi if (stk[stktop].opno == FT) { 352270063Sluigi stktop--; 353270063Sluigi } else { 354270063Sluigi pe(lineno); 355270063Sluigi printf("unmatched \\fP\n"); 356270063Sluigi } 357270063Sluigi } else { 358270063Sluigi stk[++stktop].opno = FT; 359270063Sluigi stk[stktop].pl = 1; 360270063Sluigi stk[stktop].parm = n; 361270063Sluigi stk[stktop].lno = lineno; 362270063Sluigi } 363270063Sluigi } 364270063Sluigi } 365270063Sluigi } 366270063Sluigi /* 367270063Sluigi * We've hit the end and look at all this stuff that hasn't been 368270063Sluigi * matched yet! Complain, complain. 369270063Sluigi */ 370270063Sluigi for (i=stktop; i>=0; i--) { 371270063Sluigi complain(i); 372270063Sluigi } 373270063Sluigi} 374270063Sluigi 375270063Sluigistatic void 376270063Sluigicomplain(int i) 377270063Sluigi{ 378270063Sluigi pe(stk[i].lno); 379270063Sluigi printf("Unmatched "); 380270063Sluigi prop(i); 381270063Sluigi printf("\n"); 382270063Sluigi} 383270063Sluigi 384270063Sluigistatic void 385270063Sluigiprop(int i) 386270063Sluigi{ 387270063Sluigi if (stk[i].pl == 0) 388270063Sluigi printf(".%s", br[stk[i].opno].opbr); 389270063Sluigi else switch(stk[i].opno) { 390270063Sluigi case SZ: 391270063Sluigi printf("\\s%c%d", stk[i].pl, stk[i].parm); 392270063Sluigi break; 393270063Sluigi case FT: 394270063Sluigi printf("\\f%c", stk[i].parm); 395270063Sluigi break; 396270063Sluigi default: 397270063Sluigi printf("Bug: stk[%d].opno = %d = .%s, .%s", 398270063Sluigi i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr); 399270063Sluigi } 400270063Sluigi} 401270063Sluigi 402270063Sluigistatic void 403270063Sluigichkcmd(const char *line __unused, const char *mac) 404270063Sluigi{ 405270063Sluigi int i; 406270063Sluigi 407270063Sluigi /* 408270063Sluigi * Check to see if it matches top of stack. 409270063Sluigi */ 410270063Sluigi if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) 411270063Sluigi stktop--; /* OK. Pop & forget */ 412270063Sluigi else { 413270063Sluigi /* No. Maybe it's an opener */ 414270063Sluigi for (i=0; br[i].opbr; i++) { 415270063Sluigi if (eq(mac, br[i].opbr)) { 416270063Sluigi /* Found. Push it. */ 417270063Sluigi stktop++; 418270063Sluigi stk[stktop].opno = i; 419270063Sluigi stk[stktop].pl = 0; 420270063Sluigi stk[stktop].parm = 0; 421270063Sluigi stk[stktop].lno = lineno; 422270063Sluigi break; 423270063Sluigi } 424270063Sluigi /* 425270063Sluigi * Maybe it's an unmatched closer. 426270063Sluigi * NOTE: this depends on the fact 427270063Sluigi * that none of the closers can be 428270063Sluigi * openers too. 429270063Sluigi */ 430270063Sluigi if (eq(mac, br[i].clbr)) { 431270063Sluigi nomatch(mac); 432270063Sluigi break; 433270063Sluigi } 434270063Sluigi } 435270063Sluigi } 436270063Sluigi} 437270063Sluigi 438270063Sluigistatic void 439270063Sluiginomatch(const char *mac) 440270063Sluigi{ 441270063Sluigi int i, j; 442270063Sluigi 443270063Sluigi /* 444270063Sluigi * Look for a match further down on stack 445270063Sluigi * If we find one, it suggests that the stuff in 446270063Sluigi * between is supposed to match itself. 447270063Sluigi */ 448270063Sluigi for (j=stktop; j>=0; j--) 449270063Sluigi if (eq(mac,br[stk[j].opno].clbr)) { 450270063Sluigi /* Found. Make a good diagnostic. */ 451259412Sluigi if (j == stktop-2) { 452259412Sluigi /* 453259412Sluigi * Check for special case \fx..\fR and don't 454259412Sluigi * complain. 455259412Sluigi */ 456259412Sluigi if (stk[j+1].opno==FT && stk[j+1].parm!='R' 457259412Sluigi && stk[j+2].opno==FT && stk[j+2].parm=='R') { 458259412Sluigi stktop = j -1; 459259412Sluigi return; 460259412Sluigi } 461260368Sluigi /* 462259412Sluigi * We have two unmatched frobs. Chances are 463259412Sluigi * they were intended to match, so we mention 464259412Sluigi * them together. 465259412Sluigi */ 466259412Sluigi pe(stk[j+1].lno); 467261909Sluigi prop(j+1); 468261909Sluigi printf(" does not match %d: ", stk[j+2].lno); 469261909Sluigi prop(j+2); 470261909Sluigi printf("\n"); 471274354Sluigi } else for (i=j+1; i <= stktop; i++) { 472274354Sluigi complain(i); 473259412Sluigi } 474259412Sluigi stktop = j-1; 475259412Sluigi return; 476259412Sluigi } 477259412Sluigi /* Didn't find one. Throw this away. */ 478259412Sluigi pe(lineno); 479259412Sluigi printf("Unmatched .%s\n", mac); 480259412Sluigi} 481259412Sluigi 482259412Sluigi/* eq: are two strings equal? */ 483259412Sluigistatic int 484261909Sluigieq(const char *s1, const char *s2) 485261909Sluigi{ 486261909Sluigi return (strcmp(s1, s2) == 0); 487259412Sluigi} 488259412Sluigi 489259412Sluigi/* print the first part of an error message, given the line number */ 490259412Sluigistatic void 491259412Sluigipe(int linen) 492260368Sluigi{ 493259412Sluigi if (nfiles > 1) 494259412Sluigi printf("%s: ", cfilename); 495259412Sluigi printf("%d: ", linen); 496259412Sluigi} 497259412Sluigi 498259412Sluigistatic void 499285349Sluigicheckknown(const char *mac) 500259412Sluigi{ 501259412Sluigi 502259412Sluigi if (eq(mac, ".")) 503259412Sluigi return; 504259412Sluigi if (binsrch(mac) >= 0) 505259412Sluigi return; 506259412Sluigi if (mac[0] == '\\' && mac[1] == '"') /* comments */ 507259412Sluigi return; 508259412Sluigi 509285349Sluigi pe(lineno); 510259412Sluigi printf("Unknown command: .%s\n", mac); 511259412Sluigi} 512259412Sluigi 513259412Sluigi/* 514259412Sluigi * We have a .de xx line in "line". Add xx to the list of known commands. 515259412Sluigi */ 516259412Sluigistatic void 517259412Sluigiaddcmd(char *line) 518259412Sluigi{ 519259412Sluigi char *mac; 520259412Sluigi 521259412Sluigi /* grab the macro being defined */ 522259412Sluigi mac = line+4; 523259412Sluigi while (isspace(*mac)) 524259412Sluigi mac++; 525259412Sluigi if (*mac == 0) { 526259412Sluigi pe(lineno); 527259412Sluigi printf("illegal define: %s\n", line); 528259412Sluigi return; 529259412Sluigi } 530259412Sluigi mac[2] = 0; 531259412Sluigi if (isspace(mac[1]) || mac[1] == '\\') 532259412Sluigi mac[1] = 0; 533259412Sluigi if (ncmds >= MAXCMDS) { 534259412Sluigi printf("Only %d known commands allowed\n", MAXCMDS); 535259412Sluigi exit(1); 536259412Sluigi } 537259412Sluigi addmac(mac); 538259412Sluigi} 539259412Sluigi 540259412Sluigi/* 541259412Sluigi * Add mac to the list. We should really have some kind of tree 542259412Sluigi * structure here but this is a quick-and-dirty job and I just don't 543259412Sluigi * have time to mess with it. (I wonder if this will come back to haunt 544259412Sluigi * me someday?) Anyway, I claim that .de is fairly rare in user 545259412Sluigi * nroff programs, and the register loop below is pretty fast. 546259412Sluigi */ 547259412Sluigistatic void 548259412Sluigiaddmac(const char *mac) 549259412Sluigi{ 550259412Sluigi const char **src, **dest, **loc; 551259412Sluigi 552259412Sluigi if (binsrch(mac) >= 0){ /* it's OK to redefine something */ 553259412Sluigi#ifdef DEBUG 554259412Sluigi printf("binsrch(%s) -> already in table\n", mac); 555259412Sluigi#endif 556259412Sluigi return; 557259412Sluigi } 558259412Sluigi /* binsrch sets slot as a side effect */ 559259412Sluigi#ifdef DEBUG 560261909Sluigiprintf("binsrch(%s) -> %d\n", mac, slot); 561261909Sluigi#endif 562261909Sluigi loc = &knowncmds[slot]; 563259412Sluigi src = &knowncmds[ncmds-1]; 564259412Sluigi dest = src+1; 565259412Sluigi while (dest > loc) 566259412Sluigi *dest-- = *src--; 567259412Sluigi *loc = strcpy(malloc(3), mac); 568259412Sluigi ncmds++; 569259412Sluigi#ifdef DEBUG 570259412Sluigiprintf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds); 571259412Sluigi#endif 572259412Sluigi} 573259412Sluigi 574285349Sluigi/* 575285349Sluigi * Do a binary search in knowncmds for mac. 576285349Sluigi * If found, return the index. If not, return -1. 577285349Sluigi */ 578259412Sluigistatic int 579285359Sluigibinsrch(const char *mac) 580259412Sluigi{ 581259412Sluigi const char *p; /* pointer to current cmd in list */ 582259412Sluigi int d; /* difference if any */ 583259412Sluigi int mid; /* mid point in binary search */ 584259412Sluigi int top, bot; /* boundaries of bin search, inclusive */ 585259412Sluigi 586259412Sluigi top = ncmds-1; 587259412Sluigi bot = 0; 588259412Sluigi while (top >= bot) { 589259412Sluigi mid = (top+bot)/2; 590259412Sluigi p = knowncmds[mid]; 591259412Sluigi d = p[0] - mac[0]; 592259412Sluigi if (d == 0) 593259412Sluigi d = p[1] - mac[1]; 594259412Sluigi if (d == 0) 595259412Sluigi return mid; 596285359Sluigi if (d < 0) 597259412Sluigi bot = mid + 1; 598259412Sluigi else 599259412Sluigi top = mid - 1; 600259412Sluigi } 601259412Sluigi slot = bot; /* place it would have gone */ 602259412Sluigi return -1; 603259412Sluigi} 604285349Sluigi