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