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