]> git.cameronkatri.com Git - mandoc.git/blob - macro.c
Lintified sources.
[mandoc.git] / macro.c
1 /* $Id: macro.c,v 1.54 2009/02/27 09:39:40 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #ifdef __linux__
25 #include <time.h>
26 #endif
27
28 /*
29 * This has scanning/parsing routines, each of which extract a macro and
30 * its arguments and parameters, then know how to progress to the next
31 * macro.
32 */
33
34 #include "private.h"
35
36 static int macro_obsolete(MACRO_PROT_ARGS);
37 static int macro_constant(MACRO_PROT_ARGS);
38 static int macro_constant_scoped(MACRO_PROT_ARGS);
39 static int macro_constant_delimited(MACRO_PROT_ARGS);
40 static int macro_text(MACRO_PROT_ARGS);
41 static int macro_scoped(MACRO_PROT_ARGS);
42 static int macro_scoped_close(MACRO_PROT_ARGS);
43 static int macro_scoped_line(MACRO_PROT_ARGS);
44
45 #define REWIND_REWIND (1 << 0)
46 #define REWIND_NOHALT (1 << 1)
47 #define REWIND_HALT (1 << 2)
48
49 static int rewind_dohalt(int, enum mdoc_type,
50 const struct mdoc_node *);
51 static int rewind_alt(int);
52 static int rewind_dobreak(int, const struct mdoc_node *);
53 static int rewind_elem(struct mdoc *, int);
54 static int rewind_impblock(struct mdoc *, int, int, int);
55 static int rewind_expblock(struct mdoc *, int, int, int);
56 static int rewind_subblock(enum mdoc_type,
57 struct mdoc *, int, int, int);
58 static int rewind_last(struct mdoc *, struct mdoc_node *);
59 static int append_delims(struct mdoc *, int, int *, char *);
60 static int lookup(struct mdoc *, int, int, int, const char *);
61 static int pwarn(struct mdoc *, int, int, int);
62 static int perr(struct mdoc *, int, int, int);
63
64 #define WMACPARM (1)
65 #define WOBS (2)
66
67 #define ENOCTX (1)
68 #define ENOPARMS (2)
69 #define EARGVLIM (3)
70
71 /* Central table of library: who gets parsed how. */
72
73 const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
74 { NULL, 0 }, /* \" */
75 { macro_constant, MDOC_PROLOGUE }, /* Dd */
76 { macro_constant, MDOC_PROLOGUE }, /* Dt */
77 { macro_constant, MDOC_PROLOGUE }, /* Os */
78 { macro_scoped, 0 }, /* Sh */
79 { macro_scoped, 0 }, /* Ss */
80 { macro_text, 0 }, /* Pp */
81 { macro_scoped_line, MDOC_PARSED }, /* D1 */
82 { macro_scoped_line, MDOC_PARSED }, /* Dl */
83 { macro_scoped, MDOC_EXPLICIT }, /* Bd */
84 { macro_scoped_close, MDOC_EXPLICIT }, /* Ed */
85 { macro_scoped, MDOC_EXPLICIT }, /* Bl */
86 { macro_scoped_close, MDOC_EXPLICIT }, /* El */
87 { macro_scoped, MDOC_PARSED }, /* It */
88 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
89 { macro_text, MDOC_PARSED }, /* An */
90 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
91 { macro_constant, 0 }, /* Cd */
92 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
93 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
94 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
95 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
96 { macro_constant, 0 }, /* Ex */
97 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
98 { macro_constant, 0 }, /* Fd */
99 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
100 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
101 { macro_text, MDOC_PARSED }, /* Ft */
102 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
103 { macro_constant, 0 }, /* In */
104 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
105 { macro_constant, 0 }, /* Nd */
106 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
107 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
108 { macro_obsolete, 0 }, /* Ot */
109 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
110 { macro_constant, 0 }, /* Rv */
111 /* XXX - .St supposed to be (but isn't) callable. */
112 { macro_constant_delimited, MDOC_PARSED }, /* St */
113 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
114 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
115 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
116 { macro_constant, 0 }, /* %A */
117 { macro_constant, 0 }, /* %B */
118 { macro_constant, 0 }, /* %D */
119 { macro_constant, 0 }, /* %I */
120 { macro_constant, 0 }, /* %J */
121 { macro_constant, 0 }, /* %N */
122 { macro_constant, 0 }, /* %O */
123 { macro_constant, 0 }, /* %P */
124 { macro_constant, 0 }, /* %R */
125 { macro_constant, 0 }, /* %T */
126 { macro_constant, 0 }, /* %V */
127 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
128 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */
129 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
130 { macro_constant_delimited, 0 }, /* At */
131 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
132 { macro_scoped, MDOC_EXPLICIT }, /* Bf */
133 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */
134 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */
135 { macro_constant_delimited, MDOC_PARSED }, /* Bsx */
136 { macro_constant_delimited, MDOC_PARSED }, /* Bx */
137 { macro_constant, 0 }, /* Db */
138 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
139 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */
140 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
141 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
142 { macro_scoped_close, MDOC_EXPLICIT }, /* Ef */
143 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Em */
144 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
145 { macro_constant_delimited, MDOC_PARSED }, /* Fx */
146 { macro_text, MDOC_PARSED }, /* Ms */
147 { macro_constant_delimited, MDOC_CALLABLE | MDOC_PARSED }, /* No */
148 { macro_constant_delimited, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */
149 { macro_constant_delimited, MDOC_PARSED }, /* Nx */
150 { macro_constant_delimited, MDOC_PARSED }, /* Ox */
151 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
152 { macro_constant_delimited, MDOC_PARSED }, /* Pf */
153 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
154 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
155 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
156 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
157 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */
158 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
159 { macro_scoped_close, MDOC_EXPLICIT }, /* Re */
160 { macro_scoped, MDOC_EXPLICIT }, /* Rs */
161 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
162 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */
163 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */
164 { macro_constant, 0 }, /* Sm */
165 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */
166 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */
167 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
168 { macro_constant_delimited, MDOC_PARSED }, /* Ux */
169 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
170 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
171 /* XXX - .Fo supposed to be (but isn't) callable. */
172 { macro_scoped, MDOC_EXPLICIT }, /* Fo */
173 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */
174 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */
175 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
176 { macro_scoped, MDOC_EXPLICIT }, /* Bk */
177 { macro_scoped_close, MDOC_EXPLICIT }, /* Ek */
178 { macro_constant, 0 }, /* Bt */
179 { macro_constant, 0 }, /* Hf */
180 { macro_obsolete, 0 }, /* Fr */
181 { macro_constant, 0 }, /* Ud */
182 };
183
184 const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
185
186
187 static int
188 perr(struct mdoc *mdoc, int line, int pos, int type)
189 {
190 int c;
191
192 switch (type) {
193 case (ENOCTX):
194 c = mdoc_perr(mdoc, line, pos,
195 "closing macro has prior context");
196 break;
197 case (ENOPARMS):
198 c = mdoc_perr(mdoc, line, pos,
199 "macro doesn't expect parameters");
200 break;
201 case (EARGVLIM):
202 c = mdoc_perr(mdoc, line, pos,
203 "argument hard-limit %d reached",
204 MDOC_LINEARG_MAX);
205 break;
206 default:
207 abort();
208 /* NOTREACHED */
209 }
210 return(c);
211 }
212
213 static int
214 pwarn(struct mdoc *mdoc, int line, int pos, int type)
215 {
216 int c;
217
218 switch (type) {
219 case (WMACPARM):
220 c = mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX,
221 "macro-like parameter");
222 break;
223 case (WOBS):
224 c = mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX,
225 "macro is marked obsolete");
226 break;
227 default:
228 abort();
229 /* NOTREACHED */
230 }
231 return(c);
232 }
233
234
235 static int
236 lookup(struct mdoc *mdoc, int line, int pos, int from, const char *p)
237 {
238 int res;
239
240 res = mdoc_find(mdoc, p);
241 if (MDOC_PARSED & mdoc_macros[from].flags)
242 return(res);
243 if (MDOC_MAX == res)
244 return(res);
245 if ( ! pwarn(mdoc, line, pos, WMACPARM))
246 return(-1);
247 return(MDOC_MAX);
248 }
249
250
251 static int
252 rewind_last(struct mdoc *mdoc, struct mdoc_node *to)
253 {
254
255 assert(to);
256 mdoc->next = MDOC_NEXT_SIBLING;
257
258 /* LINTED */
259 while (mdoc->last != to) {
260 if ( ! mdoc_valid_post(mdoc))
261 return(0);
262 if ( ! mdoc_action_post(mdoc))
263 return(0);
264 mdoc->last = mdoc->last->parent;
265 assert(mdoc->last);
266 }
267
268 if ( ! mdoc_valid_post(mdoc))
269 return(0);
270 return(mdoc_action_post(mdoc));
271 }
272
273
274 static int
275 rewind_alt(int tok)
276 {
277 switch (tok) {
278 case (MDOC_Ac):
279 return(MDOC_Ao);
280 case (MDOC_Bc):
281 return(MDOC_Bo);
282 case (MDOC_Dc):
283 return(MDOC_Do);
284 case (MDOC_Ec):
285 return(MDOC_Eo);
286 case (MDOC_Ed):
287 return(MDOC_Bd);
288 case (MDOC_Ef):
289 return(MDOC_Bf);
290 case (MDOC_Ek):
291 return(MDOC_Bk);
292 case (MDOC_El):
293 return(MDOC_Bl);
294 case (MDOC_Fc):
295 return(MDOC_Fo);
296 case (MDOC_Oc):
297 return(MDOC_Oo);
298 case (MDOC_Pc):
299 return(MDOC_Po);
300 case (MDOC_Qc):
301 return(MDOC_Qo);
302 case (MDOC_Re):
303 return(MDOC_Rs);
304 case (MDOC_Sc):
305 return(MDOC_So);
306 case (MDOC_Xc):
307 return(MDOC_Xo);
308 default:
309 break;
310 }
311 abort();
312 /* NOTREACHED */
313 }
314
315
316 static int
317 rewind_dohalt(int tok, enum mdoc_type type, const struct mdoc_node *p)
318 {
319
320 if (MDOC_ROOT == p->type)
321 return(REWIND_HALT);
322 if (MDOC_VALID & p->flags)
323 return(REWIND_NOHALT);
324
325 switch (tok) {
326 /* One-liner implicit-scope. */
327 case (MDOC_Aq):
328 /* FALLTHROUGH */
329 case (MDOC_Bq):
330 /* FALLTHROUGH */
331 case (MDOC_D1):
332 /* FALLTHROUGH */
333 case (MDOC_Dl):
334 /* FALLTHROUGH */
335 case (MDOC_Dq):
336 /* FALLTHROUGH */
337 case (MDOC_Op):
338 /* FALLTHROUGH */
339 case (MDOC_Pq):
340 /* FALLTHROUGH */
341 case (MDOC_Ql):
342 /* FALLTHROUGH */
343 case (MDOC_Qq):
344 /* FALLTHROUGH */
345 case (MDOC_Sq):
346 assert(MDOC_HEAD != type);
347 assert(MDOC_TAIL != type);
348 if (type == p->type && tok == p->tok)
349 return(REWIND_REWIND);
350 break;
351
352 /* Multi-line implicit-scope. */
353 case (MDOC_It):
354 assert(MDOC_TAIL != type);
355 if (type == p->type && tok == p->tok)
356 return(REWIND_REWIND);
357 if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
358 return(REWIND_HALT);
359 break;
360 case (MDOC_Sh):
361 if (type == p->type && tok == p->tok)
362 return(REWIND_REWIND);
363 break;
364 case (MDOC_Ss):
365 assert(MDOC_TAIL != type);
366 if (type == p->type && tok == p->tok)
367 return(REWIND_REWIND);
368 if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
369 return(REWIND_HALT);
370 break;
371
372 /* Multi-line explicit scope start. */
373 case (MDOC_Ao):
374 /* FALLTHROUGH */
375 case (MDOC_Bd):
376 /* FALLTHROUGH */
377 case (MDOC_Bf):
378 /* FALLTHROUGH */
379 case (MDOC_Bk):
380 /* FALLTHROUGH */
381 case (MDOC_Bl):
382 /* FALLTHROUGH */
383 case (MDOC_Bo):
384 /* FALLTHROUGH */
385 case (MDOC_Do):
386 /* FALLTHROUGH */
387 case (MDOC_Eo):
388 /* FALLTHROUGH */
389 case (MDOC_Fo):
390 /* FALLTHROUGH */
391 case (MDOC_Oo):
392 /* FALLTHROUGH */
393 case (MDOC_Po):
394 /* FALLTHROUGH */
395 case (MDOC_Qo):
396 /* FALLTHROUGH */
397 case (MDOC_Rs):
398 /* FALLTHROUGH */
399 case (MDOC_So):
400 /* FALLTHROUGH */
401 case (MDOC_Xo):
402 if (type == p->type && tok == p->tok)
403 return(REWIND_REWIND);
404 break;
405
406 /* Multi-line explicit scope close. */
407 case (MDOC_Ac):
408 /* FALLTHROUGH */
409 case (MDOC_Bc):
410 /* FALLTHROUGH */
411 case (MDOC_Dc):
412 /* FALLTHROUGH */
413 case (MDOC_Ec):
414 /* FALLTHROUGH */
415 case (MDOC_Ed):
416 /* FALLTHROUGH */
417 case (MDOC_Ek):
418 /* FALLTHROUGH */
419 case (MDOC_El):
420 /* FALLTHROUGH */
421 case (MDOC_Fc):
422 /* FALLTHROUGH */
423 case (MDOC_Ef):
424 /* FALLTHROUGH */
425 case (MDOC_Oc):
426 /* FALLTHROUGH */
427 case (MDOC_Pc):
428 /* FALLTHROUGH */
429 case (MDOC_Qc):
430 /* FALLTHROUGH */
431 case (MDOC_Re):
432 /* FALLTHROUGH */
433 case (MDOC_Sc):
434 /* FALLTHROUGH */
435 case (MDOC_Xc):
436 if (type == p->type && rewind_alt(tok) == p->tok)
437 return(REWIND_REWIND);
438 break;
439 default:
440 abort();
441 /* NOTREACHED */
442 }
443
444 return(REWIND_NOHALT);
445 }
446
447
448 static int
449 rewind_dobreak(int tok, const struct mdoc_node *p)
450 {
451
452 assert(MDOC_ROOT != p->type);
453 if (MDOC_ELEM == p->type)
454 return(1);
455 if (MDOC_TEXT == p->type)
456 return(1);
457 if (MDOC_VALID & p->flags)
458 return(1);
459
460 switch (tok) {
461 /* Implicit rules. */
462 case (MDOC_It):
463 return(MDOC_It == p->tok);
464 case (MDOC_Ss):
465 return(MDOC_Ss == p->tok);
466 case (MDOC_Sh):
467 if (MDOC_Ss == p->tok)
468 return(1);
469 return(MDOC_Sh == p->tok);
470
471 /* Extra scope rules. */
472 case (MDOC_El):
473 if (MDOC_It == p->tok)
474 return(1);
475 break;
476 default:
477 break;
478 }
479
480 if (MDOC_EXPLICIT & mdoc_macros[tok].flags)
481 return(p->tok == rewind_alt(tok));
482 else if (MDOC_BLOCK == p->type)
483 return(1);
484
485 return(tok == p->tok);
486 }
487
488
489 static int
490 rewind_elem(struct mdoc *mdoc, int tok)
491 {
492 struct mdoc_node *n;
493
494 n = mdoc->last;
495 if (MDOC_ELEM != n->type)
496 n = n->parent;
497 assert(MDOC_ELEM == n->type);
498 assert(tok == n->tok);
499
500 return(rewind_last(mdoc, n));
501 }
502
503
504 static int
505 rewind_subblock(enum mdoc_type type, struct mdoc *mdoc,
506 int tok, int line, int ppos)
507 {
508 struct mdoc_node *n;
509 int c;
510
511 /* LINTED */
512 for (n = mdoc->last; n; n = n->parent) {
513 c = rewind_dohalt(tok, type, n);
514 if (REWIND_HALT == c)
515 return(1);
516 if (REWIND_REWIND == c)
517 break;
518 else if (rewind_dobreak(tok, n))
519 continue;
520 return(mdoc_perr(mdoc, line, ppos, "scope breaks prior %s", mdoc_node2a(n)));
521 }
522
523 assert(n);
524 return(rewind_last(mdoc, n));
525 }
526
527
528 static int
529 rewind_expblock(struct mdoc *mdoc, int tok, int line, int ppos)
530 {
531 struct mdoc_node *n;
532 int c;
533
534 /* LINTED */
535 for (n = mdoc->last; n; n = n->parent) {
536 c = rewind_dohalt(tok, MDOC_BLOCK, n);
537 if (REWIND_HALT == c)
538 return(perr(mdoc, line, ppos, ENOCTX));
539 if (REWIND_REWIND == c)
540 break;
541 else if (rewind_dobreak(tok, n))
542 continue;
543 return(mdoc_perr(mdoc, line, ppos,
544 "scope breaks prior %s",
545 mdoc_node2a(n)));
546 }
547
548 assert(n);
549 return(rewind_last(mdoc, n));
550 }
551
552
553 static int
554 rewind_impblock(struct mdoc *mdoc, int tok, int line, int ppos)
555 {
556 struct mdoc_node *n;
557 int c;
558
559 /* LINTED */
560 for (n = mdoc->last; n; n = n->parent) {
561 c = rewind_dohalt(tok, MDOC_BLOCK, n);
562 if (REWIND_HALT == c)
563 return(1);
564 else if (REWIND_REWIND == c)
565 break;
566 else if (rewind_dobreak(tok, n))
567 continue;
568 return(mdoc_perr(mdoc, line, ppos,
569 "scope breaks prior %s",
570 mdoc_node2a(n)));
571 }
572
573 assert(n);
574 return(rewind_last(mdoc, n));
575 }
576
577
578 static int
579 append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
580 {
581 int c, lastarg;
582 char *p;
583
584 if (0 == buf[*pos])
585 return(1);
586
587 for (;;) {
588 lastarg = *pos;
589 c = mdoc_args(mdoc, line, pos, buf, 0, &p);
590 assert(ARGS_PHRASE != c);
591
592 if (ARGS_ERROR == c)
593 return(0);
594 else if (ARGS_EOLN == c)
595 break;
596 assert(mdoc_isdelim(p));
597 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
598 return(0);
599 mdoc->next = MDOC_NEXT_SIBLING;
600 }
601
602 return(1);
603 }
604
605
606 /*
607 * Close out an explicit scope. This optionally parses a TAIL type with
608 * a set number of TEXT children.
609 */
610 static int
611 macro_scoped_close(MACRO_PROT_ARGS)
612 {
613 int tt, j, c, lastarg, maxargs, flushed;
614 char *p;
615
616 switch (tok) {
617 case (MDOC_Ec):
618 maxargs = 1;
619 break;
620 default:
621 maxargs = 0;
622 break;
623 }
624
625 tt = rewind_alt(tok);
626
627 mdoc_msg(mdoc, "parse: %s closing %s",
628 mdoc_macronames[tok], mdoc_macronames[tt]);
629
630 if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
631 if (0 == buf[*pos]) {
632 if ( ! rewind_subblock(MDOC_BODY, mdoc,
633 tok, line, ppos))
634 return(0);
635 return(rewind_expblock(mdoc, tok, line, ppos));
636 }
637 return(perr(mdoc, line, ppos, ENOPARMS));
638 }
639
640 if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
641 return(0);
642
643 lastarg = ppos;
644 flushed = 0;
645
646 if (maxargs > 0) {
647 if ( ! mdoc_tail_alloc(mdoc, line, ppos, tt))
648 return(0);
649 mdoc->next = MDOC_NEXT_CHILD;
650 }
651
652 for (j = 0; /* No sentinel. */; j++) {
653 lastarg = *pos;
654
655 if (j == maxargs && ! flushed) {
656 if ( ! rewind_expblock(mdoc, tok, line, ppos))
657 return(0);
658 flushed = 1;
659 }
660
661 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
662 assert(ARGS_PHRASE != c);
663
664 if (ARGS_ERROR == c)
665 return(0);
666 if (ARGS_PUNCT == c)
667 break;
668 if (ARGS_EOLN == c)
669 break;
670
671 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
672 return(0);
673 else if (MDOC_MAX != c) {
674 if ( ! flushed) {
675 if ( ! rewind_expblock(mdoc, tok,
676 line, ppos))
677 return(0);
678 flushed = 1;
679 }
680 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
681 return(0);
682 break;
683 }
684
685 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
686 return(0);
687 mdoc->next = MDOC_NEXT_SIBLING;
688 }
689
690 if ( ! flushed && ! rewind_expblock(mdoc, tok, line, ppos))
691 return(0);
692
693 if (ppos > 1)
694 return(1);
695 return(append_delims(mdoc, line, pos, buf));
696 }
697
698
699 /*
700 * A general text macro. This is a complex case because of punctuation.
701 * If a text macro is followed by words, then punctuation, the macro is
702 * "stopped" and "reopened" following the punctuation. Thus, the
703 * following arises:
704 *
705 * .Fl a ; b
706 *
707 * ELEMENT (.Fl)
708 * TEXT (`a')
709 * TEXT (`;')
710 * ELEMENT (.Fl)
711 * TEXT (`b')
712 *
713 * This must handle the following situations:
714 *
715 * .Fl Ar b ; ;
716 *
717 * ELEMENT (.Fl)
718 * ELEMENT (.Ar)
719 * TEXT (`b')
720 * TEXT (`;')
721 * TEXT (`;')
722 */
723 static int
724 macro_text(MACRO_PROT_ARGS)
725 {
726 int la, lastpunct, c, w, argc;
727 struct mdoc_arg argv[MDOC_LINEARG_MAX];
728 char *p;
729
730 la = ppos;
731 lastpunct = 0;
732
733 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
734 la = *pos;
735 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
736 if (ARGV_EOLN == c)
737 break;
738 if (ARGV_WORD == c) {
739 *pos = la;
740 break;
741 } else if (ARGV_ARG == c)
742 continue;
743
744 mdoc_argv_free(argc, argv);
745 return(0);
746 }
747
748 if (MDOC_LINEARG_MAX == argc) {
749 mdoc_argv_free(argc - 1, argv);
750 return(perr(mdoc, line, ppos, EARGVLIM));
751 }
752
753 c = mdoc_elem_alloc(mdoc, line, ppos,
754 tok, (size_t)argc, argv);
755
756 if (0 == c) {
757 mdoc_argv_free(argc, argv);
758 return(0);
759 }
760
761 mdoc->next = MDOC_NEXT_CHILD;
762
763 lastpunct = 0;
764 for (;;) {
765 la = *pos;
766 w = mdoc_args(mdoc, line, pos, buf, tok, &p);
767 assert(ARGS_PHRASE != c);
768
769 if (ARGS_ERROR == w) {
770 mdoc_argv_free(argc, argv);
771 return(0);
772 }
773
774 if (ARGS_EOLN == w)
775 break;
776 if (ARGS_PUNCT == w)
777 break;
778
779 c = ARGS_QWORD == w ? MDOC_MAX :
780 lookup(mdoc, line, la, tok, p);
781
782 if (MDOC_MAX != c && -1 != c) {
783 if (0 == lastpunct && ! rewind_elem(mdoc, tok)) {
784 mdoc_argv_free(argc, argv);
785 return(0);
786 }
787 mdoc_argv_free(argc, argv);
788 c = mdoc_macro(mdoc, c, line, la, pos, buf);
789 if (0 == c)
790 return(0);
791 if (ppos > 1)
792 return(1);
793 return(append_delims(mdoc, line, pos, buf));
794 } else if (-1 == c) {
795 mdoc_argv_free(argc, argv);
796 return(0);
797 }
798
799 if (ARGS_QWORD != w && mdoc_isdelim(p)) {
800 if (0 == lastpunct && ! rewind_elem(mdoc, tok)) {
801 mdoc_argv_free(argc, argv);
802 return(0);
803 }
804 lastpunct = 1;
805 } else if (lastpunct) {
806 c = mdoc_elem_alloc(mdoc, line, ppos,
807 tok, (size_t)argc, argv);
808 if (0 == c) {
809 mdoc_argv_free(argc, argv);
810 return(0);
811 }
812 mdoc->next = MDOC_NEXT_CHILD;
813 lastpunct = 0;
814 }
815
816 if ( ! mdoc_word_alloc(mdoc, line, la, p))
817 return(0);
818 mdoc->next = MDOC_NEXT_SIBLING;
819 }
820
821 mdoc_argv_free(argc, argv);
822
823 if (0 == lastpunct && ! rewind_elem(mdoc, tok))
824 return(0);
825 if (ppos > 1)
826 return(1);
827 return(append_delims(mdoc, line, pos, buf));
828 }
829
830
831 /*
832 * Handle explicit-scope (having a different closure token) and implicit
833 * scope (closing out prior scopes when re-invoked) macros. These
834 * constitute the BLOCK type and usually span multiple lines. These
835 * always have HEAD and sometimes have BODY types. In the multi-line
836 * case:
837 *
838 * .Bd -ragged
839 * Text.
840 * .Fl macro
841 * Another.
842 * .Ed
843 *
844 * BLOCK (.Bd)
845 * HEAD
846 * BODY
847 * TEXT (`Text.')
848 * ELEMENT (.Fl)
849 * TEXT (`macro')
850 * TEXT (`Another.')
851 *
852 * Note that the `.It' macro, possibly the most difficult (as it has
853 * embedded scope, etc.) is handled by this routine.
854 */
855 static int
856 macro_scoped(MACRO_PROT_ARGS)
857 {
858 int c, lastarg, argc;
859 struct mdoc_arg argv[MDOC_LINEARG_MAX];
860 char *p;
861
862 assert ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags));
863
864 /* First rewind extant implicit scope. */
865
866 if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
867 if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
868 return(0);
869 if ( ! rewind_impblock(mdoc, tok, line, ppos))
870 return(0);
871 }
872
873 /* Parse arguments. */
874
875 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
876 lastarg = *pos;
877 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
878 if (ARGV_EOLN == c)
879 break;
880 if (ARGV_WORD == c) {
881 *pos = lastarg;
882 break;
883 } else if (ARGV_ARG == c)
884 continue;
885 mdoc_argv_free(argc, argv);
886 return(0);
887 }
888
889 if (MDOC_LINEARG_MAX == argc) {
890 mdoc_argv_free(argc - 1, argv);
891 return(perr(mdoc, line, ppos, EARGVLIM));
892 }
893
894 c = mdoc_block_alloc(mdoc, line, ppos,
895 tok, (size_t)argc, argv);
896 mdoc_argv_free(argc, argv);
897
898 if (0 == c)
899 return(0);
900
901 mdoc->next = MDOC_NEXT_CHILD;
902
903 if (0 == buf[*pos]) {
904 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
905 return(0);
906 if ( ! rewind_subblock(MDOC_HEAD, mdoc,
907 tok, line, ppos))
908 return(0);
909 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
910 return(0);
911 mdoc->next = MDOC_NEXT_CHILD;
912 return(1);
913 }
914
915 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
916 return(0);
917 mdoc->next = MDOC_NEXT_CHILD;
918
919 for (;;) {
920 lastarg = *pos;
921 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
922
923 if (ARGS_ERROR == c)
924 return(0);
925 if (ARGS_EOLN == c)
926 break;
927 if (ARGS_PHRASE == c) {
928 /*
929 if ( ! mdoc_phrase(mdoc, line, lastarg, buf))
930 return(0);
931 */
932 continue;
933 }
934
935 /* FIXME: if .It -column, the lookup must be for a
936 * sub-line component. BLAH. */
937
938 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
939 return(0);
940
941 if (MDOC_MAX == c) {
942 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
943 return(0);
944 mdoc->next = MDOC_NEXT_SIBLING;
945 continue;
946 }
947
948 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
949 return(0);
950 break;
951 }
952
953 if (1 == ppos && ! append_delims(mdoc, line, pos, buf))
954 return(0);
955 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
956 return(0);
957
958 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
959 return(0);
960 mdoc->next = MDOC_NEXT_CHILD;
961
962 return(1);
963 }
964
965
966 /*
967 * This handles a case of implicitly-scoped macro (BLOCK) limited to a
968 * single line. Instead of being closed out by a subsequent call to
969 * another macro, the scope is closed at the end of line. These don't
970 * have BODY or TAIL types. Notice that the punctuation falls outside
971 * of the HEAD type.
972 *
973 * .Qq a Fl b Ar d ; ;
974 *
975 * BLOCK (Qq)
976 * HEAD
977 * TEXT (`a')
978 * ELEMENT (.Fl)
979 * TEXT (`b')
980 * ELEMENT (.Ar)
981 * TEXT (`d')
982 * TEXT (`;')
983 * TEXT (`;')
984 */
985 static int
986 macro_scoped_line(MACRO_PROT_ARGS)
987 {
988 int lastarg, c;
989 char *p;
990
991 if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, 0, NULL))
992 return(0);
993 mdoc->next = MDOC_NEXT_CHILD;
994
995 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
996 return(0);
997 mdoc->next = MDOC_NEXT_SIBLING;
998 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
999 return(0);
1000 mdoc->next = MDOC_NEXT_CHILD;
1001
1002 /* XXX - no known argument macros. */
1003
1004 lastarg = ppos;
1005 for (;;) {
1006 lastarg = *pos;
1007 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
1008 assert(ARGS_PHRASE != c);
1009
1010 if (ARGS_ERROR == c)
1011 return(0);
1012 if (ARGS_PUNCT == c)
1013 break;
1014 if (ARGS_EOLN == c)
1015 break;
1016
1017 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
1018 return(0);
1019 else if (MDOC_MAX == c) {
1020 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
1021 return(0);
1022 mdoc->next = MDOC_NEXT_SIBLING;
1023 continue;
1024 }
1025
1026 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
1027 return(0);
1028 break;
1029 }
1030
1031 if (1 == ppos) {
1032 if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
1033 return(0);
1034 if ( ! append_delims(mdoc, line, pos, buf))
1035 return(0);
1036 } else if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
1037 return(0);
1038 return(rewind_impblock(mdoc, tok, line, ppos));
1039 }
1040
1041
1042 /*
1043 * A constant-scoped macro is like a simple-scoped macro (mdoc_scoped)
1044 * except that it doesn't handle implicit scopes and explicit ones have
1045 * a fixed number of TEXT children to the BODY.
1046 *
1047 * .Fl a So b Sc ;
1048 *
1049 * ELEMENT (.Fl)
1050 * TEXT (`a')
1051 * BLOCK (.So)
1052 * HEAD
1053 * BODY
1054 * TEXT (`b')
1055 * TEXT (';')
1056 */
1057 static int
1058 macro_constant_scoped(MACRO_PROT_ARGS)
1059 {
1060 int lastarg, flushed, j, c, maxargs;
1061 char *p;
1062
1063 lastarg = ppos;
1064 flushed = 0;
1065
1066 switch (tok) {
1067 case (MDOC_Eo):
1068 maxargs = 1;
1069 break;
1070 default:
1071 maxargs = 0;
1072 break;
1073 }
1074
1075 if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, 0, NULL))
1076 return(0);
1077 mdoc->next = MDOC_NEXT_CHILD;
1078
1079 if (0 == maxargs) {
1080 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
1081 return(0);
1082 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
1083 return(0);
1084 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1085 return(0);
1086 flushed = 1;
1087 } else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
1088 return(0);
1089
1090 mdoc->next = MDOC_NEXT_CHILD;
1091
1092 for (j = 0; /* No sentinel. */; j++) {
1093 lastarg = *pos;
1094
1095 if (j == maxargs && ! flushed) {
1096 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
1097 return(0);
1098 flushed = 1;
1099 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1100 return(0);
1101 mdoc->next = MDOC_NEXT_CHILD;
1102 }
1103
1104 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
1105 assert(ARGS_PHRASE != c);
1106
1107 if (ARGS_ERROR == c)
1108 return(0);
1109 if (ARGS_PUNCT == c)
1110 break;
1111 if (ARGS_EOLN == c)
1112 break;
1113
1114 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
1115 return(0);
1116 else if (MDOC_MAX != c) {
1117 if ( ! flushed) {
1118 if ( ! rewind_subblock(MDOC_HEAD, mdoc,
1119 tok, line, ppos))
1120 return(0);
1121 flushed = 1;
1122 if ( ! mdoc_body_alloc(mdoc, line,
1123 ppos, tok))
1124 return(0);
1125 mdoc->next = MDOC_NEXT_CHILD;
1126 }
1127 if ( ! mdoc_macro(mdoc, c, line, lastarg,
1128 pos, buf))
1129 return(0);
1130 break;
1131 }
1132
1133 if ( ! flushed && mdoc_isdelim(p)) {
1134 if ( ! rewind_subblock(MDOC_HEAD, mdoc,
1135 tok, line, ppos))
1136 return(0);
1137 flushed = 1;
1138 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1139 return(0);
1140 mdoc->next = MDOC_NEXT_CHILD;
1141 }
1142
1143 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
1144 return(0);
1145 mdoc->next = MDOC_NEXT_SIBLING;
1146 }
1147
1148 if ( ! flushed) {
1149 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
1150 return(0);
1151 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1152 return(0);
1153 mdoc->next = MDOC_NEXT_CHILD;
1154 }
1155
1156 if (ppos > 1)
1157 return(1);
1158 return(append_delims(mdoc, line, pos, buf));
1159 }
1160
1161
1162 /*
1163 * A delimited constant is very similar to the macros parsed by
1164 * macro_text except that, in the event of punctuation, the macro isn't
1165 * "re-opened" as it is in macro_text. Also, these macros have a fixed
1166 * number of parameters.
1167 *
1168 * .Fl a No b
1169 *
1170 * ELEMENT (.Fl)
1171 * TEXT (`a')
1172 * ELEMENT (.No)
1173 * TEXT (`b')
1174 */
1175 static int
1176 macro_constant_delimited(MACRO_PROT_ARGS)
1177 {
1178 int lastarg, flushed, j, c, maxargs, argc,
1179 igndelim;
1180 struct mdoc_arg argv[MDOC_LINEARG_MAX];
1181 char *p;
1182
1183 lastarg = ppos;
1184 flushed = 0;
1185
1186 switch (tok) {
1187 case (MDOC_No):
1188 /* FALLTHROUGH */
1189 case (MDOC_Ns):
1190 /* FALLTHROUGH */
1191 case (MDOC_Ux):
1192 /* FALLTHROUGH */
1193 case (MDOC_St):
1194 maxargs = 0;
1195 break;
1196 default:
1197 maxargs = 1;
1198 break;
1199 }
1200
1201 switch (tok) {
1202 case (MDOC_Pf):
1203 igndelim = 1;
1204 break;
1205 default:
1206 igndelim = 0;
1207 break;
1208 }
1209
1210 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
1211 lastarg = *pos;
1212 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
1213 if (ARGV_EOLN == c)
1214 break;
1215 if (ARGV_WORD == c) {
1216 *pos = lastarg;
1217 break;
1218 } else if (ARGV_ARG == c)
1219 continue;
1220 mdoc_argv_free(argc, argv);
1221 return(0);
1222 }
1223
1224 if (MDOC_LINEARG_MAX == argc) {
1225 mdoc_argv_free(argc - 1, argv);
1226 return(perr(mdoc, line, ppos, EARGVLIM));
1227 }
1228
1229 c = mdoc_elem_alloc(mdoc, line, ppos,
1230 tok, (size_t)argc, argv);
1231
1232 mdoc_argv_free(argc, argv);
1233
1234 if (0 == c)
1235 return(0);
1236
1237 mdoc->next = MDOC_NEXT_CHILD;
1238
1239 for (j = 0; /* No sentinel. */; j++) {
1240 lastarg = *pos;
1241
1242 if (j == maxargs && ! flushed) {
1243 if ( ! rewind_elem(mdoc, tok))
1244 return(0);
1245 flushed = 1;
1246 }
1247
1248 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
1249 assert(ARGS_PHRASE != c);
1250
1251 if (ARGS_ERROR == c)
1252 return(0);
1253 if (ARGS_PUNCT == c)
1254 break;
1255 if (ARGS_EOLN == c)
1256 break;
1257
1258 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
1259 return(0);
1260 else if (MDOC_MAX != c) {
1261 if ( ! flushed && ! rewind_elem(mdoc, tok))
1262 return(0);
1263 flushed = 1;
1264 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
1265 return(0);
1266 break;
1267 }
1268
1269 if ( ! flushed && mdoc_isdelim(p) && ! igndelim) {
1270 if ( ! rewind_elem(mdoc, tok))
1271 return(0);
1272 flushed = 1;
1273 }
1274
1275 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
1276 return(0);
1277 mdoc->next = MDOC_NEXT_SIBLING;
1278 }
1279
1280 if ( ! flushed && ! rewind_elem(mdoc, tok))
1281 return(0);
1282
1283 if (ppos > 1)
1284 return(1);
1285 return(append_delims(mdoc, line, pos, buf));
1286 }
1287
1288
1289 /*
1290 * A constant macro is the simplest classification. It spans an entire
1291 * line.
1292 */
1293 static int
1294 macro_constant(MACRO_PROT_ARGS)
1295 {
1296 int c, w, la, argc;
1297 struct mdoc_arg argv[MDOC_LINEARG_MAX];
1298 char *p;
1299
1300 assert( ! (MDOC_CALLABLE & mdoc_macros[tok].flags));
1301
1302 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
1303 la = *pos;
1304 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
1305 if (ARGV_EOLN == c)
1306 break;
1307 if (ARGV_WORD == c) {
1308 *pos = la;
1309 break;
1310 } else if (ARGV_ARG == c)
1311 continue;
1312
1313 mdoc_argv_free(argc, argv);
1314 return(0);
1315 }
1316
1317 if (MDOC_LINEARG_MAX == argc) {
1318 mdoc_argv_free(argc - 1, argv);
1319 return(perr(mdoc, line, ppos, EARGVLIM));
1320 }
1321
1322 c = mdoc_elem_alloc(mdoc, line, ppos,
1323 tok, (size_t)argc, argv);
1324
1325 mdoc_argv_free(argc, argv);
1326
1327 if (0 == c)
1328 return(0);
1329
1330 mdoc->next = MDOC_NEXT_CHILD;
1331
1332 for (;;) {
1333 la = *pos;
1334 w = mdoc_args(mdoc, line, pos, buf, tok, &p);
1335 assert(ARGS_PHRASE != c);
1336
1337 if (ARGS_ERROR == w)
1338 return(0);
1339 if (ARGS_EOLN == w)
1340 break;
1341
1342 c = ARGS_QWORD == w ? MDOC_MAX :
1343 lookup(mdoc, line, la, tok, p);
1344
1345 if (MDOC_MAX != c && -1 != c) {
1346 if ( ! rewind_elem(mdoc, tok))
1347 return(0);
1348 return(mdoc_macro(mdoc, c, line, la, pos, buf));
1349 } else if (-1 == c)
1350 return(0);
1351
1352 if ( ! mdoc_word_alloc(mdoc, line, la, p))
1353 return(0);
1354 mdoc->next = MDOC_NEXT_SIBLING;
1355 }
1356
1357 return(rewind_elem(mdoc, tok));
1358 }
1359
1360
1361 /* ARGSUSED */
1362 static int
1363 macro_obsolete(MACRO_PROT_ARGS)
1364 {
1365
1366 return(pwarn(mdoc, line, ppos, WOBS));
1367 }
1368
1369
1370 /*
1371 * This is called at the end of parsing. It must traverse up the tree,
1372 * closing out open [implicit] scopes. Obviously, open explicit scopes
1373 * are errors.
1374 */
1375 int
1376 macro_end(struct mdoc *mdoc)
1377 {
1378 struct mdoc_node *n;
1379
1380 assert(mdoc->first);
1381 assert(mdoc->last);
1382
1383 /* Scan for open explicit scopes. */
1384
1385 n = MDOC_VALID & mdoc->last->flags ?
1386 mdoc->last->parent : mdoc->last;
1387
1388 for ( ; n; n = n->parent) {
1389 if (MDOC_BLOCK != n->type)
1390 continue;
1391 if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags))
1392 continue;
1393 return(mdoc_nerr(mdoc, n,
1394 "macro scope still open on exit"));
1395 }
1396
1397 return(rewind_last(mdoc, mdoc->first));
1398 }