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