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