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