1 1.25 mrg /* $NetBSD: checknr.c,v 1.25 2021/04/13 01:38:04 mrg Exp $ */ 2 1.4 glass 3 1.1 cgd /* 4 1.4 glass * Copyright (c) 1980, 1993 5 1.4 glass * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.12 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.5 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.20 lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ 35 1.20 lukem The Regents of the University of California. All rights reserved."); 36 1.1 cgd #endif /* not lint */ 37 1.1 cgd 38 1.1 cgd #ifndef lint 39 1.4 glass #if 0 40 1.4 glass static char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93"; 41 1.4 glass #else 42 1.25 mrg __RCSID("$NetBSD: checknr.c,v 1.25 2021/04/13 01:38:04 mrg Exp $"); 43 1.4 glass #endif 44 1.1 cgd #endif /* not lint */ 45 1.1 cgd 46 1.1 cgd /* 47 1.1 cgd * checknr: check an nroff/troff input file for matching macro calls. 48 1.1 cgd * we also attempt to match size and font changes, but only the embedded 49 1.1 cgd * kind. These must end in \s0 and \fP resp. Maybe more sophistication 50 1.1 cgd * later but for now think of these restrictions as contributions to 51 1.1 cgd * structured typesetting. 52 1.1 cgd */ 53 1.5 lukem #include <ctype.h> 54 1.17 xtraeme #include <err.h> 55 1.1 cgd #include <stdio.h> 56 1.5 lukem #include <stdlib.h> 57 1.3 cgd #include <string.h> 58 1.1 cgd 59 1.1 cgd #define MAXSTK 100 /* Stack size */ 60 1.1 cgd #define MAXBR 100 /* Max number of bracket pairs known */ 61 1.1 cgd #define MAXCMDS 500 /* Max number of commands known */ 62 1.1 cgd 63 1.1 cgd /* 64 1.1 cgd * The stack on which we remember what we've seen so far. 65 1.1 cgd */ 66 1.23 dholland static struct stkstr { 67 1.1 cgd int opno; /* number of opening bracket */ 68 1.1 cgd int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ 69 1.1 cgd int parm; /* parm to size, font, etc */ 70 1.1 cgd int lno; /* line number the thing came in in */ 71 1.1 cgd } stk[MAXSTK]; 72 1.23 dholland static int stktop; 73 1.1 cgd 74 1.1 cgd /* 75 1.1 cgd * The kinds of opening and closing brackets. 76 1.1 cgd */ 77 1.23 dholland static struct brstr { 78 1.21 dholland const char *opbr; 79 1.21 dholland const char *clbr; 80 1.1 cgd } br[MAXBR] = { 81 1.1 cgd /* A few bare bones troff commands */ 82 1.1 cgd #define SZ 0 83 1.5 lukem { "sz", "sz"}, /* also \s */ 84 1.1 cgd #define FT 1 85 1.5 lukem { "ft", "ft"}, /* also \f */ 86 1.1 cgd /* the -mm package */ 87 1.5 lukem {"AL", "LE"}, 88 1.5 lukem {"AS", "AE"}, 89 1.5 lukem {"BL", "LE"}, 90 1.5 lukem {"BS", "BE"}, 91 1.5 lukem {"DF", "DE"}, 92 1.5 lukem {"DL", "LE"}, 93 1.5 lukem {"DS", "DE"}, 94 1.5 lukem {"FS", "FE"}, 95 1.5 lukem {"ML", "LE"}, 96 1.5 lukem {"NS", "NE"}, 97 1.5 lukem {"RL", "LE"}, 98 1.5 lukem {"VL", "LE"}, 99 1.1 cgd /* the -ms package */ 100 1.5 lukem {"AB", "AE"}, 101 1.5 lukem {"BD", "DE"}, 102 1.5 lukem {"CD", "DE"}, 103 1.5 lukem {"DS", "DE"}, 104 1.5 lukem {"FS", "FE"}, 105 1.5 lukem {"ID", "DE"}, 106 1.5 lukem {"KF", "KE"}, 107 1.5 lukem {"KS", "KE"}, 108 1.5 lukem {"LD", "DE"}, 109 1.5 lukem {"LG", "NL"}, 110 1.5 lukem {"QS", "QE"}, 111 1.5 lukem {"RS", "RE"}, 112 1.5 lukem {"SM", "NL"}, 113 1.5 lukem {"XA", "XE"}, 114 1.5 lukem {"XS", "XE"}, 115 1.1 cgd /* The -me package */ 116 1.5 lukem {"(b", ")b"}, 117 1.5 lukem {"(c", ")c"}, 118 1.5 lukem {"(d", ")d"}, 119 1.5 lukem {"(f", ")f"}, 120 1.5 lukem {"(l", ")l"}, 121 1.5 lukem {"(q", ")q"}, 122 1.5 lukem {"(x", ")x"}, 123 1.5 lukem {"(z", ")z"}, 124 1.10 wiz /* The -mdoc package */ 125 1.10 wiz {"Ao", "Ac"}, 126 1.10 wiz {"Bd", "Ed"}, 127 1.10 wiz {"Bk", "Ek"}, 128 1.10 wiz {"Bo", "Bc"}, 129 1.10 wiz {"Do", "Dc"}, 130 1.10 wiz {"Fo", "Fc"}, 131 1.10 wiz {"Oo", "Oc"}, 132 1.10 wiz {"Po", "Pc"}, 133 1.10 wiz {"Qo", "Qc"}, 134 1.10 wiz {"Rs", "Re"}, 135 1.10 wiz {"So", "Sc"}, 136 1.10 wiz {"Xo", "Xc"}, 137 1.1 cgd /* Things needed by preprocessors */ 138 1.5 lukem {"EQ", "EN"}, 139 1.5 lukem {"TS", "TE"}, 140 1.1 cgd /* Refer */ 141 1.5 lukem {"[", "]"}, 142 1.16 wiz {0, 0} 143 1.1 cgd }; 144 1.1 cgd 145 1.1 cgd /* 146 1.1 cgd * All commands known to nroff, plus macro packages. 147 1.1 cgd * Used so we can complain about unrecognized commands. 148 1.1 cgd */ 149 1.23 dholland static const char *knowncmds[MAXCMDS] = { 150 1.10 wiz "$c", "$f", "$h", "$p", "$s", "%A", "%B", "%C", "%D", "%I", "%J", "%N", 151 1.10 wiz "%O", "%P", "%Q", "%R", "%T", "%V", "(b", "(c", "(d", "(f", "(l", "(q", 152 1.10 wiz "(t", "(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", 153 1.10 wiz ")z", "++", "+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", 154 1.10 wiz "@F", "@I", "@M", "@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", 155 1.10 wiz "@r", "@t", "@z", "AB", "AE", "AF", "AI", "AL", "AM", "AS", "AT", 156 1.10 wiz "AU", "AX", "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "B" , "B1", 157 1.10 wiz "B2", "BD", "BE", "BG", "BL", "BS", "BT", "BX", "Bc", "Bd", "Bf", 158 1.10 wiz "Bk", "Bl", "Bo", "Bq", "Bsx", "Bx", "C1", "C2", "CD", "CM", "CT", 159 1.10 wiz "Cd", "Cm", "D" , "D1", "DA", "DE", "DF", "DL", "DS", "DT", "Db", "Dc", 160 1.10 wiz "Dd", "Dl", "Do", "Dq", "Dt", "Dv", "EC", "EF", "EG", "EH", "EM", 161 1.10 wiz "EN", "EQ", "EX", "Ec", "Ed", "Ef", "Ek", "El", "Em", "Eo", "Er", 162 1.10 wiz "Ev", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", "FQ", 163 1.10 wiz "FS", "FV", "FX", "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Ft", "Fx", 164 1.10 wiz "H" , "HC", "HD", "HM", "HO", "HU", "I" , "ID", "IE", "IH", "IM", 165 1.15 wiz "IP", "IX", "IZ", "Ic", "In", "It", "KD", "KE", "KF", "KQ", "KS", "LB", 166 1.10 wiz "LC", "LD", "LE", "LG", "LI", "LP", "Lb", "Li", "MC", "ME", "MF", 167 1.10 wiz "MH", "ML", "MR", "MT", "ND", "NE", "NH", "NL", "NP", "NS", "Nd", 168 1.10 wiz "Nm", "No", "Ns", "Nx", "OF", "OH", "OK", "OP", "Oc", "Oo", "Op", 169 1.10 wiz "Os", "Ot", "Ox", "P" , "P1", "PF", "PH", "PP", "PT", "PX", "PY", 170 1.10 wiz "Pa", "Pc", "Pf", "Po", "Pp", "Pq", "QE", "QP", "QS", "Qc", "Ql", 171 1.10 wiz "Qo", "Qq", "R" , "RA", "RC", "RE", "RL", "RP", "RQ", "RS", "RT", 172 1.10 wiz "Re", "Rs", "S" , "S0", "S2", "S3", "SA", "SG", "SH", "SK", "SM", 173 1.10 wiz "SP", "SY", "Sc", "Sh", "Sm", "So", "Sq", "Ss", "St", "Sx", "Sy", 174 1.10 wiz "T&", "TA", "TB", "TC", "TD", "TE", "TH", "TL", "TM", "TP", "TQ", 175 1.10 wiz "TR", "TS", "TX", "Tn", "UL", "US", "UX", "Ud", "Ux", "VL", "Va", "Vt", 176 1.10 wiz "WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "Xc", "Xo", 177 1.10 wiz "Xr", "[" , "[-", "[0", "[1", "[2", "[3", "[4", "[5", "[<", "[>", 178 1.10 wiz "[]", "\\{", "\\}", "]" , "]-", "]<", "]>", "][", "ab", "ac", "ad", "af", "am", 179 1.10 wiz "ar", "as", "b" , "ba", "bc", "bd", "bi", "bl", "bp", "br", "bx", 180 1.10 wiz "c.", "c2", "cc", "ce", "cf", "ch", "cs", "ct", "cu", "da", "de", 181 1.10 wiz "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec", "ef", "eh", "el", 182 1.10 wiz "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo", "fp", "ft", 183 1.10 wiz "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i" , "ie", 184 1.10 wiz "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln", 185 1.10 wiz "lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", 186 1.10 wiz "n1", "n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", 187 1.10 wiz "ns", "nx", "of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", 188 1.10 wiz "po", "pp", "ps", "q" , "r" , "rb", "rd", "re", "rm", "rn", "ro", 189 1.10 wiz "rr", "rs", "rt", "sb", "sc", "sh", "sk", "so", "sp", "ss", "st", 190 1.10 wiz "sv", "sz", "ta", "tc", "th", "ti", "tl", "tm", "tp", "tr", "u", 191 1.10 wiz "uf", "uh", "ul", "vs", "wh", "xp", "yr", 0 192 1.1 cgd }; 193 1.1 cgd 194 1.23 dholland static int lineno; /* current line number in input file */ 195 1.23 dholland static const char *cfilename; /* name of current file */ 196 1.23 dholland static int nfiles; /* number of files to process */ 197 1.23 dholland static int fflag; /* -f: ignore \f */ 198 1.23 dholland static int sflag; /* -s: ignore \s */ 199 1.23 dholland static int ncmds; /* size of knowncmds */ 200 1.23 dholland static int slot; /* slot in knowncmds found by binsrch */ 201 1.23 dholland 202 1.23 dholland static void addcmd(char *); 203 1.23 dholland static void addmac(const char *); 204 1.23 dholland static int binsrch(const char *); 205 1.23 dholland static void checkknown(const char *); 206 1.23 dholland static void chkcmd(const char *); 207 1.23 dholland static void complain(int); 208 1.21 dholland static int eq(const char *, const char *); 209 1.23 dholland static void nomatch(const char *); 210 1.23 dholland static void pe(int); 211 1.23 dholland static void process(FILE *); 212 1.23 dholland static void prop(int); 213 1.24 joerg static void usage(void) __dead; 214 1.1 cgd 215 1.5 lukem int 216 1.8 wiz main(int argc, char **argv) 217 1.1 cgd { 218 1.1 cgd FILE *f; 219 1.1 cgd int i; 220 1.1 cgd char *cp; 221 1.1 cgd char b1[4]; 222 1.1 cgd 223 1.1 cgd /* Figure out how many known commands there are */ 224 1.1 cgd while (knowncmds[ncmds]) 225 1.1 cgd ncmds++; 226 1.1 cgd while (argc > 1 && argv[1][0] == '-') { 227 1.1 cgd switch(argv[1][1]) { 228 1.1 cgd 229 1.1 cgd /* -a: add pairs of macros */ 230 1.1 cgd case 'a': 231 1.1 cgd i = strlen(argv[1]) - 2; 232 1.1 cgd if (i % 6 != 0) 233 1.1 cgd usage(); 234 1.1 cgd /* look for empty macro slots */ 235 1.1 cgd for (i=0; br[i].opbr; i++) 236 1.1 cgd ; 237 1.1 cgd for (cp=argv[1]+3; cp[-1]; cp += 6) { 238 1.21 dholland char *tmp; 239 1.21 dholland 240 1.16 wiz if (i >= MAXBR) 241 1.16 wiz errx(1, "too many pairs"); 242 1.21 dholland if ((tmp = malloc(3)) == NULL) 243 1.16 wiz err(1, "malloc"); 244 1.21 dholland strlcpy(tmp, cp, 3); 245 1.21 dholland br[i].opbr = tmp; 246 1.21 dholland if ((tmp = malloc(3)) == NULL) 247 1.16 wiz err(1, "malloc"); 248 1.21 dholland strlcpy(tmp, cp+3, 3); 249 1.21 dholland br[i].clbr = tmp; 250 1.1 cgd addmac(br[i].opbr); /* knows pairs are also known cmds */ 251 1.1 cgd addmac(br[i].clbr); 252 1.1 cgd i++; 253 1.1 cgd } 254 1.1 cgd break; 255 1.1 cgd 256 1.1 cgd /* -c: add known commands */ 257 1.1 cgd case 'c': 258 1.1 cgd i = strlen(argv[1]) - 2; 259 1.1 cgd if (i % 3 != 0) 260 1.1 cgd usage(); 261 1.1 cgd for (cp=argv[1]+3; cp[-1]; cp += 3) { 262 1.1 cgd if (cp[2] && cp[2] != '.') 263 1.1 cgd usage(); 264 1.1 cgd strncpy(b1, cp, 2); 265 1.1 cgd addmac(b1); 266 1.1 cgd } 267 1.1 cgd break; 268 1.1 cgd 269 1.1 cgd /* -f: ignore font changes */ 270 1.1 cgd case 'f': 271 1.1 cgd fflag = 1; 272 1.1 cgd break; 273 1.1 cgd 274 1.1 cgd /* -s: ignore size changes */ 275 1.1 cgd case 's': 276 1.1 cgd sflag = 1; 277 1.1 cgd break; 278 1.1 cgd default: 279 1.1 cgd usage(); 280 1.1 cgd } 281 1.1 cgd argc--; argv++; 282 1.1 cgd } 283 1.1 cgd 284 1.1 cgd nfiles = argc - 1; 285 1.1 cgd 286 1.1 cgd if (nfiles > 0) { 287 1.1 cgd for (i=1; i<argc; i++) { 288 1.1 cgd cfilename = argv[i]; 289 1.1 cgd f = fopen(cfilename, "r"); 290 1.1 cgd if (f == NULL) 291 1.1 cgd perror(cfilename); 292 1.11 wiz else { 293 1.1 cgd process(f); 294 1.11 wiz fclose(f); 295 1.11 wiz } 296 1.1 cgd } 297 1.1 cgd } else { 298 1.1 cgd cfilename = "stdin"; 299 1.1 cgd process(stdin); 300 1.1 cgd } 301 1.1 cgd exit(0); 302 1.1 cgd } 303 1.1 cgd 304 1.23 dholland static void 305 1.8 wiz usage(void) 306 1.1 cgd { 307 1.14 wiz (void)fprintf(stderr, 308 1.14 wiz "usage: %s [-fs] [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] file\n", 309 1.14 wiz getprogname()); 310 1.1 cgd exit(1); 311 1.1 cgd } 312 1.1 cgd 313 1.23 dholland static void 314 1.8 wiz process(FILE *f) 315 1.1 cgd { 316 1.5 lukem int i, n; 317 1.9 wiz char line[256]; /* the current line */ 318 1.1 cgd char mac[5]; /* The current macro or nroff command */ 319 1.1 cgd int pl; 320 1.1 cgd 321 1.1 cgd stktop = -1; 322 1.1 cgd for (lineno = 1; fgets(line, sizeof line, f); lineno++) { 323 1.1 cgd if (line[0] == '.') { 324 1.1 cgd /* 325 1.1 cgd * find and isolate the macro/command name. 326 1.1 cgd */ 327 1.1 cgd strncpy(mac, line+1, 4); 328 1.25 mrg mac[4] = '\0'; 329 1.6 christos if (isspace((unsigned char)mac[0])) { 330 1.1 cgd pe(lineno); 331 1.1 cgd printf("Empty command\n"); 332 1.6 christos } else if (isspace((unsigned char)mac[1])) { 333 1.1 cgd mac[1] = 0; 334 1.6 christos } else if (isspace((unsigned char)mac[2])) { 335 1.1 cgd mac[2] = 0; 336 1.1 cgd } else if (mac[0] != '\\' || mac[1] != '\"') { 337 1.1 cgd pe(lineno); 338 1.1 cgd printf("Command too long\n"); 339 1.1 cgd } 340 1.1 cgd 341 1.1 cgd /* 342 1.1 cgd * Is it a known command? 343 1.1 cgd */ 344 1.1 cgd checkknown(mac); 345 1.1 cgd 346 1.1 cgd /* 347 1.1 cgd * Should we add it? 348 1.1 cgd */ 349 1.1 cgd if (eq(mac, "de")) 350 1.1 cgd addcmd(line); 351 1.1 cgd 352 1.22 dholland chkcmd(mac); 353 1.1 cgd } 354 1.1 cgd 355 1.1 cgd /* 356 1.1 cgd * At this point we process the line looking 357 1.1 cgd * for \s and \f. 358 1.1 cgd */ 359 1.1 cgd for (i=0; line[i]; i++) 360 1.1 cgd if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) { 361 1.1 cgd if (!sflag && line[++i]=='s') { 362 1.1 cgd pl = line[++i]; 363 1.6 christos if (isdigit((unsigned char)pl)) { 364 1.1 cgd n = pl - '0'; 365 1.1 cgd pl = ' '; 366 1.1 cgd } else 367 1.1 cgd n = 0; 368 1.6 christos while (isdigit((unsigned char)line[++i])) 369 1.1 cgd n = 10 * n + line[i] - '0'; 370 1.1 cgd i--; 371 1.1 cgd if (n == 0) { 372 1.18 christos if (stktop >= 0 && 373 1.18 christos stk[stktop].opno == SZ) { 374 1.1 cgd stktop--; 375 1.1 cgd } else { 376 1.1 cgd pe(lineno); 377 1.1 cgd printf("unmatched \\s0\n"); 378 1.1 cgd } 379 1.1 cgd } else { 380 1.1 cgd stk[++stktop].opno = SZ; 381 1.1 cgd stk[stktop].pl = pl; 382 1.1 cgd stk[stktop].parm = n; 383 1.1 cgd stk[stktop].lno = lineno; 384 1.1 cgd } 385 1.1 cgd } else if (!fflag && line[i]=='f') { 386 1.1 cgd n = line[++i]; 387 1.1 cgd if (n == 'P') { 388 1.19 christos if (stktop >= 0 && 389 1.19 christos stk[stktop].opno == FT) { 390 1.1 cgd stktop--; 391 1.1 cgd } else { 392 1.1 cgd pe(lineno); 393 1.1 cgd printf("unmatched \\fP\n"); 394 1.1 cgd } 395 1.1 cgd } else { 396 1.1 cgd stk[++stktop].opno = FT; 397 1.1 cgd stk[stktop].pl = 1; 398 1.1 cgd stk[stktop].parm = n; 399 1.1 cgd stk[stktop].lno = lineno; 400 1.1 cgd } 401 1.1 cgd } 402 1.1 cgd } 403 1.1 cgd } 404 1.1 cgd /* 405 1.1 cgd * We've hit the end and look at all this stuff that hasn't been 406 1.1 cgd * matched yet! Complain, complain. 407 1.1 cgd */ 408 1.1 cgd for (i=stktop; i>=0; i--) { 409 1.1 cgd complain(i); 410 1.1 cgd } 411 1.1 cgd } 412 1.1 cgd 413 1.23 dholland static void 414 1.8 wiz complain(int i) 415 1.1 cgd { 416 1.1 cgd pe(stk[i].lno); 417 1.1 cgd printf("Unmatched "); 418 1.1 cgd prop(i); 419 1.1 cgd printf("\n"); 420 1.1 cgd } 421 1.1 cgd 422 1.23 dholland static void 423 1.8 wiz prop(int i) 424 1.1 cgd { 425 1.1 cgd if (stk[i].pl == 0) 426 1.1 cgd printf(".%s", br[stk[i].opno].opbr); 427 1.1 cgd else switch(stk[i].opno) { 428 1.1 cgd case SZ: 429 1.1 cgd printf("\\s%c%d", stk[i].pl, stk[i].parm); 430 1.1 cgd break; 431 1.1 cgd case FT: 432 1.1 cgd printf("\\f%c", stk[i].parm); 433 1.1 cgd break; 434 1.1 cgd default: 435 1.1 cgd printf("Bug: stk[%d].opno = %d = .%s, .%s", 436 1.5 lukem i, stk[i].opno, br[stk[i].opno].opbr, 437 1.5 lukem br[stk[i].opno].clbr); 438 1.1 cgd } 439 1.1 cgd } 440 1.1 cgd 441 1.23 dholland static void 442 1.22 dholland chkcmd(const char *mac) 443 1.1 cgd { 444 1.5 lukem int i; 445 1.1 cgd 446 1.1 cgd /* 447 1.1 cgd * Check to see if it matches top of stack. 448 1.1 cgd */ 449 1.1 cgd if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) 450 1.1 cgd stktop--; /* OK. Pop & forget */ 451 1.1 cgd else { 452 1.1 cgd /* No. Maybe it's an opener */ 453 1.1 cgd for (i=0; br[i].opbr; i++) { 454 1.1 cgd if (eq(mac, br[i].opbr)) { 455 1.1 cgd /* Found. Push it. */ 456 1.1 cgd stktop++; 457 1.1 cgd stk[stktop].opno = i; 458 1.1 cgd stk[stktop].pl = 0; 459 1.1 cgd stk[stktop].parm = 0; 460 1.1 cgd stk[stktop].lno = lineno; 461 1.1 cgd break; 462 1.1 cgd } 463 1.1 cgd /* 464 1.1 cgd * Maybe it's an unmatched closer. 465 1.1 cgd * NOTE: this depends on the fact 466 1.1 cgd * that none of the closers can be 467 1.1 cgd * openers too. 468 1.1 cgd */ 469 1.1 cgd if (eq(mac, br[i].clbr)) { 470 1.1 cgd nomatch(mac); 471 1.1 cgd break; 472 1.1 cgd } 473 1.1 cgd } 474 1.1 cgd } 475 1.1 cgd } 476 1.1 cgd 477 1.23 dholland static void 478 1.22 dholland nomatch(const char *mac) 479 1.1 cgd { 480 1.5 lukem int i, j; 481 1.1 cgd 482 1.1 cgd /* 483 1.1 cgd * Look for a match further down on stack 484 1.1 cgd * If we find one, it suggests that the stuff in 485 1.1 cgd * between is supposed to match itself. 486 1.1 cgd */ 487 1.1 cgd for (j=stktop; j>=0; j--) 488 1.1 cgd if (eq(mac,br[stk[j].opno].clbr)) { 489 1.1 cgd /* Found. Make a good diagnostic. */ 490 1.1 cgd if (j == stktop-2) { 491 1.1 cgd /* 492 1.1 cgd * Check for special case \fx..\fR and don't 493 1.1 cgd * complain. 494 1.1 cgd */ 495 1.1 cgd if (stk[j+1].opno==FT && stk[j+1].parm!='R' 496 1.1 cgd && stk[j+2].opno==FT && stk[j+2].parm=='R') { 497 1.1 cgd stktop = j -1; 498 1.1 cgd return; 499 1.1 cgd } 500 1.1 cgd /* 501 1.1 cgd * We have two unmatched frobs. Chances are 502 1.1 cgd * they were intended to match, so we mention 503 1.1 cgd * them together. 504 1.1 cgd */ 505 1.1 cgd pe(stk[j+1].lno); 506 1.1 cgd prop(j+1); 507 1.1 cgd printf(" does not match %d: ", stk[j+2].lno); 508 1.1 cgd prop(j+2); 509 1.1 cgd printf("\n"); 510 1.1 cgd } else for (i=j+1; i <= stktop; i++) { 511 1.1 cgd complain(i); 512 1.1 cgd } 513 1.1 cgd stktop = j-1; 514 1.1 cgd return; 515 1.1 cgd } 516 1.1 cgd /* Didn't find one. Throw this away. */ 517 1.1 cgd pe(lineno); 518 1.1 cgd printf("Unmatched .%s\n", mac); 519 1.1 cgd } 520 1.1 cgd 521 1.1 cgd /* eq: are two strings equal? */ 522 1.21 dholland static int 523 1.21 dholland eq(const char *s1, const char *s2) 524 1.1 cgd { 525 1.21 dholland return strcmp(s1, s2) == 0; 526 1.1 cgd } 527 1.1 cgd 528 1.1 cgd /* print the first part of an error message, given the line number */ 529 1.23 dholland static void 530 1.9 wiz pe(int pelineno) 531 1.1 cgd { 532 1.1 cgd if (nfiles > 1) 533 1.1 cgd printf("%s: ", cfilename); 534 1.9 wiz printf("%d: ", pelineno); 535 1.1 cgd } 536 1.1 cgd 537 1.23 dholland static void 538 1.22 dholland checkknown(const char *mac) 539 1.1 cgd { 540 1.1 cgd 541 1.1 cgd if (eq(mac, ".")) 542 1.1 cgd return; 543 1.1 cgd if (binsrch(mac) >= 0) 544 1.1 cgd return; 545 1.1 cgd if (mac[0] == '\\' && mac[1] == '"') /* comments */ 546 1.1 cgd return; 547 1.1 cgd 548 1.1 cgd pe(lineno); 549 1.1 cgd printf("Unknown command: .%s\n", mac); 550 1.1 cgd } 551 1.1 cgd 552 1.1 cgd /* 553 1.1 cgd * We have a .de xx line in "line". Add xx to the list of known commands. 554 1.1 cgd */ 555 1.23 dholland static void 556 1.8 wiz addcmd(char *line) 557 1.1 cgd { 558 1.1 cgd char *mac; 559 1.1 cgd 560 1.1 cgd /* grab the macro being defined */ 561 1.1 cgd mac = line+4; 562 1.6 christos while (isspace((unsigned char)*mac)) 563 1.1 cgd mac++; 564 1.1 cgd if (*mac == 0) { 565 1.1 cgd pe(lineno); 566 1.1 cgd printf("illegal define: %s\n", line); 567 1.1 cgd return; 568 1.1 cgd } 569 1.1 cgd mac[2] = 0; 570 1.6 christos if (isspace((unsigned char)mac[1]) || mac[1] == '\\') 571 1.1 cgd mac[1] = 0; 572 1.1 cgd if (ncmds >= MAXCMDS) { 573 1.1 cgd printf("Only %d known commands allowed\n", MAXCMDS); 574 1.1 cgd exit(1); 575 1.1 cgd } 576 1.1 cgd addmac(mac); 577 1.1 cgd } 578 1.1 cgd 579 1.1 cgd /* 580 1.1 cgd * Add mac to the list. We should really have some kind of tree 581 1.1 cgd * structure here but this is a quick-and-dirty job and I just don't 582 1.1 cgd * have time to mess with it. (I wonder if this will come back to haunt 583 1.1 cgd * me someday?) Anyway, I claim that .de is fairly rare in user 584 1.1 cgd * nroff programs, and the register loop below is pretty fast. 585 1.1 cgd */ 586 1.23 dholland static void 587 1.21 dholland addmac(const char *mac) 588 1.1 cgd { 589 1.21 dholland const char **src, **dest, **loc; 590 1.1 cgd 591 1.1 cgd if (binsrch(mac) >= 0){ /* it's OK to redefine something */ 592 1.1 cgd #ifdef DEBUG 593 1.1 cgd printf("binsrch(%s) -> already in table\n", mac); 594 1.7 cgd #endif /* DEBUG */ 595 1.1 cgd return; 596 1.1 cgd } 597 1.1 cgd /* binsrch sets slot as a side effect */ 598 1.1 cgd #ifdef DEBUG 599 1.9 wiz printf("binsrch(%s) -> %d\n", mac, slot); 600 1.1 cgd #endif 601 1.1 cgd loc = &knowncmds[slot]; 602 1.1 cgd src = &knowncmds[ncmds-1]; 603 1.1 cgd dest = src+1; 604 1.1 cgd while (dest > loc) 605 1.1 cgd *dest-- = *src--; 606 1.16 wiz if ((*loc = strdup(mac)) == NULL) 607 1.16 wiz err(1, "strdup"); 608 1.1 cgd ncmds++; 609 1.1 cgd #ifdef DEBUG 610 1.9 wiz printf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], 611 1.9 wiz knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], 612 1.9 wiz knowncmds[slot+2], ncmds); 613 1.1 cgd #endif 614 1.1 cgd } 615 1.1 cgd 616 1.1 cgd /* 617 1.1 cgd * Do a binary search in knowncmds for mac. 618 1.1 cgd * If found, return the index. If not, return -1. 619 1.1 cgd */ 620 1.23 dholland static int 621 1.21 dholland binsrch(const char *mac) 622 1.1 cgd { 623 1.21 dholland const char *p; /* pointer to current cmd in list */ 624 1.5 lukem int d; /* difference if any */ 625 1.5 lukem int mid; /* mid point in binary search */ 626 1.5 lukem int top, bot; /* boundaries of bin search, inclusive */ 627 1.1 cgd 628 1.1 cgd top = ncmds-1; 629 1.1 cgd bot = 0; 630 1.1 cgd while (top >= bot) { 631 1.1 cgd mid = (top+bot)/2; 632 1.1 cgd p = knowncmds[mid]; 633 1.1 cgd d = p[0] - mac[0]; 634 1.1 cgd if (d == 0) 635 1.1 cgd d = p[1] - mac[1]; 636 1.1 cgd if (d == 0) 637 1.1 cgd return mid; 638 1.1 cgd if (d < 0) 639 1.1 cgd bot = mid + 1; 640 1.1 cgd else 641 1.1 cgd top = mid - 1; 642 1.1 cgd } 643 1.1 cgd slot = bot; /* place it would have gone */ 644 1.1 cgd return -1; 645 1.1 cgd } 646