]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - mdoc_validate.c
Import CVS version of mandoc as of 20141201
[FreeBSD/FreeBSD.git] / mdoc_validate.c
1 /*      $Id: mdoc_validate.c,v 1.262 2014/11/28 18:36:35 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
5  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include "config.h"
20
21 #include <sys/types.h>
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
25
26 #include <assert.h>
27 #include <ctype.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33
34 #include "mdoc.h"
35 #include "mandoc.h"
36 #include "mandoc_aux.h"
37 #include "libmdoc.h"
38 #include "libmandoc.h"
39
40 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
41
42 #define PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
43 #define POST_ARGS struct mdoc *mdoc
44
45 enum    check_ineq {
46         CHECK_LT,
47         CHECK_GT,
48         CHECK_EQ
49 };
50
51 enum    check_lvl {
52         CHECK_WARN,
53         CHECK_ERROR,
54 };
55
56 typedef void    (*v_pre)(PRE_ARGS);
57 typedef void    (*v_post)(POST_ARGS);
58
59 struct  valids {
60         v_pre    pre;
61         v_post   post;
62 };
63
64 static  void     check_count(struct mdoc *, enum mdoc_type,
65                         enum check_lvl, enum check_ineq, int);
66 static  void     check_text(struct mdoc *, int, int, char *);
67 static  void     check_argv(struct mdoc *,
68                         struct mdoc_node *, struct mdoc_argv *);
69 static  void     check_args(struct mdoc *, struct mdoc_node *);
70 static  int      child_an(const struct mdoc_node *);
71 static  enum mdoc_sec   a2sec(const char *);
72 static  size_t          macro2len(enum mdoct);
73 static  void     rewrite_macro2len(char **);
74
75 static  void     bwarn_ge1(POST_ARGS);
76 static  void     ewarn_eq1(POST_ARGS);
77 static  void     ewarn_ge1(POST_ARGS);
78 static  void     hwarn_eq0(POST_ARGS);
79
80 static  void     post_an(POST_ARGS);
81 static  void     post_at(POST_ARGS);
82 static  void     post_bf(POST_ARGS);
83 static  void     post_bk(POST_ARGS);
84 static  void     post_bl(POST_ARGS);
85 static  void     post_bl_block(POST_ARGS);
86 static  void     post_bl_block_tag(POST_ARGS);
87 static  void     post_bl_head(POST_ARGS);
88 static  void     post_bx(POST_ARGS);
89 static  void     post_d1(POST_ARGS);
90 static  void     post_defaults(POST_ARGS);
91 static  void     post_dd(POST_ARGS);
92 static  void     post_dt(POST_ARGS);
93 static  void     post_en(POST_ARGS);
94 static  void     post_es(POST_ARGS);
95 static  void     post_eoln(POST_ARGS);
96 static  void     post_ex(POST_ARGS);
97 static  void     post_fa(POST_ARGS);
98 static  void     post_fn(POST_ARGS);
99 static  void     post_fname(POST_ARGS);
100 static  void     post_fo(POST_ARGS);
101 static  void     post_hyph(POST_ARGS);
102 static  void     post_hyphtext(POST_ARGS);
103 static  void     post_ignpar(POST_ARGS);
104 static  void     post_it(POST_ARGS);
105 static  void     post_lb(POST_ARGS);
106 static  void     post_literal(POST_ARGS);
107 static  void     post_nd(POST_ARGS);
108 static  void     post_nm(POST_ARGS);
109 static  void     post_ns(POST_ARGS);
110 static  void     post_os(POST_ARGS);
111 static  void     post_par(POST_ARGS);
112 static  void     post_root(POST_ARGS);
113 static  void     post_rs(POST_ARGS);
114 static  void     post_sh(POST_ARGS);
115 static  void     post_sh_head(POST_ARGS);
116 static  void     post_sh_name(POST_ARGS);
117 static  void     post_sh_see_also(POST_ARGS);
118 static  void     post_sh_authors(POST_ARGS);
119 static  void     post_sm(POST_ARGS);
120 static  void     post_st(POST_ARGS);
121 static  void     post_vt(POST_ARGS);
122
123 static  void     pre_an(PRE_ARGS);
124 static  void     pre_bd(PRE_ARGS);
125 static  void     pre_bl(PRE_ARGS);
126 static  void     pre_dd(PRE_ARGS);
127 static  void     pre_display(PRE_ARGS);
128 static  void     pre_dt(PRE_ARGS);
129 static  void     pre_literal(PRE_ARGS);
130 static  void     pre_obsolete(PRE_ARGS);
131 static  void     pre_os(PRE_ARGS);
132 static  void     pre_par(PRE_ARGS);
133 static  void     pre_std(PRE_ARGS);
134
135 static  const struct valids mdoc_valids[MDOC_MAX] = {
136         { NULL, NULL },                         /* Ap */
137         { pre_dd, post_dd },                    /* Dd */
138         { pre_dt, post_dt },                    /* Dt */
139         { pre_os, post_os },                    /* Os */
140         { NULL, post_sh },                      /* Sh */
141         { NULL, post_ignpar },                  /* Ss */
142         { pre_par, post_par },                  /* Pp */
143         { pre_display, post_d1 },               /* D1 */
144         { pre_literal, post_literal },          /* Dl */
145         { pre_bd, post_literal },               /* Bd */
146         { NULL, NULL },                         /* Ed */
147         { pre_bl, post_bl },                    /* Bl */
148         { NULL, NULL },                         /* El */
149         { pre_par, post_it },                   /* It */
150         { NULL, NULL },                         /* Ad */
151         { pre_an, post_an },                    /* An */
152         { NULL, post_defaults },                /* Ar */
153         { NULL, NULL },                         /* Cd */
154         { NULL, NULL },                         /* Cm */
155         { NULL, NULL },                         /* Dv */
156         { NULL, NULL },                         /* Er */
157         { NULL, NULL },                         /* Ev */
158         { pre_std, post_ex },                   /* Ex */
159         { NULL, post_fa },                      /* Fa */
160         { NULL, ewarn_ge1 },                    /* Fd */
161         { NULL, NULL },                         /* Fl */
162         { NULL, post_fn },                      /* Fn */
163         { NULL, NULL },                         /* Ft */
164         { NULL, NULL },                         /* Ic */
165         { NULL, ewarn_eq1 },                    /* In */
166         { NULL, post_defaults },                /* Li */
167         { NULL, post_nd },                      /* Nd */
168         { NULL, post_nm },                      /* Nm */
169         { NULL, NULL },                         /* Op */
170         { pre_obsolete, NULL },                 /* Ot */
171         { NULL, post_defaults },                /* Pa */
172         { pre_std, NULL },                      /* Rv */
173         { NULL, post_st },                      /* St */
174         { NULL, NULL },                         /* Va */
175         { NULL, post_vt },                      /* Vt */
176         { NULL, ewarn_ge1 },                    /* Xr */
177         { NULL, ewarn_ge1 },                    /* %A */
178         { NULL, post_hyphtext },                /* %B */ /* FIXME: can be used outside Rs/Re. */
179         { NULL, ewarn_ge1 },                    /* %D */
180         { NULL, ewarn_ge1 },                    /* %I */
181         { NULL, ewarn_ge1 },                    /* %J */
182         { NULL, post_hyphtext },                /* %N */
183         { NULL, post_hyphtext },                /* %O */
184         { NULL, ewarn_ge1 },                    /* %P */
185         { NULL, post_hyphtext },                /* %R */
186         { NULL, post_hyphtext },                /* %T */ /* FIXME: can be used outside Rs/Re. */
187         { NULL, ewarn_ge1 },                    /* %V */
188         { NULL, NULL },                         /* Ac */
189         { NULL, NULL },                         /* Ao */
190         { NULL, NULL },                         /* Aq */
191         { NULL, post_at },                      /* At */
192         { NULL, NULL },                         /* Bc */
193         { NULL, post_bf },                      /* Bf */
194         { NULL, NULL },                         /* Bo */
195         { NULL, NULL },                         /* Bq */
196         { NULL, NULL },                         /* Bsx */
197         { NULL, post_bx },                      /* Bx */
198         { pre_obsolete, NULL },                 /* Db */
199         { NULL, NULL },                         /* Dc */
200         { NULL, NULL },                         /* Do */
201         { NULL, NULL },                         /* Dq */
202         { NULL, NULL },                         /* Ec */
203         { NULL, NULL },                         /* Ef */
204         { NULL, NULL },                         /* Em */
205         { NULL, NULL },                         /* Eo */
206         { NULL, NULL },                         /* Fx */
207         { NULL, NULL },                         /* Ms */
208         { NULL, NULL },                         /* No */
209         { NULL, post_ns },                      /* Ns */
210         { NULL, NULL },                         /* Nx */
211         { NULL, NULL },                         /* Ox */
212         { NULL, NULL },                         /* Pc */
213         { NULL, ewarn_eq1 },                    /* Pf */
214         { NULL, NULL },                         /* Po */
215         { NULL, NULL },                         /* Pq */
216         { NULL, NULL },                         /* Qc */
217         { NULL, NULL },                         /* Ql */
218         { NULL, NULL },                         /* Qo */
219         { NULL, NULL },                         /* Qq */
220         { NULL, NULL },                         /* Re */
221         { NULL, post_rs },                      /* Rs */
222         { NULL, NULL },                         /* Sc */
223         { NULL, NULL },                         /* So */
224         { NULL, NULL },                         /* Sq */
225         { NULL, post_sm },                      /* Sm */
226         { NULL, post_hyph },                    /* Sx */
227         { NULL, NULL },                         /* Sy */
228         { NULL, NULL },                         /* Tn */
229         { NULL, NULL },                         /* Ux */
230         { NULL, NULL },                         /* Xc */
231         { NULL, NULL },                         /* Xo */
232         { NULL, post_fo },                      /* Fo */
233         { NULL, NULL },                         /* Fc */
234         { NULL, NULL },                         /* Oo */
235         { NULL, NULL },                         /* Oc */
236         { NULL, post_bk },                      /* Bk */
237         { NULL, NULL },                         /* Ek */
238         { NULL, post_eoln },                    /* Bt */
239         { NULL, NULL },                         /* Hf */
240         { pre_obsolete, NULL },                 /* Fr */
241         { NULL, post_eoln },                    /* Ud */
242         { NULL, post_lb },                      /* Lb */
243         { pre_par, post_par },                  /* Lp */
244         { NULL, NULL },                         /* Lk */
245         { NULL, post_defaults },                /* Mt */
246         { NULL, NULL },                         /* Brq */
247         { NULL, NULL },                         /* Bro */
248         { NULL, NULL },                         /* Brc */
249         { NULL, ewarn_ge1 },                    /* %C */
250         { pre_obsolete, post_es },              /* Es */
251         { pre_obsolete, post_en },              /* En */
252         { NULL, NULL },                         /* Dx */
253         { NULL, ewarn_ge1 },                    /* %Q */
254         { NULL, post_par },                     /* br */
255         { NULL, post_par },                     /* sp */
256         { NULL, ewarn_eq1 },                    /* %U */
257         { NULL, NULL },                         /* Ta */
258         { NULL, NULL },                         /* ll */
259 };
260
261 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
262
263 static  const enum mdoct rsord[RSORD_MAX] = {
264         MDOC__A,
265         MDOC__T,
266         MDOC__B,
267         MDOC__I,
268         MDOC__J,
269         MDOC__R,
270         MDOC__N,
271         MDOC__V,
272         MDOC__U,
273         MDOC__P,
274         MDOC__Q,
275         MDOC__C,
276         MDOC__D,
277         MDOC__O
278 };
279
280 static  const char * const secnames[SEC__MAX] = {
281         NULL,
282         "NAME",
283         "LIBRARY",
284         "SYNOPSIS",
285         "DESCRIPTION",
286         "CONTEXT",
287         "IMPLEMENTATION NOTES",
288         "RETURN VALUES",
289         "ENVIRONMENT",
290         "FILES",
291         "EXIT STATUS",
292         "EXAMPLES",
293         "DIAGNOSTICS",
294         "COMPATIBILITY",
295         "ERRORS",
296         "SEE ALSO",
297         "STANDARDS",
298         "HISTORY",
299         "AUTHORS",
300         "CAVEATS",
301         "BUGS",
302         "SECURITY CONSIDERATIONS",
303         NULL
304 };
305
306
307 void
308 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
309 {
310         v_pre    p;
311
312         switch (n->type) {
313         case MDOC_TEXT:
314                 check_text(mdoc, n->line, n->pos, n->string);
315                 /* FALLTHROUGH */
316         case MDOC_TBL:
317                 /* FALLTHROUGH */
318         case MDOC_EQN:
319                 /* FALLTHROUGH */
320         case MDOC_ROOT:
321                 return;
322         default:
323                 break;
324         }
325
326         check_args(mdoc, n);
327         p = mdoc_valids[n->tok].pre;
328         if (*p)
329                 (*p)(mdoc, n);
330 }
331
332 void
333 mdoc_valid_post(struct mdoc *mdoc)
334 {
335         struct mdoc_node *n;
336         v_post p;
337
338         n = mdoc->last;
339         if (n->flags & MDOC_VALID)
340                 return;
341         n->flags |= MDOC_VALID;
342
343         switch (n->type) {
344         case MDOC_TEXT:
345                 /* FALLTHROUGH */
346         case MDOC_EQN:
347                 /* FALLTHROUGH */
348         case MDOC_TBL:
349                 break;
350         case MDOC_ROOT:
351                 post_root(mdoc);
352                 break;
353         default:
354
355                 /*
356                  * Closing delimiters are not special at the
357                  * beginning of a block, opening delimiters
358                  * are not special at the end.
359                  */
360
361                 if (n->child != NULL)
362                         n->child->flags &= ~MDOC_DELIMC;
363                 if (n->last != NULL)
364                         n->last->flags &= ~MDOC_DELIMO;
365
366                 /* Call the macro's postprocessor. */
367
368                 p = mdoc_valids[n->tok].post;
369                 if (*p)
370                         (*p)(mdoc);
371                 break;
372         }
373 }
374
375 static void
376 check_count(struct mdoc *mdoc, enum mdoc_type type,
377                 enum check_lvl lvl, enum check_ineq ineq, int val)
378 {
379         const char      *p;
380         enum mandocerr   t;
381
382         if (mdoc->last->type != type)
383                 return;
384
385         switch (ineq) {
386         case CHECK_LT:
387                 p = "less than ";
388                 if (mdoc->last->nchild < val)
389                         return;
390                 break;
391         case CHECK_GT:
392                 p = "more than ";
393                 if (mdoc->last->nchild > val)
394                         return;
395                 break;
396         case CHECK_EQ:
397                 p = "";
398                 if (val == mdoc->last->nchild)
399                         return;
400                 break;
401         default:
402                 abort();
403                 /* NOTREACHED */
404         }
405
406         t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
407         mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
408             mdoc->last->pos, "want %s%d children (have %d)",
409             p, val, mdoc->last->nchild);
410 }
411
412 static void
413 bwarn_ge1(POST_ARGS)
414 {
415         check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
416 }
417
418 static void
419 ewarn_eq1(POST_ARGS)
420 {
421         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
422 }
423
424 static void
425 ewarn_ge1(POST_ARGS)
426 {
427         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
428 }
429
430 static void
431 hwarn_eq0(POST_ARGS)
432 {
433         check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
434 }
435
436 static void
437 check_args(struct mdoc *mdoc, struct mdoc_node *n)
438 {
439         int              i;
440
441         if (NULL == n->args)
442                 return;
443
444         assert(n->args->argc);
445         for (i = 0; i < (int)n->args->argc; i++)
446                 check_argv(mdoc, n, &n->args->argv[i]);
447 }
448
449 static void
450 check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
451 {
452         int              i;
453
454         for (i = 0; i < (int)v->sz; i++)
455                 check_text(mdoc, v->line, v->pos, v->value[i]);
456 }
457
458 static void
459 check_text(struct mdoc *mdoc, int ln, int pos, char *p)
460 {
461         char            *cp;
462
463         if (MDOC_LITERAL & mdoc->flags)
464                 return;
465
466         for (cp = p; NULL != (p = strchr(p, '\t')); p++)
467                 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
468                     ln, pos + (int)(p - cp), NULL);
469 }
470
471 static void
472 pre_display(PRE_ARGS)
473 {
474         struct mdoc_node *node;
475
476         if (MDOC_BLOCK != n->type)
477                 return;
478
479         for (node = mdoc->last->parent; node; node = node->parent)
480                 if (MDOC_BLOCK == node->type)
481                         if (MDOC_Bd == node->tok)
482                                 break;
483
484         if (node)
485                 mandoc_vmsg(MANDOCERR_BD_NEST,
486                     mdoc->parse, n->line, n->pos,
487                     "%s in Bd", mdoc_macronames[n->tok]);
488 }
489
490 static void
491 pre_bl(PRE_ARGS)
492 {
493         struct mdoc_node *np;
494         struct mdoc_argv *argv, *wa;
495         int               i;
496         enum mdocargt     mdoclt;
497         enum mdoc_list    lt;
498
499         if (MDOC_BLOCK != n->type) {
500                 if (ENDBODY_NOT != n->end) {
501                         assert(n->pending);
502                         np = n->pending->parent;
503                 } else
504                         np = n->parent;
505
506                 assert(np);
507                 assert(MDOC_BLOCK == np->type);
508                 assert(MDOC_Bl == np->tok);
509                 return;
510         }
511
512         /*
513          * First figure out which kind of list to use: bind ourselves to
514          * the first mentioned list type and warn about any remaining
515          * ones.  If we find no list type, we default to LIST_item.
516          */
517
518         wa = (n->args == NULL) ? NULL : n->args->argv;
519         mdoclt = MDOC_ARG_MAX;
520         for (i = 0; n->args && i < (int)n->args->argc; i++) {
521                 argv = n->args->argv + i;
522                 lt = LIST__NONE;
523                 switch (argv->arg) {
524                 /* Set list types. */
525                 case MDOC_Bullet:
526                         lt = LIST_bullet;
527                         break;
528                 case MDOC_Dash:
529                         lt = LIST_dash;
530                         break;
531                 case MDOC_Enum:
532                         lt = LIST_enum;
533                         break;
534                 case MDOC_Hyphen:
535                         lt = LIST_hyphen;
536                         break;
537                 case MDOC_Item:
538                         lt = LIST_item;
539                         break;
540                 case MDOC_Tag:
541                         lt = LIST_tag;
542                         break;
543                 case MDOC_Diag:
544                         lt = LIST_diag;
545                         break;
546                 case MDOC_Hang:
547                         lt = LIST_hang;
548                         break;
549                 case MDOC_Ohang:
550                         lt = LIST_ohang;
551                         break;
552                 case MDOC_Inset:
553                         lt = LIST_inset;
554                         break;
555                 case MDOC_Column:
556                         lt = LIST_column;
557                         break;
558                 /* Set list arguments. */
559                 case MDOC_Compact:
560                         if (n->norm->Bl.comp)
561                                 mandoc_msg(MANDOCERR_ARG_REP,
562                                     mdoc->parse, argv->line,
563                                     argv->pos, "Bl -compact");
564                         n->norm->Bl.comp = 1;
565                         break;
566                 case MDOC_Width:
567                         wa = argv;
568                         if (0 == argv->sz) {
569                                 mandoc_msg(MANDOCERR_ARG_EMPTY,
570                                     mdoc->parse, argv->line,
571                                     argv->pos, "Bl -width");
572                                 n->norm->Bl.width = "0n";
573                                 break;
574                         }
575                         if (NULL != n->norm->Bl.width)
576                                 mandoc_vmsg(MANDOCERR_ARG_REP,
577                                     mdoc->parse, argv->line,
578                                     argv->pos, "Bl -width %s",
579                                     argv->value[0]);
580                         rewrite_macro2len(argv->value);
581                         n->norm->Bl.width = argv->value[0];
582                         break;
583                 case MDOC_Offset:
584                         if (0 == argv->sz) {
585                                 mandoc_msg(MANDOCERR_ARG_EMPTY,
586                                     mdoc->parse, argv->line,
587                                     argv->pos, "Bl -offset");
588                                 break;
589                         }
590                         if (NULL != n->norm->Bl.offs)
591                                 mandoc_vmsg(MANDOCERR_ARG_REP,
592                                     mdoc->parse, argv->line,
593                                     argv->pos, "Bl -offset %s",
594                                     argv->value[0]);
595                         rewrite_macro2len(argv->value);
596                         n->norm->Bl.offs = argv->value[0];
597                         break;
598                 default:
599                         continue;
600                 }
601                 if (LIST__NONE == lt)
602                         continue;
603                 mdoclt = argv->arg;
604
605                 /* Check: multiple list types. */
606
607                 if (LIST__NONE != n->norm->Bl.type) {
608                         mandoc_vmsg(MANDOCERR_BL_REP,
609                             mdoc->parse, n->line, n->pos,
610                             "Bl -%s", mdoc_argnames[argv->arg]);
611                         continue;
612                 }
613
614                 /* The list type should come first. */
615
616                 if (n->norm->Bl.width ||
617                     n->norm->Bl.offs ||
618                     n->norm->Bl.comp)
619                         mandoc_vmsg(MANDOCERR_BL_LATETYPE,
620                             mdoc->parse, n->line, n->pos, "Bl -%s",
621                             mdoc_argnames[n->args->argv[0].arg]);
622
623                 n->norm->Bl.type = lt;
624                 if (LIST_column == lt) {
625                         n->norm->Bl.ncols = argv->sz;
626                         n->norm->Bl.cols = (void *)argv->value;
627                 }
628         }
629
630         /* Allow lists to default to LIST_item. */
631
632         if (LIST__NONE == n->norm->Bl.type) {
633                 mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
634                     n->line, n->pos, "Bl");
635                 n->norm->Bl.type = LIST_item;
636         }
637
638         /*
639          * Validate the width field.  Some list types don't need width
640          * types and should be warned about them.  Others should have it
641          * and must also be warned.  Yet others have a default and need
642          * no warning.
643          */
644
645         switch (n->norm->Bl.type) {
646         case LIST_tag:
647                 if (NULL == n->norm->Bl.width)
648                         mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
649                             n->line, n->pos, "Bl -tag");
650                 break;
651         case LIST_column:
652                 /* FALLTHROUGH */
653         case LIST_diag:
654                 /* FALLTHROUGH */
655         case LIST_ohang:
656                 /* FALLTHROUGH */
657         case LIST_inset:
658                 /* FALLTHROUGH */
659         case LIST_item:
660                 if (n->norm->Bl.width)
661                         mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
662                             wa->line, wa->pos, "Bl -%s",
663                             mdoc_argnames[mdoclt]);
664                 break;
665         case LIST_bullet:
666                 /* FALLTHROUGH */
667         case LIST_dash:
668                 /* FALLTHROUGH */
669         case LIST_hyphen:
670                 if (NULL == n->norm->Bl.width)
671                         n->norm->Bl.width = "2n";
672                 break;
673         case LIST_enum:
674                 if (NULL == n->norm->Bl.width)
675                         n->norm->Bl.width = "3n";
676                 break;
677         default:
678                 break;
679         }
680         pre_par(mdoc, n);
681 }
682
683 static void
684 pre_bd(PRE_ARGS)
685 {
686         struct mdoc_node *np;
687         struct mdoc_argv *argv;
688         int               i;
689         enum mdoc_disp    dt;
690
691         pre_literal(mdoc, n);
692
693         if (MDOC_BLOCK != n->type) {
694                 if (ENDBODY_NOT != n->end) {
695                         assert(n->pending);
696                         np = n->pending->parent;
697                 } else
698                         np = n->parent;
699
700                 assert(np);
701                 assert(MDOC_BLOCK == np->type);
702                 assert(MDOC_Bd == np->tok);
703                 return;
704         }
705
706         for (i = 0; n->args && i < (int)n->args->argc; i++) {
707                 argv = n->args->argv + i;
708                 dt = DISP__NONE;
709
710                 switch (argv->arg) {
711                 case MDOC_Centred:
712                         dt = DISP_centered;
713                         break;
714                 case MDOC_Ragged:
715                         dt = DISP_ragged;
716                         break;
717                 case MDOC_Unfilled:
718                         dt = DISP_unfilled;
719                         break;
720                 case MDOC_Filled:
721                         dt = DISP_filled;
722                         break;
723                 case MDOC_Literal:
724                         dt = DISP_literal;
725                         break;
726                 case MDOC_File:
727                         mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
728                             n->line, n->pos, NULL);
729                         break;
730                 case MDOC_Offset:
731                         if (0 == argv->sz) {
732                                 mandoc_msg(MANDOCERR_ARG_EMPTY,
733                                     mdoc->parse, argv->line,
734                                     argv->pos, "Bd -offset");
735                                 break;
736                         }
737                         if (NULL != n->norm->Bd.offs)
738                                 mandoc_vmsg(MANDOCERR_ARG_REP,
739                                     mdoc->parse, argv->line,
740                                     argv->pos, "Bd -offset %s",
741                                     argv->value[0]);
742                         rewrite_macro2len(argv->value);
743                         n->norm->Bd.offs = argv->value[0];
744                         break;
745                 case MDOC_Compact:
746                         if (n->norm->Bd.comp)
747                                 mandoc_msg(MANDOCERR_ARG_REP,
748                                     mdoc->parse, argv->line,
749                                     argv->pos, "Bd -compact");
750                         n->norm->Bd.comp = 1;
751                         break;
752                 default:
753                         abort();
754                         /* NOTREACHED */
755                 }
756                 if (DISP__NONE == dt)
757                         continue;
758
759                 if (DISP__NONE == n->norm->Bd.type)
760                         n->norm->Bd.type = dt;
761                 else
762                         mandoc_vmsg(MANDOCERR_BD_REP,
763                             mdoc->parse, n->line, n->pos,
764                             "Bd -%s", mdoc_argnames[argv->arg]);
765         }
766
767         if (DISP__NONE == n->norm->Bd.type) {
768                 mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
769                     n->line, n->pos, "Bd");
770                 n->norm->Bd.type = DISP_ragged;
771         }
772         pre_par(mdoc, n);
773 }
774
775 static void
776 pre_an(PRE_ARGS)
777 {
778         struct mdoc_argv *argv;
779         size_t   i;
780
781         if (n->args == NULL)
782                 return;
783
784         for (i = 1; i < n->args->argc; i++) {
785                 argv = n->args->argv + i;
786                 mandoc_vmsg(MANDOCERR_AN_REP,
787                     mdoc->parse, argv->line, argv->pos,
788                     "An -%s", mdoc_argnames[argv->arg]);
789         }
790
791         argv = n->args->argv;
792         if (argv->arg == MDOC_Split)
793                 n->norm->An.auth = AUTH_split;
794         else if (argv->arg == MDOC_Nosplit)
795                 n->norm->An.auth = AUTH_nosplit;
796         else
797                 abort();
798 }
799
800 static void
801 pre_std(PRE_ARGS)
802 {
803
804         if (n->args && 1 == n->args->argc)
805                 if (MDOC_Std == n->args->argv[0].arg)
806                         return;
807
808         mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
809             n->line, n->pos, mdoc_macronames[n->tok]);
810 }
811
812 static void
813 pre_obsolete(PRE_ARGS)
814 {
815
816         if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
817                 mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
818                     n->line, n->pos, mdoc_macronames[n->tok]);
819 }
820
821 static void
822 pre_dt(PRE_ARGS)
823 {
824
825         if (mdoc->meta.title != NULL)
826                 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
827                     n->line, n->pos, "Dt");
828         else if (mdoc->meta.os != NULL)
829                 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
830                     n->line, n->pos, "Dt after Os");
831 }
832
833 static void
834 pre_os(PRE_ARGS)
835 {
836
837         if (mdoc->meta.os != NULL)
838                 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
839                     n->line, n->pos, "Os");
840         else if (mdoc->flags & MDOC_PBODY)
841                 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
842                     n->line, n->pos, "Os");
843 }
844
845 static void
846 pre_dd(PRE_ARGS)
847 {
848
849         if (mdoc->meta.date != NULL)
850                 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
851                     n->line, n->pos, "Dd");
852         else if (mdoc->flags & MDOC_PBODY)
853                 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
854                     n->line, n->pos, "Dd");
855         else if (mdoc->meta.title != NULL)
856                 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
857                     n->line, n->pos, "Dd after Dt");
858         else if (mdoc->meta.os != NULL)
859                 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
860                     n->line, n->pos, "Dd after Os");
861 }
862
863 static void
864 post_bf(POST_ARGS)
865 {
866         struct mdoc_node *np, *nch;
867         enum mdocargt     arg;
868
869         /*
870          * Unlike other data pointers, these are "housed" by the HEAD
871          * element, which contains the goods.
872          */
873
874         if (MDOC_HEAD != mdoc->last->type) {
875                 if (ENDBODY_NOT != mdoc->last->end) {
876                         assert(mdoc->last->pending);
877                         np = mdoc->last->pending->parent->head;
878                 } else if (MDOC_BLOCK != mdoc->last->type) {
879                         np = mdoc->last->parent->head;
880                 } else
881                         np = mdoc->last->head;
882
883                 assert(np);
884                 assert(MDOC_HEAD == np->type);
885                 assert(MDOC_Bf == np->tok);
886                 return;
887         }
888
889         np = mdoc->last;
890         assert(MDOC_BLOCK == np->parent->type);
891         assert(MDOC_Bf == np->parent->tok);
892
893         /* Check the number of arguments. */
894
895         nch = np->child;
896         if (NULL == np->parent->args) {
897                 if (NULL == nch) {
898                         mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
899                             np->line, np->pos, "Bf");
900                         return;
901                 }
902                 nch = nch->next;
903         }
904         if (NULL != nch)
905                 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
906                     nch->line, nch->pos, "Bf ... %s", nch->string);
907
908         /* Extract argument into data. */
909
910         if (np->parent->args) {
911                 arg = np->parent->args->argv[0].arg;
912                 if (MDOC_Emphasis == arg)
913                         np->norm->Bf.font = FONT_Em;
914                 else if (MDOC_Literal == arg)
915                         np->norm->Bf.font = FONT_Li;
916                 else if (MDOC_Symbolic == arg)
917                         np->norm->Bf.font = FONT_Sy;
918                 else
919                         abort();
920                 return;
921         }
922
923         /* Extract parameter into data. */
924
925         if (0 == strcmp(np->child->string, "Em"))
926                 np->norm->Bf.font = FONT_Em;
927         else if (0 == strcmp(np->child->string, "Li"))
928                 np->norm->Bf.font = FONT_Li;
929         else if (0 == strcmp(np->child->string, "Sy"))
930                 np->norm->Bf.font = FONT_Sy;
931         else
932                 mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
933                     np->child->line, np->child->pos,
934                     "Bf %s", np->child->string);
935 }
936
937 static void
938 post_lb(POST_ARGS)
939 {
940         struct mdoc_node        *n;
941         const char              *stdlibname;
942         char                    *libname;
943
944         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
945         n = mdoc->last->child;
946         assert(MDOC_TEXT == n->type);
947
948         if (NULL == (stdlibname = mdoc_a2lib(n->string)))
949                 mandoc_asprintf(&libname,
950                     "library \\(lq%s\\(rq", n->string);
951         else
952                 libname = mandoc_strdup(stdlibname);
953
954         free(n->string);
955         n->string = libname;
956 }
957
958 static void
959 post_eoln(POST_ARGS)
960 {
961         const struct mdoc_node *n;
962
963         n = mdoc->last;
964         if (n->child)
965                 mandoc_vmsg(MANDOCERR_ARG_SKIP,
966                     mdoc->parse, n->line, n->pos,
967                     "%s %s", mdoc_macronames[n->tok],
968                     n->child->string);
969 }
970
971 static void
972 post_fname(POST_ARGS)
973 {
974         const struct mdoc_node  *n;
975         const char              *cp;
976         size_t                   pos;
977
978         n = mdoc->last->child;
979         pos = strcspn(n->string, "()");
980         cp = n->string + pos;
981         if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
982                 mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
983                     n->line, n->pos + pos, n->string);
984 }
985
986 static void
987 post_fn(POST_ARGS)
988 {
989
990         post_fname(mdoc);
991         post_fa(mdoc);
992 }
993
994 static void
995 post_fo(POST_ARGS)
996 {
997
998         check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1);
999         bwarn_ge1(mdoc);
1000         if (mdoc->last->type == MDOC_HEAD && mdoc->last->nchild)
1001                 post_fname(mdoc);
1002 }
1003
1004 static void
1005 post_fa(POST_ARGS)
1006 {
1007         const struct mdoc_node *n;
1008         const char *cp;
1009
1010         for (n = mdoc->last->child; n != NULL; n = n->next) {
1011                 for (cp = n->string; *cp != '\0'; cp++) {
1012                         /* Ignore callbacks and alterations. */
1013                         if (*cp == '(' || *cp == '{')
1014                                 break;
1015                         if (*cp != ',')
1016                                 continue;
1017                         mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
1018                             n->line, n->pos + (cp - n->string),
1019                             n->string);
1020                         break;
1021                 }
1022         }
1023 }
1024
1025 static void
1026 post_vt(POST_ARGS)
1027 {
1028         const struct mdoc_node *n;
1029
1030         /*
1031          * The Vt macro comes in both ELEM and BLOCK form, both of which
1032          * have different syntaxes (yet more context-sensitive
1033          * behaviour).  ELEM types must have a child, which is already
1034          * guaranteed by the in_line parsing routine; BLOCK types,
1035          * specifically the BODY, should only have TEXT children.
1036          */
1037
1038         if (MDOC_BODY != mdoc->last->type)
1039                 return;
1040
1041         for (n = mdoc->last->child; n; n = n->next)
1042                 if (MDOC_TEXT != n->type)
1043                         mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1044                             n->line, n->pos, mdoc_macronames[n->tok]);
1045 }
1046
1047 static void
1048 post_nm(POST_ARGS)
1049 {
1050
1051         if (NULL != mdoc->meta.name)
1052                 return;
1053
1054         mdoc_deroff(&mdoc->meta.name, mdoc->last);
1055
1056         if (NULL == mdoc->meta.name)
1057                 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1058                     mdoc->last->line, mdoc->last->pos, "Nm");
1059 }
1060
1061 static void
1062 post_nd(POST_ARGS)
1063 {
1064
1065         check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0);
1066         post_hyph(mdoc);
1067 }
1068
1069 static void
1070 post_d1(POST_ARGS)
1071 {
1072
1073         bwarn_ge1(mdoc);
1074         post_hyph(mdoc);
1075 }
1076
1077 static void
1078 post_literal(POST_ARGS)
1079 {
1080
1081         if (mdoc->last->tok == MDOC_Bd)
1082                 hwarn_eq0(mdoc);
1083         bwarn_ge1(mdoc);
1084
1085         /*
1086          * The `Dl' (note "el" not "one") and `Bd' macros unset the
1087          * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1088          * this in literal mode, but it doesn't hurt to just switch it
1089          * off in general since displays can't be nested.
1090          */
1091
1092         if (MDOC_BODY == mdoc->last->type)
1093                 mdoc->flags &= ~MDOC_LITERAL;
1094 }
1095
1096 static void
1097 post_defaults(POST_ARGS)
1098 {
1099         struct mdoc_node *nn;
1100
1101         /*
1102          * The `Ar' defaults to "file ..." if no value is provided as an
1103          * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1104          * gets an empty string.
1105          */
1106
1107         if (mdoc->last->child)
1108                 return;
1109
1110         nn = mdoc->last;
1111         mdoc->next = MDOC_NEXT_CHILD;
1112
1113         switch (nn->tok) {
1114         case MDOC_Ar:
1115                 mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
1116                 mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
1117                 break;
1118         case MDOC_Pa:
1119                 /* FALLTHROUGH */
1120         case MDOC_Mt:
1121                 mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
1122                 break;
1123         default:
1124                 abort();
1125                 /* NOTREACHED */
1126         }
1127         mdoc->last = nn;
1128 }
1129
1130 static void
1131 post_at(POST_ARGS)
1132 {
1133         struct mdoc_node        *n;
1134         const char              *std_att;
1135         char                    *att;
1136
1137         n = mdoc->last;
1138         if (n->child == NULL) {
1139                 mdoc->next = MDOC_NEXT_CHILD;
1140                 mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1141                 mdoc->last = n;
1142                 return;
1143         }
1144
1145         /*
1146          * If we have a child, look it up in the standard keys.  If a
1147          * key exist, use that instead of the child; if it doesn't,
1148          * prefix "AT&T UNIX " to the existing data.
1149          */
1150
1151         n = n->child;
1152         assert(MDOC_TEXT == n->type);
1153         if (NULL == (std_att = mdoc_a2att(n->string))) {
1154                 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1155                     n->line, n->pos, "At %s", n->string);
1156                 mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1157         } else
1158                 att = mandoc_strdup(std_att);
1159
1160         free(n->string);
1161         n->string = att;
1162 }
1163
1164 static void
1165 post_an(POST_ARGS)
1166 {
1167         struct mdoc_node *np;
1168
1169         np = mdoc->last;
1170         if (AUTH__NONE == np->norm->An.auth) {
1171                 if (0 == np->child)
1172                         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1173         } else if (np->child)
1174                 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1175 }
1176
1177 static void
1178 post_en(POST_ARGS)
1179 {
1180
1181         if (MDOC_BLOCK == mdoc->last->type)
1182                 mdoc->last->norm->Es = mdoc->last_es;
1183 }
1184
1185 static void
1186 post_es(POST_ARGS)
1187 {
1188
1189         mdoc->last_es = mdoc->last;
1190 }
1191
1192 static void
1193 post_it(POST_ARGS)
1194 {
1195         int               i, cols;
1196         enum mdoc_list    lt;
1197         struct mdoc_node *nbl, *nit, *nch;
1198
1199         nit = mdoc->last;
1200         if (MDOC_BLOCK != nit->type)
1201                 return;
1202
1203         nbl = nit->parent->parent;
1204         lt = nbl->norm->Bl.type;
1205
1206         switch (lt) {
1207         case LIST_tag:
1208                 /* FALLTHROUGH */
1209         case LIST_hang:
1210                 /* FALLTHROUGH */
1211         case LIST_ohang:
1212                 /* FALLTHROUGH */
1213         case LIST_inset:
1214                 /* FALLTHROUGH */
1215         case LIST_diag:
1216                 if (NULL == nit->head->child)
1217                         mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1218                             mdoc->parse, nit->line, nit->pos,
1219                             "Bl -%s It",
1220                             mdoc_argnames[nbl->args->argv[0].arg]);
1221                 break;
1222         case LIST_bullet:
1223                 /* FALLTHROUGH */
1224         case LIST_dash:
1225                 /* FALLTHROUGH */
1226         case LIST_enum:
1227                 /* FALLTHROUGH */
1228         case LIST_hyphen:
1229                 if (NULL == nit->body->child)
1230                         mandoc_vmsg(MANDOCERR_IT_NOBODY,
1231                             mdoc->parse, nit->line, nit->pos,
1232                             "Bl -%s It",
1233                             mdoc_argnames[nbl->args->argv[0].arg]);
1234                 /* FALLTHROUGH */
1235         case LIST_item:
1236                 if (NULL != nit->head->child)
1237                         mandoc_vmsg(MANDOCERR_ARG_SKIP,
1238                             mdoc->parse, nit->line, nit->pos,
1239                             "It %s", nit->head->child->string);
1240                 break;
1241         case LIST_column:
1242                 cols = (int)nbl->norm->Bl.ncols;
1243
1244                 assert(NULL == nit->head->child);
1245
1246                 for (i = 0, nch = nit->child; nch; nch = nch->next)
1247                         if (MDOC_BODY == nch->type)
1248                                 i++;
1249
1250                 if (i < cols || i > cols + 1)
1251                         mandoc_vmsg(MANDOCERR_ARGCOUNT,
1252                             mdoc->parse, nit->line, nit->pos,
1253                             "columns == %d (have %d)", cols, i);
1254                 break;
1255         default:
1256                 abort();
1257         }
1258 }
1259
1260 static void
1261 post_bl_block(POST_ARGS)
1262 {
1263         struct mdoc_node *n, *ni, *nc;
1264
1265         /*
1266          * These are fairly complicated, so we've broken them into two
1267          * functions.  post_bl_block_tag() is called when a -tag is
1268          * specified, but no -width (it must be guessed).  The second
1269          * when a -width is specified (macro indicators must be
1270          * rewritten into real lengths).
1271          */
1272
1273         n = mdoc->last;
1274
1275         if (LIST_tag == n->norm->Bl.type &&
1276             NULL == n->norm->Bl.width) {
1277                 post_bl_block_tag(mdoc);
1278                 assert(n->norm->Bl.width);
1279         }
1280
1281         for (ni = n->body->child; ni; ni = ni->next) {
1282                 if (NULL == ni->body)
1283                         continue;
1284                 nc = ni->body->last;
1285                 while (NULL != nc) {
1286                         switch (nc->tok) {
1287                         case MDOC_Pp:
1288                                 /* FALLTHROUGH */
1289                         case MDOC_Lp:
1290                                 /* FALLTHROUGH */
1291                         case MDOC_br:
1292                                 break;
1293                         default:
1294                                 nc = NULL;
1295                                 continue;
1296                         }
1297                         if (NULL == ni->next) {
1298                                 mandoc_msg(MANDOCERR_PAR_MOVE,
1299                                     mdoc->parse, nc->line, nc->pos,
1300                                     mdoc_macronames[nc->tok]);
1301                                 mdoc_node_relink(mdoc, nc);
1302                         } else if (0 == n->norm->Bl.comp &&
1303                             LIST_column != n->norm->Bl.type) {
1304                                 mandoc_vmsg(MANDOCERR_PAR_SKIP,
1305                                     mdoc->parse, nc->line, nc->pos,
1306                                     "%s before It",
1307                                     mdoc_macronames[nc->tok]);
1308                                 mdoc_node_delete(mdoc, nc);
1309                         } else
1310                                 break;
1311                         nc = ni->body->last;
1312                 }
1313         }
1314 }
1315
1316 /*
1317  * If the argument of -offset or -width is a macro,
1318  * replace it with the associated default width.
1319  */
1320 void
1321 rewrite_macro2len(char **arg)
1322 {
1323         size_t            width;
1324         enum mdoct        tok;
1325
1326         if (*arg == NULL)
1327                 return;
1328         else if ( ! strcmp(*arg, "Ds"))
1329                 width = 6;
1330         else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
1331                 return;
1332         else
1333                 width = macro2len(tok);
1334
1335         free(*arg);
1336         mandoc_asprintf(arg, "%zun", width);
1337 }
1338
1339 static void
1340 post_bl_block_tag(POST_ARGS)
1341 {
1342         struct mdoc_node *n, *nn;
1343         size_t            sz, ssz;
1344         int               i;
1345         char              buf[24];
1346
1347         /*
1348          * Calculate the -width for a `Bl -tag' list if it hasn't been
1349          * provided.  Uses the first head macro.  NOTE AGAIN: this is
1350          * ONLY if the -width argument has NOT been provided.  See
1351          * rewrite_macro2len() for converting the -width string.
1352          */
1353
1354         sz = 10;
1355         n = mdoc->last;
1356
1357         for (nn = n->body->child; nn; nn = nn->next) {
1358                 if (MDOC_It != nn->tok)
1359                         continue;
1360
1361                 assert(MDOC_BLOCK == nn->type);
1362                 nn = nn->head->child;
1363
1364                 if (nn == NULL)
1365                         break;
1366
1367                 if (MDOC_TEXT == nn->type) {
1368                         sz = strlen(nn->string) + 1;
1369                         break;
1370                 }
1371
1372                 if (0 != (ssz = macro2len(nn->tok)))
1373                         sz = ssz;
1374
1375                 break;
1376         }
1377
1378         /* Defaults to ten ens. */
1379
1380         (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1381
1382         /*
1383          * We have to dynamically add this to the macro's argument list.
1384          * We're guaranteed that a MDOC_Width doesn't already exist.
1385          */
1386
1387         assert(n->args);
1388         i = (int)(n->args->argc)++;
1389
1390         n->args->argv = mandoc_reallocarray(n->args->argv,
1391             n->args->argc, sizeof(struct mdoc_argv));
1392
1393         n->args->argv[i].arg = MDOC_Width;
1394         n->args->argv[i].line = n->line;
1395         n->args->argv[i].pos = n->pos;
1396         n->args->argv[i].sz = 1;
1397         n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1398         n->args->argv[i].value[0] = mandoc_strdup(buf);
1399
1400         /* Set our width! */
1401         n->norm->Bl.width = n->args->argv[i].value[0];
1402 }
1403
1404 static void
1405 post_bl_head(POST_ARGS)
1406 {
1407         struct mdoc_node *np, *nn, *nnp;
1408         struct mdoc_argv *argv;
1409         int               i, j;
1410
1411         if (LIST_column != mdoc->last->norm->Bl.type) {
1412                 /* FIXME: this should be ERROR class... */
1413                 hwarn_eq0(mdoc);
1414                 return;
1415         }
1416
1417         /*
1418          * Append old-style lists, where the column width specifiers
1419          * trail as macro parameters, to the new-style ("normal-form")
1420          * lists where they're argument values following -column.
1421          */
1422
1423         if (mdoc->last->child == NULL)
1424                 return;
1425
1426         np = mdoc->last->parent;
1427         assert(np->args);
1428
1429         for (j = 0; j < (int)np->args->argc; j++)
1430                 if (MDOC_Column == np->args->argv[j].arg)
1431                         break;
1432
1433         assert(j < (int)np->args->argc);
1434
1435         /*
1436          * Accommodate for new-style groff column syntax.  Shuffle the
1437          * child nodes, all of which must be TEXT, as arguments for the
1438          * column field.  Then, delete the head children.
1439          */
1440
1441         argv = np->args->argv + j;
1442         i = argv->sz;
1443         argv->sz += mdoc->last->nchild;
1444         argv->value = mandoc_reallocarray(argv->value,
1445             argv->sz, sizeof(char *));
1446
1447         mdoc->last->norm->Bl.ncols = argv->sz;
1448         mdoc->last->norm->Bl.cols = (void *)argv->value;
1449
1450         for (nn = mdoc->last->child; nn; i++) {
1451                 argv->value[i] = nn->string;
1452                 nn->string = NULL;
1453                 nnp = nn;
1454                 nn = nn->next;
1455                 mdoc_node_delete(NULL, nnp);
1456         }
1457
1458         mdoc->last->nchild = 0;
1459         mdoc->last->child = NULL;
1460 }
1461
1462 static void
1463 post_bl(POST_ARGS)
1464 {
1465         struct mdoc_node        *nparent, *nprev; /* of the Bl block */
1466         struct mdoc_node        *nblock, *nbody;  /* of the Bl */
1467         struct mdoc_node        *nchild, *nnext;  /* of the Bl body */
1468
1469         nbody = mdoc->last;
1470         switch (nbody->type) {
1471         case MDOC_BLOCK:
1472                 post_bl_block(mdoc);
1473                 return;
1474         case MDOC_HEAD:
1475                 post_bl_head(mdoc);
1476                 return;
1477         case MDOC_BODY:
1478                 break;
1479         default:
1480                 return;
1481         }
1482
1483         bwarn_ge1(mdoc);
1484
1485         nchild = nbody->child;
1486         while (NULL != nchild) {
1487                 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1488                         nchild = nchild->next;
1489                         continue;
1490                 }
1491
1492                 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1493                     nchild->line, nchild->pos,
1494                     mdoc_macronames[nchild->tok]);
1495
1496                 /*
1497                  * Move the node out of the Bl block.
1498                  * First, collect all required node pointers.
1499                  */
1500
1501                 nblock  = nbody->parent;
1502                 nprev   = nblock->prev;
1503                 nparent = nblock->parent;
1504                 nnext   = nchild->next;
1505
1506                 /*
1507                  * Unlink this child.
1508                  */
1509
1510                 assert(NULL == nchild->prev);
1511                 if (0 == --nbody->nchild) {
1512                         nbody->child = NULL;
1513                         nbody->last  = NULL;
1514                         assert(NULL == nnext);
1515                 } else {
1516                         nbody->child = nnext;
1517                         nnext->prev = NULL;
1518                 }
1519
1520                 /*
1521                  * Relink this child.
1522                  */
1523
1524                 nchild->parent = nparent;
1525                 nchild->prev   = nprev;
1526                 nchild->next   = nblock;
1527
1528                 nblock->prev = nchild;
1529                 nparent->nchild++;
1530                 if (NULL == nprev)
1531                         nparent->child = nchild;
1532                 else
1533                         nprev->next = nchild;
1534
1535                 nchild = nnext;
1536         }
1537 }
1538
1539 static void
1540 post_bk(POST_ARGS)
1541 {
1542
1543         hwarn_eq0(mdoc);
1544         bwarn_ge1(mdoc);
1545 }
1546
1547 static void
1548 post_sm(struct mdoc *mdoc)
1549 {
1550         struct mdoc_node        *nch;
1551
1552         nch = mdoc->last->child;
1553
1554         if (nch == NULL) {
1555                 mdoc->flags ^= MDOC_SMOFF;
1556                 return;
1557         }
1558
1559         assert(nch->type == MDOC_TEXT);
1560
1561         if ( ! strcmp(nch->string, "on")) {
1562                 mdoc->flags &= ~MDOC_SMOFF;
1563                 return;
1564         }
1565         if ( ! strcmp(nch->string, "off")) {
1566                 mdoc->flags |= MDOC_SMOFF;
1567                 return;
1568         }
1569
1570         mandoc_vmsg(MANDOCERR_SM_BAD,
1571             mdoc->parse, nch->line, nch->pos,
1572             "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1573         mdoc_node_relink(mdoc, nch);
1574         return;
1575 }
1576
1577 static void
1578 post_root(POST_ARGS)
1579 {
1580         struct mdoc_node *n;
1581
1582         /* Add missing prologue data. */
1583
1584         if (mdoc->meta.date == NULL)
1585                 mdoc->meta.date = mdoc->quick ?
1586                     mandoc_strdup("") :
1587                     mandoc_normdate(mdoc->parse, NULL, 0, 0);
1588
1589         if (mdoc->meta.title == NULL) {
1590                 mandoc_msg(MANDOCERR_DT_NOTITLE,
1591                     mdoc->parse, 0, 0, "EOF");
1592                 mdoc->meta.title = mandoc_strdup("UNTITLED");
1593         }
1594
1595         if (mdoc->meta.vol == NULL)
1596                 mdoc->meta.vol = mandoc_strdup("LOCAL");
1597
1598         if (mdoc->meta.os == NULL) {
1599                 mandoc_msg(MANDOCERR_OS_MISSING,
1600                     mdoc->parse, 0, 0, NULL);
1601                 mdoc->meta.os = mandoc_strdup("");
1602         }
1603
1604         /* Check that we begin with a proper `Sh'. */
1605
1606         n = mdoc->first->child;
1607         while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1608                 n = n->next;
1609
1610         if (n == NULL)
1611                 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1612         else if (n->tok != MDOC_Sh)
1613                 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1614                     n->line, n->pos, mdoc_macronames[n->tok]);
1615 }
1616
1617 static void
1618 post_st(POST_ARGS)
1619 {
1620         struct mdoc_node         *n, *nch;
1621         const char               *p;
1622
1623         n = mdoc->last;
1624         nch = n->child;
1625
1626         if (NULL == nch) {
1627                 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1628                     n->line, n->pos, mdoc_macronames[n->tok]);
1629                 mdoc_node_delete(mdoc, n);
1630                 return;
1631         }
1632
1633         assert(MDOC_TEXT == nch->type);
1634
1635         if (NULL == (p = mdoc_a2st(nch->string))) {
1636                 mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1637                     nch->line, nch->pos, "St %s", nch->string);
1638                 mdoc_node_delete(mdoc, n);
1639         } else {
1640                 free(nch->string);
1641                 nch->string = mandoc_strdup(p);
1642         }
1643 }
1644
1645 static void
1646 post_rs(POST_ARGS)
1647 {
1648         struct mdoc_node *nn, *next, *prev;
1649         int               i, j;
1650
1651         switch (mdoc->last->type) {
1652         case MDOC_HEAD:
1653                 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1654                 return;
1655         case MDOC_BODY:
1656                 if (mdoc->last->child)
1657                         break;
1658                 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1659                 return;
1660         default:
1661                 return;
1662         }
1663
1664         /*
1665          * The full `Rs' block needs special handling to order the
1666          * sub-elements according to `rsord'.  Pick through each element
1667          * and correctly order it.  This is an insertion sort.
1668          */
1669
1670         next = NULL;
1671         for (nn = mdoc->last->child->next; nn; nn = next) {
1672                 /* Determine order of `nn'. */
1673                 for (i = 0; i < RSORD_MAX; i++)
1674                         if (rsord[i] == nn->tok)
1675                                 break;
1676
1677                 if (i == RSORD_MAX) {
1678                         mandoc_msg(MANDOCERR_RS_BAD,
1679                             mdoc->parse, nn->line, nn->pos,
1680                             mdoc_macronames[nn->tok]);
1681                         i = -1;
1682                 } else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1683                         mdoc->last->norm->Rs.quote_T++;
1684
1685                 /*
1686                  * Remove `nn' from the chain.  This somewhat
1687                  * repeats mdoc_node_unlink(), but since we're
1688                  * just re-ordering, there's no need for the
1689                  * full unlink process.
1690                  */
1691
1692                 if (NULL != (next = nn->next))
1693                         next->prev = nn->prev;
1694
1695                 if (NULL != (prev = nn->prev))
1696                         prev->next = nn->next;
1697
1698                 nn->prev = nn->next = NULL;
1699
1700                 /*
1701                  * Scan back until we reach a node that's
1702                  * ordered before `nn'.
1703                  */
1704
1705                 for ( ; prev ; prev = prev->prev) {
1706                         /* Determine order of `prev'. */
1707                         for (j = 0; j < RSORD_MAX; j++)
1708                                 if (rsord[j] == prev->tok)
1709                                         break;
1710                         if (j == RSORD_MAX)
1711                                 j = -1;
1712
1713                         if (j <= i)
1714                                 break;
1715                 }
1716
1717                 /*
1718                  * Set `nn' back into its correct place in front
1719                  * of the `prev' node.
1720                  */
1721
1722                 nn->prev = prev;
1723
1724                 if (prev) {
1725                         if (prev->next)
1726                                 prev->next->prev = nn;
1727                         nn->next = prev->next;
1728                         prev->next = nn;
1729                 } else {
1730                         mdoc->last->child->prev = nn;
1731                         nn->next = mdoc->last->child;
1732                         mdoc->last->child = nn;
1733                 }
1734         }
1735 }
1736
1737 /*
1738  * For some arguments of some macros,
1739  * convert all breakable hyphens into ASCII_HYPH.
1740  */
1741 static void
1742 post_hyph(POST_ARGS)
1743 {
1744         struct mdoc_node        *n, *nch;
1745         char                    *cp;
1746
1747         n = mdoc->last;
1748         switch (n->type) {
1749         case MDOC_HEAD:
1750                 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1751                         break;
1752                 return;
1753         case MDOC_BODY:
1754                 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1755                         break;
1756                 return;
1757         case MDOC_ELEM:
1758                 break;
1759         default:
1760                 return;
1761         }
1762
1763         for (nch = n->child; nch; nch = nch->next) {
1764                 if (MDOC_TEXT != nch->type)
1765                         continue;
1766                 cp = nch->string;
1767                 if ('\0' == *cp)
1768                         continue;
1769                 while ('\0' != *(++cp))
1770                         if ('-' == *cp &&
1771                             isalpha((unsigned char)cp[-1]) &&
1772                             isalpha((unsigned char)cp[1]))
1773                                 *cp = ASCII_HYPH;
1774         }
1775 }
1776
1777 static void
1778 post_hyphtext(POST_ARGS)
1779 {
1780
1781         ewarn_ge1(mdoc);
1782         post_hyph(mdoc);
1783 }
1784
1785 static void
1786 post_ns(POST_ARGS)
1787 {
1788
1789         if (MDOC_LINE & mdoc->last->flags)
1790                 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1791                     mdoc->last->line, mdoc->last->pos, NULL);
1792 }
1793
1794 static void
1795 post_sh(POST_ARGS)
1796 {
1797
1798         post_ignpar(mdoc);
1799
1800         switch (mdoc->last->type) {
1801         case MDOC_HEAD:
1802                 post_sh_head(mdoc);
1803                 break;
1804         case MDOC_BODY:
1805                 switch (mdoc->lastsec)  {
1806                 case SEC_NAME:
1807                         post_sh_name(mdoc);
1808                         break;
1809                 case SEC_SEE_ALSO:
1810                         post_sh_see_also(mdoc);
1811                         break;
1812                 case SEC_AUTHORS:
1813                         post_sh_authors(mdoc);
1814                         break;
1815                 default:
1816                         break;
1817                 }
1818                 break;
1819         default:
1820                 break;
1821         }
1822 }
1823
1824 static void
1825 post_sh_name(POST_ARGS)
1826 {
1827         struct mdoc_node *n;
1828
1829         /*
1830          * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1831          * macros (can have multiple `Nm' and one `Nd').  Note that the
1832          * children of the BODY declaration can also be "text".
1833          */
1834
1835         if (NULL == (n = mdoc->last->child)) {
1836                 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1837                     mdoc->last->line, mdoc->last->pos, "empty");
1838                 return;
1839         }
1840
1841         for ( ; n && n->next; n = n->next) {
1842                 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1843                         continue;
1844                 if (MDOC_TEXT == n->type)
1845                         continue;
1846                 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1847                     n->line, n->pos, mdoc_macronames[n->tok]);
1848         }
1849
1850         assert(n);
1851         if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1852                 return;
1853
1854         mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1855             n->line, n->pos, mdoc_macronames[n->tok]);
1856 }
1857
1858 static void
1859 post_sh_see_also(POST_ARGS)
1860 {
1861         const struct mdoc_node  *n;
1862         const char              *name, *sec;
1863         const char              *lastname, *lastsec, *lastpunct;
1864         int                      cmp;
1865
1866         n = mdoc->last->child;
1867         lastname = lastsec = lastpunct = NULL;
1868         while (n != NULL) {
1869                 if (n->tok != MDOC_Xr || n->nchild < 2)
1870                         break;
1871
1872                 /* Process one .Xr node. */
1873
1874                 name = n->child->string;
1875                 sec = n->child->next->string;
1876                 if (lastsec != NULL) {
1877                         if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1878                                 mandoc_vmsg(MANDOCERR_XR_PUNCT,
1879                                     mdoc->parse, n->line, n->pos,
1880                                     "%s before %s(%s)", lastpunct,
1881                                     name, sec);
1882                         cmp = strcmp(lastsec, sec);
1883                         if (cmp > 0)
1884                                 mandoc_vmsg(MANDOCERR_XR_ORDER,
1885                                     mdoc->parse, n->line, n->pos,
1886                                     "%s(%s) after %s(%s)", name,
1887                                     sec, lastname, lastsec);
1888                         else if (cmp == 0 &&
1889                             strcasecmp(lastname, name) > 0)
1890                                 mandoc_vmsg(MANDOCERR_XR_ORDER,
1891                                     mdoc->parse, n->line, n->pos,
1892                                     "%s after %s", name, lastname);
1893                 }
1894                 lastname = name;
1895                 lastsec = sec;
1896
1897                 /* Process the following node. */
1898
1899                 n = n->next;
1900                 if (n == NULL)
1901                         break;
1902                 if (n->tok == MDOC_Xr) {
1903                         lastpunct = "none";
1904                         continue;
1905                 }
1906                 if (n->type != MDOC_TEXT)
1907                         break;
1908                 for (name = n->string; *name != '\0'; name++)
1909                         if (isalpha((const unsigned char)*name))
1910                                 return;
1911                 lastpunct = n->string;
1912                 if (n->next == NULL)
1913                         mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1914                             n->line, n->pos, "%s after %s(%s)",
1915                             lastpunct, lastname, lastsec);
1916                 n = n->next;
1917         }
1918 }
1919
1920 static int
1921 child_an(const struct mdoc_node *n)
1922 {
1923
1924         for (n = n->child; n != NULL; n = n->next)
1925                 if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1926                         return(1);
1927         return(0);
1928 }
1929
1930 static void
1931 post_sh_authors(POST_ARGS)
1932 {
1933
1934         if ( ! child_an(mdoc->last))
1935                 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1936                     mdoc->last->line, mdoc->last->pos, NULL);
1937 }
1938
1939 static void
1940 post_sh_head(POST_ARGS)
1941 {
1942         struct mdoc_node *n;
1943         const char      *goodsec;
1944         char            *secname;
1945         enum mdoc_sec    sec;
1946
1947         /*
1948          * Process a new section.  Sections are either "named" or
1949          * "custom".  Custom sections are user-defined, while named ones
1950          * follow a conventional order and may only appear in certain
1951          * manual sections.
1952          */
1953
1954         secname = NULL;
1955         sec = SEC_CUSTOM;
1956         mdoc_deroff(&secname, mdoc->last);
1957         sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1958
1959         /* The NAME should be first. */
1960
1961         if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1962                 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1963                     mdoc->last->line, mdoc->last->pos,
1964                     "Sh %s", secname);
1965
1966         /* The SYNOPSIS gets special attention in other areas. */
1967
1968         if (SEC_SYNOPSIS == sec) {
1969                 roff_setreg(mdoc->roff, "nS", 1, '=');
1970                 mdoc->flags |= MDOC_SYNOPSIS;
1971         } else {
1972                 roff_setreg(mdoc->roff, "nS", 0, '=');
1973                 mdoc->flags &= ~MDOC_SYNOPSIS;
1974         }
1975
1976         /* Mark our last section. */
1977
1978         mdoc->lastsec = sec;
1979
1980         /*
1981          * Set the section attribute for the current HEAD, for its
1982          * parent BLOCK, and for the HEAD children; the latter can
1983          * only be TEXT nodes, so no recursion is needed.
1984          * For other blocks and elements, including .Sh BODY, this is
1985          * done when allocating the node data structures, but for .Sh
1986          * BLOCK and HEAD, the section is still unknown at that time.
1987          */
1988
1989         mdoc->last->parent->sec = sec;
1990         mdoc->last->sec = sec;
1991         for (n = mdoc->last->child; n; n = n->next)
1992                 n->sec = sec;
1993
1994         /* We don't care about custom sections after this. */
1995
1996         if (SEC_CUSTOM == sec) {
1997                 free(secname);
1998                 return;
1999         }
2000
2001         /*
2002          * Check whether our non-custom section is being repeated or is
2003          * out of order.
2004          */
2005
2006         if (sec == mdoc->lastnamed)
2007                 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2008                     mdoc->last->line, mdoc->last->pos,
2009                     "Sh %s", secname);
2010
2011         if (sec < mdoc->lastnamed)
2012                 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2013                     mdoc->last->line, mdoc->last->pos,
2014                     "Sh %s", secname);
2015
2016         /* Mark the last named section. */
2017
2018         mdoc->lastnamed = sec;
2019
2020         /* Check particular section/manual conventions. */
2021
2022         if (mdoc->meta.msec == NULL) {
2023                 free(secname);
2024                 return;
2025         }
2026
2027         goodsec = NULL;
2028         switch (sec) {
2029         case SEC_ERRORS:
2030                 if (*mdoc->meta.msec == '4')
2031                         break;
2032                 goodsec = "2, 3, 4, 9";
2033                 /* FALLTHROUGH */
2034         case SEC_RETURN_VALUES:
2035                 /* FALLTHROUGH */
2036         case SEC_LIBRARY:
2037                 if (*mdoc->meta.msec == '2')
2038                         break;
2039                 if (*mdoc->meta.msec == '3')
2040                         break;
2041                 if (NULL == goodsec)
2042                         goodsec = "2, 3, 9";
2043                 /* FALLTHROUGH */
2044         case SEC_CONTEXT:
2045                 if (*mdoc->meta.msec == '9')
2046                         break;
2047                 if (NULL == goodsec)
2048                         goodsec = "9";
2049                 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2050                     mdoc->last->line, mdoc->last->pos,
2051                     "Sh %s for %s only", secname, goodsec);
2052                 break;
2053         default:
2054                 break;
2055         }
2056         free(secname);
2057 }
2058
2059 static void
2060 post_ignpar(POST_ARGS)
2061 {
2062         struct mdoc_node *np;
2063
2064         check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0);
2065         post_hyph(mdoc);
2066
2067         if (MDOC_BODY != mdoc->last->type)
2068                 return;
2069
2070         if (NULL != (np = mdoc->last->child))
2071                 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2072                         mandoc_vmsg(MANDOCERR_PAR_SKIP,
2073                             mdoc->parse, np->line, np->pos,
2074                             "%s after %s", mdoc_macronames[np->tok],
2075                             mdoc_macronames[mdoc->last->tok]);
2076                         mdoc_node_delete(mdoc, np);
2077                 }
2078
2079         if (NULL != (np = mdoc->last->last))
2080                 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2081                         mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2082                             np->line, np->pos, "%s at the end of %s",
2083                             mdoc_macronames[np->tok],
2084                             mdoc_macronames[mdoc->last->tok]);
2085                         mdoc_node_delete(mdoc, np);
2086                 }
2087 }
2088
2089 static void
2090 pre_par(PRE_ARGS)
2091 {
2092
2093         if (NULL == mdoc->last)
2094                 return;
2095         if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2096                 return;
2097
2098         /*
2099          * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2100          * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2101          */
2102
2103         if (MDOC_Pp != mdoc->last->tok &&
2104             MDOC_Lp != mdoc->last->tok &&
2105             MDOC_br != mdoc->last->tok)
2106                 return;
2107         if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2108                 return;
2109         if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2110                 return;
2111         if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2112                 return;
2113
2114         mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2115             mdoc->last->line, mdoc->last->pos,
2116             "%s before %s", mdoc_macronames[mdoc->last->tok],
2117             mdoc_macronames[n->tok]);
2118         mdoc_node_delete(mdoc, mdoc->last);
2119 }
2120
2121 static void
2122 post_par(POST_ARGS)
2123 {
2124         struct mdoc_node *np;
2125
2126         if (mdoc->last->tok == MDOC_sp)
2127                 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
2128         else
2129                 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
2130
2131         if (MDOC_ELEM != mdoc->last->type &&
2132             MDOC_BLOCK != mdoc->last->type)
2133                 return;
2134
2135         if (NULL == (np = mdoc->last->prev)) {
2136                 np = mdoc->last->parent;
2137                 if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2138                         return;
2139         } else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2140             (MDOC_br != mdoc->last->tok ||
2141              (MDOC_sp != np->tok && MDOC_br != np->tok)))
2142                 return;
2143
2144         mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2145             mdoc->last->line, mdoc->last->pos,
2146             "%s after %s", mdoc_macronames[mdoc->last->tok],
2147             mdoc_macronames[np->tok]);
2148         mdoc_node_delete(mdoc, mdoc->last);
2149 }
2150
2151 static void
2152 pre_literal(PRE_ARGS)
2153 {
2154
2155         pre_display(mdoc, n);
2156
2157         if (MDOC_BODY != n->type)
2158                 return;
2159
2160         /*
2161          * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2162          * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2163          */
2164
2165         switch (n->tok) {
2166         case MDOC_Dl:
2167                 mdoc->flags |= MDOC_LITERAL;
2168                 break;
2169         case MDOC_Bd:
2170                 if (DISP_literal == n->norm->Bd.type)
2171                         mdoc->flags |= MDOC_LITERAL;
2172                 if (DISP_unfilled == n->norm->Bd.type)
2173                         mdoc->flags |= MDOC_LITERAL;
2174                 break;
2175         default:
2176                 abort();
2177                 /* NOTREACHED */
2178         }
2179 }
2180
2181 static void
2182 post_dd(POST_ARGS)
2183 {
2184         struct mdoc_node *n;
2185         char             *datestr;
2186
2187         if (mdoc->meta.date)
2188                 free(mdoc->meta.date);
2189
2190         n = mdoc->last;
2191         if (NULL == n->child || '\0' == n->child->string[0]) {
2192                 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2193                     mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2194                 goto out;
2195         }
2196
2197         datestr = NULL;
2198         mdoc_deroff(&datestr, n);
2199         if (mdoc->quick)
2200                 mdoc->meta.date = datestr;
2201         else {
2202                 mdoc->meta.date = mandoc_normdate(mdoc->parse,
2203                     datestr, n->line, n->pos);
2204                 free(datestr);
2205         }
2206 out:
2207         mdoc_node_delete(mdoc, n);
2208 }
2209
2210 static void
2211 post_dt(POST_ARGS)
2212 {
2213         struct mdoc_node *nn, *n;
2214         const char       *cp;
2215         char             *p;
2216
2217         n = mdoc->last;
2218
2219         free(mdoc->meta.title);
2220         free(mdoc->meta.msec);
2221         free(mdoc->meta.vol);
2222         free(mdoc->meta.arch);
2223
2224         mdoc->meta.title = NULL;
2225         mdoc->meta.msec = NULL;
2226         mdoc->meta.vol = NULL;
2227         mdoc->meta.arch = NULL;
2228
2229         /* First check that all characters are uppercase. */
2230
2231         if (NULL != (nn = n->child))
2232                 for (p = nn->string; *p; p++) {
2233                         if (toupper((unsigned char)*p) == *p)
2234                                 continue;
2235                         mandoc_vmsg(MANDOCERR_TITLE_CASE,
2236                             mdoc->parse, nn->line,
2237                             nn->pos + (p - nn->string),
2238                             "Dt %s", nn->string);
2239                         break;
2240                 }
2241
2242         /* No argument: msec and arch remain NULL. */
2243
2244         if (NULL == (nn = n->child)) {
2245                 mandoc_msg(MANDOCERR_DT_NOTITLE,
2246                     mdoc->parse, n->line, n->pos, "Dt");
2247                 mdoc->meta.title = mandoc_strdup("UNTITLED");
2248                 mdoc->meta.vol = mandoc_strdup("LOCAL");
2249                 goto out;
2250         }
2251
2252         /* One argument: msec and arch remain NULL. */
2253
2254         mdoc->meta.title = mandoc_strdup(
2255             '\0' == nn->string[0] ? "UNTITLED" : nn->string);
2256
2257         if (NULL == (nn = nn->next)) {
2258                 mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2259                     mdoc->parse, n->line, n->pos,
2260                     "Dt %s", mdoc->meta.title);
2261                 mdoc->meta.vol = mandoc_strdup("LOCAL");
2262                 goto out;
2263         }
2264
2265         /* Handles: `.Dt TITLE SEC'
2266          * title = TITLE,
2267          * volume = SEC is msec ? format(msec) : SEC,
2268          * msec = SEC is msec ? atoi(msec) : 0,
2269          * arch = NULL
2270          */
2271
2272         cp = mandoc_a2msec(nn->string);
2273         if (cp) {
2274                 mdoc->meta.vol = mandoc_strdup(cp);
2275                 mdoc->meta.msec = mandoc_strdup(nn->string);
2276         } else {
2277                 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2278                     nn->line, nn->pos, "Dt ... %s", nn->string);
2279                 mdoc->meta.vol = mandoc_strdup(nn->string);
2280                 mdoc->meta.msec = mandoc_strdup(nn->string);
2281         }
2282
2283         /* Handle an optional architecture */
2284
2285         if ((nn = nn->next) != NULL) {
2286                 for (p = nn->string; *p; p++)
2287                         *p = tolower((unsigned char)*p);
2288                 mdoc->meta.arch = mandoc_strdup(nn->string);
2289         }
2290
2291         /* Ignore any subsequent parameters... */
2292         /* FIXME: warn about subsequent parameters. */
2293 out:
2294         mdoc_node_delete(mdoc, n);
2295 }
2296
2297 static void
2298 post_bx(POST_ARGS)
2299 {
2300         struct mdoc_node        *n;
2301
2302         /*
2303          * Make `Bx's second argument always start with an uppercase
2304          * letter.  Groff checks if it's an "accepted" term, but we just
2305          * uppercase blindly.
2306          */
2307
2308         n = mdoc->last->child;
2309         if (n && NULL != (n = n->next))
2310                 *n->string = (char)toupper((unsigned char)*n->string);
2311 }
2312
2313 static void
2314 post_os(POST_ARGS)
2315 {
2316 #ifndef OSNAME
2317         struct utsname    utsname;
2318         static char      *defbuf;
2319 #endif
2320         struct mdoc_node *n;
2321
2322         n = mdoc->last;
2323
2324         /*
2325          * Set the operating system by way of the `Os' macro.
2326          * The order of precedence is:
2327          * 1. the argument of the `Os' macro, unless empty
2328          * 2. the -Ios=foo command line argument, if provided
2329          * 3. -DOSNAME="\"foo\"", if provided during compilation
2330          * 4. "sysname release" from uname(3)
2331          */
2332
2333         free(mdoc->meta.os);
2334         mdoc->meta.os = NULL;
2335         mdoc_deroff(&mdoc->meta.os, n);
2336         if (mdoc->meta.os)
2337                 goto out;
2338
2339         if (mdoc->defos) {
2340                 mdoc->meta.os = mandoc_strdup(mdoc->defos);
2341                 goto out;
2342         }
2343
2344 #ifdef OSNAME
2345         mdoc->meta.os = mandoc_strdup(OSNAME);
2346 #else /*!OSNAME */
2347         if (NULL == defbuf) {
2348                 if (-1 == uname(&utsname)) {
2349                         mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2350                             n->line, n->pos, "Os");
2351                         defbuf = mandoc_strdup("UNKNOWN");
2352                 } else
2353                         mandoc_asprintf(&defbuf, "%s %s",
2354                             utsname.sysname, utsname.release);
2355         }
2356         mdoc->meta.os = mandoc_strdup(defbuf);
2357 #endif /*!OSNAME*/
2358
2359 out:
2360         mdoc_node_delete(mdoc, n);
2361 }
2362
2363 /*
2364  * If no argument is provided,
2365  * fill in the name of the current manual page.
2366  */
2367 static void
2368 post_ex(POST_ARGS)
2369 {
2370         struct mdoc_node *n;
2371
2372         n = mdoc->last;
2373
2374         if (n->child)
2375                 return;
2376
2377         if (mdoc->meta.name == NULL) {
2378                 mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2379                     n->line, n->pos, "Ex");
2380                 return;
2381         }
2382
2383         mdoc->next = MDOC_NEXT_CHILD;
2384         mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2385         mdoc->last = n;
2386 }
2387
2388 static enum mdoc_sec
2389 a2sec(const char *p)
2390 {
2391         int              i;
2392
2393         for (i = 0; i < (int)SEC__MAX; i++)
2394                 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2395                         return((enum mdoc_sec)i);
2396
2397         return(SEC_CUSTOM);
2398 }
2399
2400 static size_t
2401 macro2len(enum mdoct macro)
2402 {
2403
2404         switch (macro) {
2405         case MDOC_Ad:
2406                 return(12);
2407         case MDOC_Ao:
2408                 return(12);
2409         case MDOC_An:
2410                 return(12);
2411         case MDOC_Aq:
2412                 return(12);
2413         case MDOC_Ar:
2414                 return(12);
2415         case MDOC_Bo:
2416                 return(12);
2417         case MDOC_Bq:
2418                 return(12);
2419         case MDOC_Cd:
2420                 return(12);
2421         case MDOC_Cm:
2422                 return(10);
2423         case MDOC_Do:
2424                 return(10);
2425         case MDOC_Dq:
2426                 return(12);
2427         case MDOC_Dv:
2428                 return(12);
2429         case MDOC_Eo:
2430                 return(12);
2431         case MDOC_Em:
2432                 return(10);
2433         case MDOC_Er:
2434                 return(17);
2435         case MDOC_Ev:
2436                 return(15);
2437         case MDOC_Fa:
2438                 return(12);
2439         case MDOC_Fl:
2440                 return(10);
2441         case MDOC_Fo:
2442                 return(16);
2443         case MDOC_Fn:
2444                 return(16);
2445         case MDOC_Ic:
2446                 return(10);
2447         case MDOC_Li:
2448                 return(16);
2449         case MDOC_Ms:
2450                 return(6);
2451         case MDOC_Nm:
2452                 return(10);
2453         case MDOC_No:
2454                 return(12);
2455         case MDOC_Oo:
2456                 return(10);
2457         case MDOC_Op:
2458                 return(14);
2459         case MDOC_Pa:
2460                 return(32);
2461         case MDOC_Pf:
2462                 return(12);
2463         case MDOC_Po:
2464                 return(12);
2465         case MDOC_Pq:
2466                 return(12);
2467         case MDOC_Ql:
2468                 return(16);
2469         case MDOC_Qo:
2470                 return(12);
2471         case MDOC_So:
2472                 return(12);
2473         case MDOC_Sq:
2474                 return(12);
2475         case MDOC_Sy:
2476                 return(6);
2477         case MDOC_Sx:
2478                 return(16);
2479         case MDOC_Tn:
2480                 return(10);
2481         case MDOC_Va:
2482                 return(12);
2483         case MDOC_Vt:
2484                 return(12);
2485         case MDOC_Xr:
2486                 return(10);
2487         default:
2488                 break;
2489         };
2490         return(0);
2491 }