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