]> git.cameronkatri.com Git - mandoc.git/blob - macro.c
More character-encoding.
[mandoc.git] / macro.c
1 /* $Id: macro.c,v 1.53 2009/02/26 14:56:27 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, tok, argc, argv);
754
755 if (0 == c) {
756 mdoc_argv_free(argc, argv);
757 return(0);
758 }
759
760 mdoc->next = MDOC_NEXT_CHILD;
761
762 lastpunct = 0;
763 for (;;) {
764 la = *pos;
765 w = mdoc_args(mdoc, line, pos, buf, tok, &p);
766 assert(ARGS_PHRASE != c);
767
768 if (ARGS_ERROR == w) {
769 mdoc_argv_free(argc, argv);
770 return(0);
771 }
772
773 if (ARGS_EOLN == w)
774 break;
775 if (ARGS_PUNCT == w)
776 break;
777
778 c = ARGS_QWORD == w ? MDOC_MAX :
779 lookup(mdoc, line, la, tok, p);
780
781 if (MDOC_MAX != c && -1 != c) {
782 if (0 == lastpunct && ! rewind_elem(mdoc, tok)) {
783 mdoc_argv_free(argc, argv);
784 return(0);
785 }
786 mdoc_argv_free(argc, argv);
787 c = mdoc_macro(mdoc, c, line, la, pos, buf);
788 if (0 == c)
789 return(0);
790 if (ppos > 1)
791 return(1);
792 return(append_delims(mdoc, line, pos, buf));
793 } else if (-1 == c) {
794 mdoc_argv_free(argc, argv);
795 return(0);
796 }
797
798 if (ARGS_QWORD != w && mdoc_isdelim(p)) {
799 if (0 == lastpunct && ! rewind_elem(mdoc, tok)) {
800 mdoc_argv_free(argc, argv);
801 return(0);
802 }
803 lastpunct = 1;
804 } else if (lastpunct) {
805 c = mdoc_elem_alloc(mdoc, line,
806 ppos, tok, argc, argv);
807 if (0 == c) {
808 mdoc_argv_free(argc, argv);
809 return(0);
810 }
811 mdoc->next = MDOC_NEXT_CHILD;
812 lastpunct = 0;
813 }
814
815 if ( ! mdoc_word_alloc(mdoc, line, la, p))
816 return(0);
817 mdoc->next = MDOC_NEXT_SIBLING;
818 }
819
820 mdoc_argv_free(argc, argv);
821
822 if (0 == lastpunct && ! rewind_elem(mdoc, tok))
823 return(0);
824 if (ppos > 1)
825 return(1);
826 return(append_delims(mdoc, line, pos, buf));
827 }
828
829
830 /*
831 * Handle explicit-scope (having a different closure token) and implicit
832 * scope (closing out prior scopes when re-invoked) macros. These
833 * constitute the BLOCK type and usually span multiple lines. These
834 * always have HEAD and sometimes have BODY types. In the multi-line
835 * case:
836 *
837 * .Bd -ragged
838 * Text.
839 * .Fl macro
840 * Another.
841 * .Ed
842 *
843 * BLOCK (.Bd)
844 * HEAD
845 * BODY
846 * TEXT (`Text.')
847 * ELEMENT (.Fl)
848 * TEXT (`macro')
849 * TEXT (`Another.')
850 *
851 * Note that the `.It' macro, possibly the most difficult (as it has
852 * embedded scope, etc.) is handled by this routine.
853 */
854 static int
855 macro_scoped(MACRO_PROT_ARGS)
856 {
857 int c, lastarg, argc;
858 struct mdoc_arg argv[MDOC_LINEARG_MAX];
859 char *p;
860
861 assert ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags));
862
863 /* First rewind extant implicit scope. */
864
865 if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
866 if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
867 return(0);
868 if ( ! rewind_impblock(mdoc, tok, line, ppos))
869 return(0);
870 }
871
872 /* Parse arguments. */
873
874 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
875 lastarg = *pos;
876 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
877 if (ARGV_EOLN == c)
878 break;
879 if (ARGV_WORD == c) {
880 *pos = lastarg;
881 break;
882 } else if (ARGV_ARG == c)
883 continue;
884 mdoc_argv_free(argc, argv);
885 return(0);
886 }
887
888 if (MDOC_LINEARG_MAX == argc) {
889 mdoc_argv_free(argc - 1, argv);
890 return(perr(mdoc, line, ppos, EARGVLIM));
891 }
892
893 c = mdoc_block_alloc(mdoc, line, ppos,
894 tok, (size_t)argc, argv);
895 mdoc_argv_free(argc, argv);
896
897 if (0 == c)
898 return(0);
899
900 mdoc->next = MDOC_NEXT_CHILD;
901
902 if (0 == buf[*pos]) {
903 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
904 return(0);
905 if ( ! rewind_subblock(MDOC_HEAD, mdoc,
906 tok, line, ppos))
907 return(0);
908 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
909 return(0);
910 mdoc->next = MDOC_NEXT_CHILD;
911 return(1);
912 }
913
914 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
915 return(0);
916 mdoc->next = MDOC_NEXT_CHILD;
917
918 for (;;) {
919 lastarg = *pos;
920 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
921
922 if (ARGS_ERROR == c)
923 return(0);
924 if (ARGS_EOLN == c)
925 break;
926 if (ARGS_PHRASE == c) {
927 /*
928 if ( ! mdoc_phrase(mdoc, line, lastarg, buf))
929 return(0);
930 */
931 continue;
932 }
933
934 /* FIXME: if .It -column, the lookup must be for a
935 * sub-line component. BLAH. */
936
937 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
938 return(0);
939
940 if (MDOC_MAX == c) {
941 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
942 return(0);
943 mdoc->next = MDOC_NEXT_SIBLING;
944 continue;
945 }
946
947 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
948 return(0);
949 break;
950 }
951
952 if (1 == ppos && ! append_delims(mdoc, line, pos, buf))
953 return(0);
954 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
955 return(0);
956
957 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
958 return(0);
959 mdoc->next = MDOC_NEXT_CHILD;
960
961 return(1);
962 }
963
964
965 /*
966 * This handles a case of implicitly-scoped macro (BLOCK) limited to a
967 * single line. Instead of being closed out by a subsequent call to
968 * another macro, the scope is closed at the end of line. These don't
969 * have BODY or TAIL types. Notice that the punctuation falls outside
970 * of the HEAD type.
971 *
972 * .Qq a Fl b Ar d ; ;
973 *
974 * BLOCK (Qq)
975 * HEAD
976 * TEXT (`a')
977 * ELEMENT (.Fl)
978 * TEXT (`b')
979 * ELEMENT (.Ar)
980 * TEXT (`d')
981 * TEXT (`;')
982 * TEXT (`;')
983 */
984 static int
985 macro_scoped_line(MACRO_PROT_ARGS)
986 {
987 int lastarg, c;
988 char *p;
989
990 if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, 0, NULL))
991 return(0);
992 mdoc->next = MDOC_NEXT_CHILD;
993
994 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
995 return(0);
996 mdoc->next = MDOC_NEXT_SIBLING;
997 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
998 return(0);
999 mdoc->next = MDOC_NEXT_CHILD;
1000
1001 /* XXX - no known argument macros. */
1002
1003 lastarg = ppos;
1004 for (;;) {
1005 lastarg = *pos;
1006 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
1007 assert(ARGS_PHRASE != c);
1008
1009 if (ARGS_ERROR == c)
1010 return(0);
1011 if (ARGS_PUNCT == c)
1012 break;
1013 if (ARGS_EOLN == c)
1014 break;
1015
1016 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
1017 return(0);
1018 else if (MDOC_MAX == c) {
1019 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
1020 return(0);
1021 mdoc->next = MDOC_NEXT_SIBLING;
1022 continue;
1023 }
1024
1025 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
1026 return(0);
1027 break;
1028 }
1029
1030 if (1 == ppos) {
1031 if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
1032 return(0);
1033 if ( ! append_delims(mdoc, line, pos, buf))
1034 return(0);
1035 } else if ( ! rewind_subblock(MDOC_BODY, mdoc, tok, line, ppos))
1036 return(0);
1037 return(rewind_impblock(mdoc, tok, line, ppos));
1038 }
1039
1040
1041 /*
1042 * A constant-scoped macro is like a simple-scoped macro (mdoc_scoped)
1043 * except that it doesn't handle implicit scopes and explicit ones have
1044 * a fixed number of TEXT children to the BODY.
1045 *
1046 * .Fl a So b Sc ;
1047 *
1048 * ELEMENT (.Fl)
1049 * TEXT (`a')
1050 * BLOCK (.So)
1051 * HEAD
1052 * BODY
1053 * TEXT (`b')
1054 * TEXT (';')
1055 */
1056 static int
1057 macro_constant_scoped(MACRO_PROT_ARGS)
1058 {
1059 int lastarg, flushed, j, c, maxargs;
1060 char *p;
1061
1062 lastarg = ppos;
1063 flushed = 0;
1064
1065 switch (tok) {
1066 case (MDOC_Eo):
1067 maxargs = 1;
1068 break;
1069 default:
1070 maxargs = 0;
1071 break;
1072 }
1073
1074 if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, 0, NULL))
1075 return(0);
1076 mdoc->next = MDOC_NEXT_CHILD;
1077
1078 if (0 == maxargs) {
1079 if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
1080 return(0);
1081 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
1082 return(0);
1083 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1084 return(0);
1085 flushed = 1;
1086 } else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
1087 return(0);
1088
1089 mdoc->next = MDOC_NEXT_CHILD;
1090
1091 for (j = 0; /* No sentinel. */; j++) {
1092 lastarg = *pos;
1093
1094 if (j == maxargs && ! flushed) {
1095 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
1096 return(0);
1097 flushed = 1;
1098 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1099 return(0);
1100 mdoc->next = MDOC_NEXT_CHILD;
1101 }
1102
1103 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
1104 assert(ARGS_PHRASE != c);
1105
1106 if (ARGS_ERROR == c)
1107 return(0);
1108 if (ARGS_PUNCT == c)
1109 break;
1110 if (ARGS_EOLN == c)
1111 break;
1112
1113 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
1114 return(0);
1115 else if (MDOC_MAX != c) {
1116 if ( ! flushed) {
1117 if ( ! rewind_subblock(MDOC_HEAD, mdoc,
1118 tok, line, ppos))
1119 return(0);
1120 flushed = 1;
1121 if ( ! mdoc_body_alloc(mdoc, line,
1122 ppos, tok))
1123 return(0);
1124 mdoc->next = MDOC_NEXT_CHILD;
1125 }
1126 if ( ! mdoc_macro(mdoc, c, line, lastarg,
1127 pos, buf))
1128 return(0);
1129 break;
1130 }
1131
1132 if ( ! flushed && mdoc_isdelim(p)) {
1133 if ( ! rewind_subblock(MDOC_HEAD, mdoc,
1134 tok, line, ppos))
1135 return(0);
1136 flushed = 1;
1137 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1138 return(0);
1139 mdoc->next = MDOC_NEXT_CHILD;
1140 }
1141
1142 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
1143 return(0);
1144 mdoc->next = MDOC_NEXT_SIBLING;
1145 }
1146
1147 if ( ! flushed) {
1148 if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
1149 return(0);
1150 if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
1151 return(0);
1152 mdoc->next = MDOC_NEXT_CHILD;
1153 }
1154
1155 if (ppos > 1)
1156 return(1);
1157 return(append_delims(mdoc, line, pos, buf));
1158 }
1159
1160
1161 /*
1162 * A delimited constant is very similar to the macros parsed by
1163 * macro_text except that, in the event of punctuation, the macro isn't
1164 * "re-opened" as it is in macro_text. Also, these macros have a fixed
1165 * number of parameters.
1166 *
1167 * .Fl a No b
1168 *
1169 * ELEMENT (.Fl)
1170 * TEXT (`a')
1171 * ELEMENT (.No)
1172 * TEXT (`b')
1173 */
1174 static int
1175 macro_constant_delimited(MACRO_PROT_ARGS)
1176 {
1177 int lastarg, flushed, j, c, maxargs, argc,
1178 igndelim;
1179 struct mdoc_arg argv[MDOC_LINEARG_MAX];
1180 char *p;
1181
1182 lastarg = ppos;
1183 flushed = 0;
1184
1185 switch (tok) {
1186 case (MDOC_No):
1187 /* FALLTHROUGH */
1188 case (MDOC_Ns):
1189 /* FALLTHROUGH */
1190 case (MDOC_Ux):
1191 /* FALLTHROUGH */
1192 case (MDOC_St):
1193 maxargs = 0;
1194 break;
1195 default:
1196 maxargs = 1;
1197 break;
1198 }
1199
1200 switch (tok) {
1201 case (MDOC_Pf):
1202 igndelim = 1;
1203 break;
1204 default:
1205 igndelim = 0;
1206 break;
1207 }
1208
1209 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
1210 lastarg = *pos;
1211 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
1212 if (ARGV_EOLN == c)
1213 break;
1214 if (ARGV_WORD == c) {
1215 *pos = lastarg;
1216 break;
1217 } else if (ARGV_ARG == c)
1218 continue;
1219 mdoc_argv_free(argc, argv);
1220 return(0);
1221 }
1222
1223 if (MDOC_LINEARG_MAX == argc) {
1224 mdoc_argv_free(argc - 1, argv);
1225 return(perr(mdoc, line, ppos, EARGVLIM));
1226 }
1227
1228 c = mdoc_elem_alloc(mdoc, line, ppos, tok, argc, argv);
1229 mdoc_argv_free(argc, argv);
1230
1231 if (0 == c)
1232 return(0);
1233
1234 mdoc->next = MDOC_NEXT_CHILD;
1235
1236 for (j = 0; /* No sentinel. */; j++) {
1237 lastarg = *pos;
1238
1239 if (j == maxargs && ! flushed) {
1240 if ( ! rewind_elem(mdoc, tok))
1241 return(0);
1242 flushed = 1;
1243 }
1244
1245 c = mdoc_args(mdoc, line, pos, buf, tok, &p);
1246 assert(ARGS_PHRASE != c);
1247
1248 if (ARGS_ERROR == c)
1249 return(0);
1250 if (ARGS_PUNCT == c)
1251 break;
1252 if (ARGS_EOLN == c)
1253 break;
1254
1255 if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
1256 return(0);
1257 else if (MDOC_MAX != c) {
1258 if ( ! flushed && ! rewind_elem(mdoc, tok))
1259 return(0);
1260 flushed = 1;
1261 if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
1262 return(0);
1263 break;
1264 }
1265
1266 if ( ! flushed && mdoc_isdelim(p) && ! igndelim) {
1267 if ( ! rewind_elem(mdoc, tok))
1268 return(0);
1269 flushed = 1;
1270 }
1271
1272 if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
1273 return(0);
1274 mdoc->next = MDOC_NEXT_SIBLING;
1275 }
1276
1277 if ( ! flushed && ! rewind_elem(mdoc, tok))
1278 return(0);
1279
1280 if (ppos > 1)
1281 return(1);
1282 return(append_delims(mdoc, line, pos, buf));
1283 }
1284
1285
1286 /*
1287 * A constant macro is the simplest classification. It spans an entire
1288 * line.
1289 */
1290 static int
1291 macro_constant(MACRO_PROT_ARGS)
1292 {
1293 int c, w, la, argc;
1294 struct mdoc_arg argv[MDOC_LINEARG_MAX];
1295 char *p;
1296
1297 assert( ! (MDOC_CALLABLE & mdoc_macros[tok].flags));
1298
1299 for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) {
1300 la = *pos;
1301 c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf);
1302 if (ARGV_EOLN == c)
1303 break;
1304 if (ARGV_WORD == c) {
1305 *pos = la;
1306 break;
1307 } else if (ARGV_ARG == c)
1308 continue;
1309
1310 mdoc_argv_free(argc, argv);
1311 return(0);
1312 }
1313
1314 if (MDOC_LINEARG_MAX == argc) {
1315 mdoc_argv_free(argc - 1, argv);
1316 return(perr(mdoc, line, ppos, EARGVLIM));
1317 }
1318
1319 c = mdoc_elem_alloc(mdoc, line, ppos, tok, argc, argv);
1320 mdoc_argv_free(argc, argv);
1321
1322 if (0 == c)
1323 return(0);
1324
1325 mdoc->next = MDOC_NEXT_CHILD;
1326
1327 for (;;) {
1328 la = *pos;
1329 w = mdoc_args(mdoc, line, pos, buf, tok, &p);
1330 assert(ARGS_PHRASE != c);
1331
1332 if (ARGS_ERROR == w)
1333 return(0);
1334 if (ARGS_EOLN == w)
1335 break;
1336
1337 c = ARGS_QWORD == w ? MDOC_MAX :
1338 lookup(mdoc, line, la, tok, p);
1339
1340 if (MDOC_MAX != c && -1 != c) {
1341 if ( ! rewind_elem(mdoc, tok))
1342 return(0);
1343 return(mdoc_macro(mdoc, c, line, la, pos, buf));
1344 } else if (-1 == c)
1345 return(0);
1346
1347 if ( ! mdoc_word_alloc(mdoc, line, la, p))
1348 return(0);
1349 mdoc->next = MDOC_NEXT_SIBLING;
1350 }
1351
1352 return(rewind_elem(mdoc, tok));
1353 }
1354
1355
1356 /* ARGSUSED */
1357 static int
1358 macro_obsolete(MACRO_PROT_ARGS)
1359 {
1360
1361 return(pwarn(mdoc, line, ppos, WOBS));
1362 }
1363
1364
1365 /*
1366 * This is called at the end of parsing. It must traverse up the tree,
1367 * closing out open [implicit] scopes. Obviously, open explicit scopes
1368 * are errors.
1369 */
1370 int
1371 macro_end(struct mdoc *mdoc)
1372 {
1373 struct mdoc_node *n;
1374
1375 assert(mdoc->first);
1376 assert(mdoc->last);
1377
1378 /* Scan for open explicit scopes. */
1379
1380 n = MDOC_VALID & mdoc->last->flags ?
1381 mdoc->last->parent : mdoc->last;
1382
1383 for ( ; n; n = n->parent) {
1384 if (MDOC_BLOCK != n->type)
1385 continue;
1386 if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags))
1387 continue;
1388 return(mdoc_nerr(mdoc, n,
1389 "macro scope still open on exit"));
1390 }
1391
1392 return(rewind_last(mdoc, mdoc->first));
1393 }