Merge branch 'vendor/DIFFUTILS'
[dragonfly.git] / contrib / mdocml / mdoc_validate.c
1 /*      $Id: mdoc_validate.c,v 1.166 2011/04/03 09:53:50 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010, 2011 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
76 static  int      concat(struct mdoc *, char *, 
77                         const struct mdoc_node *, size_t);
78 static  enum mdoc_sec   a2sec(const char *);
79 static  size_t          macro2len(enum mdoct);
80
81 static  int      ebool(POST_ARGS);
82 static  int      berr_ge1(POST_ARGS);
83 static  int      bwarn_ge1(POST_ARGS);
84 static  int      ewarn_eq0(POST_ARGS);
85 static  int      ewarn_eq1(POST_ARGS);
86 static  int      ewarn_ge1(POST_ARGS);
87 static  int      ewarn_le1(POST_ARGS);
88 static  int      hwarn_eq0(POST_ARGS);
89 static  int      hwarn_eq1(POST_ARGS);
90 static  int      hwarn_ge1(POST_ARGS);
91 static  int      hwarn_le1(POST_ARGS);
92
93 static  int      post_an(POST_ARGS);
94 static  int      post_at(POST_ARGS);
95 static  int      post_bf(POST_ARGS);
96 static  int      post_bl(POST_ARGS);
97 static  int      post_bl_block(POST_ARGS);
98 static  int      post_bl_block_width(POST_ARGS);
99 static  int      post_bl_block_tag(POST_ARGS);
100 static  int      post_bl_head(POST_ARGS);
101 static  int      post_bx(POST_ARGS);
102 static  int      post_dd(POST_ARGS);
103 static  int      post_dt(POST_ARGS);
104 static  int      post_defaults(POST_ARGS);
105 static  int      post_literal(POST_ARGS);
106 static  int      post_eoln(POST_ARGS);
107 static  int      post_it(POST_ARGS);
108 static  int      post_lb(POST_ARGS);
109 static  int      post_nm(POST_ARGS);
110 static  int      post_ns(POST_ARGS);
111 static  int      post_os(POST_ARGS);
112 static  int      post_ignpar(POST_ARGS);
113 static  int      post_prol(POST_ARGS);
114 static  int      post_root(POST_ARGS);
115 static  int      post_rs(POST_ARGS);
116 static  int      post_sh(POST_ARGS);
117 static  int      post_sh_body(POST_ARGS);
118 static  int      post_sh_head(POST_ARGS);
119 static  int      post_st(POST_ARGS);
120 static  int      post_std(POST_ARGS);
121 static  int      post_vt(POST_ARGS);
122 static  int      pre_an(PRE_ARGS);
123 static  int      pre_bd(PRE_ARGS);
124 static  int      pre_bl(PRE_ARGS);
125 static  int      pre_dd(PRE_ARGS);
126 static  int      pre_display(PRE_ARGS);
127 static  int      pre_dt(PRE_ARGS);
128 static  int      pre_it(PRE_ARGS);
129 static  int      pre_literal(PRE_ARGS);
130 static  int      pre_os(PRE_ARGS);
131 static  int      pre_par(PRE_ARGS);
132 static  int      pre_sh(PRE_ARGS);
133 static  int      pre_ss(PRE_ARGS);
134 static  int      pre_std(PRE_ARGS);
135
136 static  v_post   posts_an[] = { post_an, NULL };
137 static  v_post   posts_at[] = { post_at, post_defaults, NULL };
138 static  v_post   posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
139 static  v_post   posts_bf[] = { hwarn_le1, post_bf, NULL };
140 static  v_post   posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
141 static  v_post   posts_bl[] = { bwarn_ge1, post_bl, NULL };
142 static  v_post   posts_bx[] = { post_bx, NULL };
143 static  v_post   posts_bool[] = { ebool, NULL };
144 static  v_post   posts_eoln[] = { post_eoln, NULL };
145 static  v_post   posts_defaults[] = { post_defaults, NULL };
146 static  v_post   posts_dd[] = { post_dd, post_prol, NULL };
147 static  v_post   posts_dl[] = { post_literal, bwarn_ge1, NULL };
148 static  v_post   posts_dt[] = { post_dt, post_prol, NULL };
149 static  v_post   posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
150 static  v_post   posts_it[] = { post_it, NULL };
151 static  v_post   posts_lb[] = { post_lb, NULL };
152 static  v_post   posts_nd[] = { berr_ge1, NULL };
153 static  v_post   posts_nm[] = { post_nm, NULL };
154 static  v_post   posts_notext[] = { ewarn_eq0, NULL };
155 static  v_post   posts_ns[] = { post_ns, NULL };
156 static  v_post   posts_os[] = { post_os, post_prol, NULL };
157 static  v_post   posts_rs[] = { post_rs, NULL };
158 static  v_post   posts_sh[] = { post_ignpar, hwarn_ge1, bwarn_ge1, post_sh, NULL };
159 static  v_post   posts_sp[] = { ewarn_le1, NULL };
160 static  v_post   posts_ss[] = { post_ignpar, hwarn_ge1, bwarn_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_notext },              /* 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         { NULL, posts_notext },                 /* 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_notext },                 /* br */
303         { pres_pp, 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__P,
320         MDOC__Q,
321         MDOC__D,
322         MDOC__O,
323         MDOC__C,
324         MDOC__U
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 *m, 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 (m->last->type != type)
426                 return(1);
427         
428         switch (ineq) {
429         case (CHECK_LT):
430                 p = "less than ";
431                 if (m->last->nchild < val)
432                         return(1);
433                 break;
434         case (CHECK_GT):
435                 p = "more than ";
436                 if (m->last->nchild > val)
437                         return(1);
438                 break;
439         case (CHECK_EQ):
440                 p = "";
441                 if (val == m->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, m->parse, m->last->line, m->last->pos,
451                         "want %s%d children (have %d)",
452                         p, val, m->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 *m, 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(m, n, &n->args->argv[i]);
528 }
529
530 static void
531 check_argv(struct mdoc *m, 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(m, 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 || m->meta.name))
542                         mdoc_nmsg(m, n, MANDOCERR_NONAME);
543 }
544
545 static void
546 check_text(struct mdoc *m, int ln, int pos, char *p)
547 {
548         int              c;
549         size_t           sz;
550
551         for ( ; *p; p++, pos++) {
552                 sz = strcspn(p, "\t\\");
553                 p += (int)sz;
554
555                 if ('\0' == *p)
556                         break;
557
558                 pos += (int)sz;
559
560                 if ('\t' == *p) {
561                         if ( ! (MDOC_LITERAL & m->flags))
562                                 mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB);
563                         continue;
564                 }
565
566                 if (0 == (c = mandoc_special(p))) {
567                         mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
568                         continue;
569                 }
570
571                 p += c - 1;
572                 pos += c - 1;
573         }
574 }
575
576 static int
577 check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
578 {
579
580         assert(n->parent);
581         if ((MDOC_ROOT == t || tok == n->parent->tok) &&
582                         (t == n->parent->type))
583                 return(1);
584
585         mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line, 
586                         n->pos, "want parent %s", MDOC_ROOT == t ? 
587                         "<root>" : mdoc_macronames[tok]);
588         return(0);
589 }
590
591
592 static int
593 pre_display(PRE_ARGS)
594 {
595         struct mdoc_node *node;
596
597         if (MDOC_BLOCK != n->type)
598                 return(1);
599
600         for (node = mdoc->last->parent; node; node = node->parent) 
601                 if (MDOC_BLOCK == node->type)
602                         if (MDOC_Bd == node->tok)
603                                 break;
604
605         if (node)
606                 mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
607
608         return(1);
609 }
610
611
612 static int
613 pre_bl(PRE_ARGS)
614 {
615         int               i, comp, dup;
616         const char       *offs, *width;
617         enum mdoc_list    lt;
618         struct mdoc_node *np;
619
620         if (MDOC_BLOCK != n->type) {
621                 if (ENDBODY_NOT != n->end) {
622                         assert(n->pending);
623                         np = n->pending->parent;
624                 } else
625                         np = n->parent;
626
627                 assert(np);
628                 assert(MDOC_BLOCK == np->type);
629                 assert(MDOC_Bl == np->tok);
630                 return(1);
631         }
632
633         /* 
634          * First figure out which kind of list to use: bind ourselves to
635          * the first mentioned list type and warn about any remaining
636          * ones.  If we find no list type, we default to LIST_item.
637          */
638
639         /* LINTED */
640         for (i = 0; n->args && i < (int)n->args->argc; i++) {
641                 lt = LIST__NONE;
642                 dup = comp = 0;
643                 width = offs = NULL;
644                 switch (n->args->argv[i].arg) {
645                 /* Set list types. */
646                 case (MDOC_Bullet):
647                         lt = LIST_bullet;
648                         break;
649                 case (MDOC_Dash):
650                         lt = LIST_dash;
651                         break;
652                 case (MDOC_Enum):
653                         lt = LIST_enum;
654                         break;
655                 case (MDOC_Hyphen):
656                         lt = LIST_hyphen;
657                         break;
658                 case (MDOC_Item):
659                         lt = LIST_item;
660                         break;
661                 case (MDOC_Tag):
662                         lt = LIST_tag;
663                         break;
664                 case (MDOC_Diag):
665                         lt = LIST_diag;
666                         break;
667                 case (MDOC_Hang):
668                         lt = LIST_hang;
669                         break;
670                 case (MDOC_Ohang):
671                         lt = LIST_ohang;
672                         break;
673                 case (MDOC_Inset):
674                         lt = LIST_inset;
675                         break;
676                 case (MDOC_Column):
677                         lt = LIST_column;
678                         break;
679                 /* Set list arguments. */
680                 case (MDOC_Compact):
681                         dup = n->norm->Bl.comp;
682                         comp = 1;
683                         break;
684                 case (MDOC_Width):
685                         dup = (NULL != n->norm->Bl.width);
686                         width = n->args->argv[i].value[0];
687                         break;
688                 case (MDOC_Offset):
689                         /* NB: this can be empty! */
690                         if (n->args->argv[i].sz) {
691                                 offs = n->args->argv[i].value[0];
692                                 dup = (NULL != n->norm->Bl.offs);
693                                 break;
694                         }
695                         mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
696                         break;
697                 default:
698                         continue;
699                 }
700
701                 /* Check: duplicate auxiliary arguments. */
702
703                 if (dup)
704                         mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
705
706                 if (comp && ! dup)
707                         n->norm->Bl.comp = comp;
708                 if (offs && ! dup)
709                         n->norm->Bl.offs = offs;
710                 if (width && ! dup)
711                         n->norm->Bl.width = width;
712
713                 /* Check: multiple list types. */
714
715                 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
716                         mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
717
718                 /* Assign list type. */
719
720                 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
721                         n->norm->Bl.type = lt;
722                         /* Set column information, too. */
723                         if (LIST_column == lt) {
724                                 n->norm->Bl.ncols = 
725                                         n->args->argv[i].sz;
726                                 n->norm->Bl.cols = (const char **)
727                                         n->args->argv[i].value;
728                         }
729                 }
730
731                 /* The list type should come first. */
732
733                 if (n->norm->Bl.type == LIST__NONE)
734                         if (n->norm->Bl.width || 
735                                         n->norm->Bl.offs || 
736                                         n->norm->Bl.comp)
737                                 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
738
739                 continue;
740         }
741
742         /* Allow lists to default to LIST_item. */
743
744         if (LIST__NONE == n->norm->Bl.type) {
745                 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
746                 n->norm->Bl.type = LIST_item;
747         }
748
749         /* 
750          * Validate the width field.  Some list types don't need width
751          * types and should be warned about them.  Others should have it
752          * and must also be warned.
753          */
754
755         switch (n->norm->Bl.type) {
756         case (LIST_tag):
757                 if (n->norm->Bl.width)
758                         break;
759                 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
760                 break;
761         case (LIST_column):
762                 /* FALLTHROUGH */
763         case (LIST_diag):
764                 /* FALLTHROUGH */
765         case (LIST_ohang):
766                 /* FALLTHROUGH */
767         case (LIST_inset):
768                 /* FALLTHROUGH */
769         case (LIST_item):
770                 if (n->norm->Bl.width)
771                         mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
772                 break;
773         default:
774                 break;
775         }
776
777         return(1);
778 }
779
780
781 static int
782 pre_bd(PRE_ARGS)
783 {
784         int               i, dup, comp;
785         enum mdoc_disp    dt;
786         const char       *offs;
787         struct mdoc_node *np;
788
789         if (MDOC_BLOCK != n->type) {
790                 if (ENDBODY_NOT != n->end) {
791                         assert(n->pending);
792                         np = n->pending->parent;
793                 } else
794                         np = n->parent;
795
796                 assert(np);
797                 assert(MDOC_BLOCK == np->type);
798                 assert(MDOC_Bd == np->tok);
799                 return(1);
800         }
801
802         /* LINTED */
803         for (i = 0; n->args && i < (int)n->args->argc; i++) {
804                 dt = DISP__NONE;
805                 dup = comp = 0;
806                 offs = NULL;
807
808                 switch (n->args->argv[i].arg) {
809                 case (MDOC_Centred):
810                         dt = DISP_centred;
811                         break;
812                 case (MDOC_Ragged):
813                         dt = DISP_ragged;
814                         break;
815                 case (MDOC_Unfilled):
816                         dt = DISP_unfilled;
817                         break;
818                 case (MDOC_Filled):
819                         dt = DISP_filled;
820                         break;
821                 case (MDOC_Literal):
822                         dt = DISP_literal;
823                         break;
824                 case (MDOC_File):
825                         mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
826                         return(0);
827                 case (MDOC_Offset):
828                         /* NB: this can be empty! */
829                         if (n->args->argv[i].sz) {
830                                 offs = n->args->argv[i].value[0];
831                                 dup = (NULL != n->norm->Bd.offs);
832                                 break;
833                         }
834                         mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
835                         break;
836                 case (MDOC_Compact):
837                         comp = 1;
838                         dup = n->norm->Bd.comp;
839                         break;
840                 default:
841                         abort();
842                         /* NOTREACHED */
843                 }
844
845                 /* Check whether we have duplicates. */
846
847                 if (dup)
848                         mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
849
850                 /* Make our auxiliary assignments. */
851
852                 if (offs && ! dup)
853                         n->norm->Bd.offs = offs;
854                 if (comp && ! dup)
855                         n->norm->Bd.comp = comp;
856
857                 /* Check whether a type has already been assigned. */
858
859                 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
860                         mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
861
862                 /* Make our type assignment. */
863
864                 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
865                         n->norm->Bd.type = dt;
866         }
867
868         if (DISP__NONE == n->norm->Bd.type) {
869                 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
870                 n->norm->Bd.type = DISP_ragged;
871         }
872
873         return(1);
874 }
875
876
877 static int
878 pre_ss(PRE_ARGS)
879 {
880
881         if (MDOC_BLOCK != n->type)
882                 return(1);
883         return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
884 }
885
886
887 static int
888 pre_sh(PRE_ARGS)
889 {
890
891         if (MDOC_BLOCK != n->type)
892                 return(1);
893
894         mdoc->regs->regs[(int)REG_nS].set = 0;
895         return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
896 }
897
898
899 static int
900 pre_it(PRE_ARGS)
901 {
902
903         if (MDOC_BLOCK != n->type)
904                 return(1);
905
906         return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
907 }
908
909
910 static int
911 pre_an(PRE_ARGS)
912 {
913         int              i;
914
915         if (NULL == n->args)
916                 return(1);
917         
918         for (i = 1; i < (int)n->args->argc; i++)
919                 mdoc_pmsg(mdoc, n->args->argv[i].line, 
920                         n->args->argv[i].pos, MANDOCERR_IGNARGV);
921
922         if (MDOC_Split == n->args->argv[0].arg)
923                 n->norm->An.auth = AUTH_split;
924         else if (MDOC_Nosplit == n->args->argv[0].arg)
925                 n->norm->An.auth = AUTH_nosplit;
926         else
927                 abort();
928
929         return(1);
930 }
931
932 static int
933 pre_std(PRE_ARGS)
934 {
935
936         if (n->args && 1 == n->args->argc)
937                 if (MDOC_Std == n->args->argv[0].arg)
938                         return(1);
939
940         mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
941         return(1);
942 }
943
944 static int
945 pre_dt(PRE_ARGS)
946 {
947
948         if (NULL == mdoc->meta.date || mdoc->meta.os)
949                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
950
951         if (mdoc->meta.title)
952                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
953
954         return(1);
955 }
956
957 static int
958 pre_os(PRE_ARGS)
959 {
960
961         if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
962                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
963
964         if (mdoc->meta.os)
965                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
966
967         return(1);
968 }
969
970 static int
971 pre_dd(PRE_ARGS)
972 {
973
974         if (mdoc->meta.title || mdoc->meta.os)
975                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
976
977         if (mdoc->meta.date)
978                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
979
980         return(1);
981 }
982
983
984 static int
985 post_bf(POST_ARGS)
986 {
987         struct mdoc_node *np;
988         enum mdocargt     arg;
989
990         /*
991          * Unlike other data pointers, these are "housed" by the HEAD
992          * element, which contains the goods.
993          */
994
995         if (MDOC_HEAD != mdoc->last->type) {
996                 if (ENDBODY_NOT != mdoc->last->end) {
997                         assert(mdoc->last->pending);
998                         np = mdoc->last->pending->parent->head;
999                 } else if (MDOC_BLOCK != mdoc->last->type) {
1000                         np = mdoc->last->parent->head;
1001                 } else 
1002                         np = mdoc->last->head;
1003
1004                 assert(np);
1005                 assert(MDOC_HEAD == np->type);
1006                 assert(MDOC_Bf == np->tok);
1007                 return(1);
1008         }
1009
1010         np = mdoc->last;
1011         assert(MDOC_BLOCK == np->parent->type);
1012         assert(MDOC_Bf == np->parent->tok);
1013
1014         /* 
1015          * Cannot have both argument and parameter.
1016          * If neither is specified, let it through with a warning. 
1017          */
1018
1019         if (np->parent->args && np->child) {
1020                 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1021                 return(0);
1022         } else if (NULL == np->parent->args && NULL == np->child) {
1023                 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1024                 return(1);
1025         }
1026
1027         /* Extract argument into data. */
1028         
1029         if (np->parent->args) {
1030                 arg = np->parent->args->argv[0].arg;
1031                 if (MDOC_Emphasis == arg)
1032                         np->norm->Bf.font = FONT_Em;
1033                 else if (MDOC_Literal == arg)
1034                         np->norm->Bf.font = FONT_Li;
1035                 else if (MDOC_Symbolic == arg)
1036                         np->norm->Bf.font = FONT_Sy;
1037                 else
1038                         abort();
1039                 return(1);
1040         }
1041
1042         /* Extract parameter into data. */
1043
1044         if (0 == strcmp(np->child->string, "Em"))
1045                 np->norm->Bf.font = FONT_Em;
1046         else if (0 == strcmp(np->child->string, "Li"))
1047                 np->norm->Bf.font = FONT_Li;
1048         else if (0 == strcmp(np->child->string, "Sy"))
1049                 np->norm->Bf.font = FONT_Sy;
1050         else 
1051                 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1052
1053         return(1);
1054 }
1055
1056 static int
1057 post_lb(POST_ARGS)
1058 {
1059         const char      *p;
1060         char            *buf;
1061         size_t           sz;
1062
1063         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1064
1065         assert(mdoc->last->child);
1066         assert(MDOC_TEXT == mdoc->last->child->type);
1067
1068         p = mdoc_a2lib(mdoc->last->child->string);
1069
1070         /* If lookup ok, replace with table value. */
1071
1072         if (p) {
1073                 free(mdoc->last->child->string);
1074                 mdoc->last->child->string = mandoc_strdup(p);
1075                 return(1);
1076         }
1077
1078         /* If not, use "library ``xxxx''. */
1079
1080         sz = strlen(mdoc->last->child->string) +
1081                 2 + strlen("\\(lqlibrary\\(rq");
1082         buf = mandoc_malloc(sz);
1083         snprintf(buf, sz, "library \\(lq%s\\(rq", 
1084                         mdoc->last->child->string);
1085         free(mdoc->last->child->string);
1086         mdoc->last->child->string = buf;
1087         return(1);
1088 }
1089
1090 static int
1091 post_eoln(POST_ARGS)
1092 {
1093
1094         if (mdoc->last->child)
1095                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1096         return(1);
1097 }
1098
1099
1100 static int
1101 post_vt(POST_ARGS)
1102 {
1103         const struct mdoc_node *n;
1104
1105         /*
1106          * The Vt macro comes in both ELEM and BLOCK form, both of which
1107          * have different syntaxes (yet more context-sensitive
1108          * behaviour).  ELEM types must have a child, which is already
1109          * guaranteed by the in_line parsing routine; BLOCK types,
1110          * specifically the BODY, should only have TEXT children.
1111          */
1112
1113         if (MDOC_BODY != mdoc->last->type)
1114                 return(1);
1115         
1116         for (n = mdoc->last->child; n; n = n->next)
1117                 if (MDOC_TEXT != n->type) 
1118                         mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1119
1120         return(1);
1121 }
1122
1123
1124 static int
1125 post_nm(POST_ARGS)
1126 {
1127         char             buf[BUFSIZ];
1128
1129         /* If no child specified, make sure we have the meta name. */
1130
1131         if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1132                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1133                 return(1);
1134         } else if (mdoc->meta.name)
1135                 return(1);
1136
1137         /* If no meta name, set it from the child. */
1138
1139         if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1140                 return(0);
1141
1142         mdoc->meta.name = mandoc_strdup(buf);
1143
1144         return(1);
1145 }
1146
1147 static int
1148 post_literal(POST_ARGS)
1149 {
1150         
1151         /*
1152          * The `Dl' (note "el" not "one") and `Bd' macros unset the
1153          * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1154          * this in literal mode, but it doesn't hurt to just switch it
1155          * off in general since displays can't be nested.
1156          */
1157
1158         if (MDOC_BODY == mdoc->last->type)
1159                 mdoc->flags &= ~MDOC_LITERAL;
1160
1161         return(1);
1162 }
1163
1164 static int
1165 post_defaults(POST_ARGS)
1166 {
1167         struct mdoc_node *nn;
1168
1169         /*
1170          * The `Ar' defaults to "file ..." if no value is provided as an
1171          * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1172          * gets an empty string.
1173          */
1174
1175         if (mdoc->last->child)
1176                 return(1);
1177         
1178         nn = mdoc->last;
1179         mdoc->next = MDOC_NEXT_CHILD;
1180
1181         switch (nn->tok) {
1182         case (MDOC_Ar):
1183                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1184                         return(0);
1185                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1186                         return(0);
1187                 break;
1188         case (MDOC_At):
1189                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1190                         return(0);
1191                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1192                         return(0);
1193                 break;
1194         case (MDOC_Li):
1195                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1196                         return(0);
1197                 break;
1198         case (MDOC_Pa):
1199                 /* FALLTHROUGH */
1200         case (MDOC_Mt):
1201                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1202                         return(0);
1203                 break;
1204         default:
1205                 abort();
1206                 /* NOTREACHED */
1207         } 
1208
1209         mdoc->last = nn;
1210         return(1);
1211 }
1212
1213 static int
1214 post_at(POST_ARGS)
1215 {
1216         const char       *p, *q;
1217         char             *buf;
1218         size_t            sz;
1219
1220         /*
1221          * If we have a child, look it up in the standard keys.  If a
1222          * key exist, use that instead of the child; if it doesn't,
1223          * prefix "AT&T UNIX " to the existing data.
1224          */
1225         
1226         if (NULL == mdoc->last->child)
1227                 return(1);
1228
1229         assert(MDOC_TEXT == mdoc->last->child->type);
1230         p = mdoc_a2att(mdoc->last->child->string);
1231
1232         if (p) {
1233                 free(mdoc->last->child->string);
1234                 mdoc->last->child->string = mandoc_strdup(p);
1235         } else {
1236                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1237                 p = "AT&T UNIX ";
1238                 q = mdoc->last->child->string;
1239                 sz = strlen(p) + strlen(q) + 1;
1240                 buf = mandoc_malloc(sz);
1241                 strlcpy(buf, p, sz);
1242                 strlcat(buf, q, sz);
1243                 free(mdoc->last->child->string);
1244                 mdoc->last->child->string = buf;
1245         }
1246
1247         return(1);
1248 }
1249
1250 static int
1251 post_an(POST_ARGS)
1252 {
1253         struct mdoc_node *np;
1254
1255         np = mdoc->last;
1256         if (AUTH__NONE == np->norm->An.auth) {
1257                 if (0 == np->child)
1258                         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1259         } else if (np->child)
1260                 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1261
1262         return(1);
1263 }
1264
1265
1266 static int
1267 post_it(POST_ARGS)
1268 {
1269         int               i, cols;
1270         enum mdoc_list    lt;
1271         struct mdoc_node *n, *c;
1272         enum mandocerr    er;
1273
1274         if (MDOC_BLOCK != mdoc->last->type)
1275                 return(1);
1276
1277         n = mdoc->last->parent->parent;
1278         lt = n->norm->Bl.type;
1279
1280         if (LIST__NONE == lt) {
1281                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1282                 return(1);
1283         }
1284
1285         switch (lt) {
1286         case (LIST_tag):
1287                 if (mdoc->last->head->child)
1288                         break;
1289                 /* FIXME: give this a dummy value. */
1290                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1291                 break;
1292         case (LIST_hang):
1293                 /* FALLTHROUGH */
1294         case (LIST_ohang):
1295                 /* FALLTHROUGH */
1296         case (LIST_inset):
1297                 /* FALLTHROUGH */
1298         case (LIST_diag):
1299                 if (NULL == mdoc->last->head->child)
1300                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1301                 break;
1302         case (LIST_bullet):
1303                 /* FALLTHROUGH */
1304         case (LIST_dash):
1305                 /* FALLTHROUGH */
1306         case (LIST_enum):
1307                 /* FALLTHROUGH */
1308         case (LIST_hyphen):
1309                 if (NULL == mdoc->last->body->child)
1310                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1311                 /* FALLTHROUGH */
1312         case (LIST_item):
1313                 if (mdoc->last->head->child)
1314                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1315                 break;
1316         case (LIST_column):
1317                 cols = (int)n->norm->Bl.ncols;
1318
1319                 assert(NULL == mdoc->last->head->child);
1320
1321                 if (NULL == mdoc->last->body->child)
1322                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1323
1324                 for (i = 0, c = mdoc->last->child; c; c = c->next)
1325                         if (MDOC_BODY == c->type)
1326                                 i++;
1327
1328                 if (i < cols)
1329                         er = MANDOCERR_ARGCOUNT;
1330                 else if (i == cols || i == cols + 1)
1331                         break;
1332                 else
1333                         er = MANDOCERR_SYNTARGCOUNT;
1334
1335                 mandoc_vmsg(er, mdoc->parse, mdoc->last->line, 
1336                                 mdoc->last->pos, 
1337                                 "columns == %d (have %d)", cols, i);
1338                 return(MANDOCERR_ARGCOUNT == er);
1339         default:
1340                 break;
1341         }
1342
1343         return(1);
1344 }
1345
1346 static int
1347 post_bl_block(POST_ARGS) 
1348 {
1349         struct mdoc_node *n;
1350
1351         /*
1352          * These are fairly complicated, so we've broken them into two
1353          * functions.  post_bl_block_tag() is called when a -tag is
1354          * specified, but no -width (it must be guessed).  The second
1355          * when a -width is specified (macro indicators must be
1356          * rewritten into real lengths).
1357          */
1358
1359         n = mdoc->last;
1360
1361         if (LIST_tag == n->norm->Bl.type && 
1362                         NULL == n->norm->Bl.width) {
1363                 if ( ! post_bl_block_tag(mdoc))
1364                         return(0);
1365         } else if (NULL != n->norm->Bl.width) {
1366                 if ( ! post_bl_block_width(mdoc))
1367                         return(0);
1368         } else 
1369                 return(1);
1370
1371         assert(n->norm->Bl.width);
1372         return(1);
1373 }
1374
1375 static int
1376 post_bl_block_width(POST_ARGS)
1377 {
1378         size_t            width;
1379         int               i;
1380         enum mdoct        tok;
1381         struct mdoc_node *n;
1382         char              buf[NUMSIZ];
1383
1384         n = mdoc->last;
1385
1386         /*
1387          * Calculate the real width of a list from the -width string,
1388          * which may contain a macro (with a known default width), a
1389          * literal string, or a scaling width.
1390          *
1391          * If the value to -width is a macro, then we re-write it to be
1392          * the macro's width as set in share/tmac/mdoc/doc-common.
1393          */
1394
1395         if (0 == strcmp(n->norm->Bl.width, "Ds"))
1396                 width = 6;
1397         else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1398                 return(1);
1399         else if (0 == (width = macro2len(tok)))  {
1400                 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1401                 return(1);
1402         }
1403
1404         /* The value already exists: free and reallocate it. */
1405
1406         assert(n->args);
1407
1408         for (i = 0; i < (int)n->args->argc; i++) 
1409                 if (MDOC_Width == n->args->argv[i].arg)
1410                         break;
1411
1412         assert(i < (int)n->args->argc);
1413
1414         snprintf(buf, NUMSIZ, "%zun", width);
1415         free(n->args->argv[i].value[0]);
1416         n->args->argv[i].value[0] = mandoc_strdup(buf);
1417
1418         /* Set our width! */
1419         n->norm->Bl.width = n->args->argv[i].value[0];
1420         return(1);
1421 }
1422
1423 static int
1424 post_bl_block_tag(POST_ARGS)
1425 {
1426         struct mdoc_node *n, *nn;
1427         size_t            sz, ssz;
1428         int               i;
1429         char              buf[NUMSIZ];
1430
1431         /*
1432          * Calculate the -width for a `Bl -tag' list if it hasn't been
1433          * provided.  Uses the first head macro.  NOTE AGAIN: this is
1434          * ONLY if the -width argument has NOT been provided.  See
1435          * post_bl_block_width() for converting the -width string.
1436          */
1437
1438         sz = 10;
1439         n = mdoc->last;
1440
1441         for (nn = n->body->child; nn; nn = nn->next) {
1442                 if (MDOC_It != nn->tok)
1443                         continue;
1444
1445                 assert(MDOC_BLOCK == nn->type);
1446                 nn = nn->head->child;
1447
1448                 if (nn == NULL)
1449                         break;
1450
1451                 if (MDOC_TEXT == nn->type) {
1452                         sz = strlen(nn->string) + 1;
1453                         break;
1454                 }
1455
1456                 if (0 != (ssz = macro2len(nn->tok)))
1457                         sz = ssz;
1458
1459                 break;
1460         } 
1461
1462         /* Defaults to ten ens. */
1463
1464         snprintf(buf, NUMSIZ, "%zun", sz);
1465
1466         /*
1467          * We have to dynamically add this to the macro's argument list.
1468          * We're guaranteed that a MDOC_Width doesn't already exist.
1469          */
1470
1471         assert(n->args);
1472         i = (int)(n->args->argc)++;
1473
1474         n->args->argv = mandoc_realloc(n->args->argv, 
1475                         n->args->argc * sizeof(struct mdoc_argv));
1476
1477         n->args->argv[i].arg = MDOC_Width;
1478         n->args->argv[i].line = n->line;
1479         n->args->argv[i].pos = n->pos;
1480         n->args->argv[i].sz = 1;
1481         n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1482         n->args->argv[i].value[0] = mandoc_strdup(buf);
1483
1484         /* Set our width! */
1485         n->norm->Bl.width = n->args->argv[i].value[0];
1486         return(1);
1487 }
1488
1489
1490 static int
1491 post_bl_head(POST_ARGS) 
1492 {
1493         struct mdoc_node *np, *nn, *nnp;
1494         int               i, j;
1495
1496         if (LIST_column != mdoc->last->norm->Bl.type)
1497                 /* FIXME: this should be ERROR class... */
1498                 return(hwarn_eq0(mdoc));
1499
1500         /*
1501          * Convert old-style lists, where the column width specifiers
1502          * trail as macro parameters, to the new-style ("normal-form")
1503          * lists where they're argument values following -column.
1504          */
1505
1506         /* First, disallow both types and allow normal-form. */
1507
1508         /* 
1509          * TODO: technically, we can accept both and just merge the two
1510          * lists, but I'll leave that for another day.
1511          */
1512
1513         if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1514                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1515                 return(0);
1516         } else if (NULL == mdoc->last->child)
1517                 return(1);
1518
1519         np = mdoc->last->parent;
1520         assert(np->args);
1521
1522         for (j = 0; j < (int)np->args->argc; j++) 
1523                 if (MDOC_Column == np->args->argv[j].arg)
1524                         break;
1525
1526         assert(j < (int)np->args->argc);
1527         assert(0 == np->args->argv[j].sz);
1528
1529         /*
1530          * Accomodate for new-style groff column syntax.  Shuffle the
1531          * child nodes, all of which must be TEXT, as arguments for the
1532          * column field.  Then, delete the head children.
1533          */
1534
1535         np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1536         np->args->argv[j].value = mandoc_malloc
1537                 ((size_t)mdoc->last->nchild * sizeof(char *));
1538
1539         mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1540         mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value;
1541
1542         for (i = 0, nn = mdoc->last->child; nn; i++) {
1543                 np->args->argv[j].value[i] = nn->string;
1544                 nn->string = NULL;
1545                 nnp = nn;
1546                 nn = nn->next;
1547                 mdoc_node_delete(NULL, nnp);
1548         }
1549
1550         mdoc->last->nchild = 0;
1551         mdoc->last->child = NULL;
1552
1553         return(1);
1554 }
1555
1556 static int
1557 post_bl(POST_ARGS)
1558 {
1559         struct mdoc_node        *n;
1560
1561         if (MDOC_HEAD == mdoc->last->type) 
1562                 return(post_bl_head(mdoc));
1563         if (MDOC_BLOCK == mdoc->last->type)
1564                 return(post_bl_block(mdoc));
1565         if (MDOC_BODY != mdoc->last->type)
1566                 return(1);
1567
1568         for (n = mdoc->last->child; n; n = n->next) {
1569                 switch (n->tok) {
1570                 case (MDOC_Lp):
1571                         /* FALLTHROUGH */
1572                 case (MDOC_Pp):
1573                         mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1574                         /* FALLTHROUGH */
1575                 case (MDOC_It):
1576                         /* FALLTHROUGH */
1577                 case (MDOC_Sm):
1578                         continue;
1579                 default:
1580                         break;
1581                 }
1582
1583                 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1584                 return(0);
1585         }
1586
1587         return(1);
1588 }
1589
1590 static int
1591 ebool(struct mdoc *mdoc)
1592 {
1593
1594         if (NULL == mdoc->last->child) {
1595                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1596                 mdoc_node_delete(mdoc, mdoc->last);
1597                 return(1);
1598         }
1599         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1600
1601         assert(MDOC_TEXT == mdoc->last->child->type);
1602
1603         if (0 == strcmp(mdoc->last->child->string, "on"))
1604                 return(1);
1605         if (0 == strcmp(mdoc->last->child->string, "off"))
1606                 return(1);
1607
1608         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1609         return(1);
1610 }
1611
1612 static int
1613 post_root(POST_ARGS)
1614 {
1615         int               erc;
1616         struct mdoc_node *n;
1617
1618         erc = 0;
1619
1620         /* Check that we have a finished prologue. */
1621
1622         if ( ! (MDOC_PBODY & mdoc->flags)) {
1623                 erc++;
1624                 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1625         }
1626
1627         n = mdoc->first;
1628         assert(n);
1629         
1630         /* Check that we begin with a proper `Sh'. */
1631
1632         if (NULL == n->child) {
1633                 erc++;
1634                 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1635         } else if (MDOC_BLOCK != n->child->type || 
1636                         MDOC_Sh != n->child->tok) {
1637                 erc++;
1638                 /* Can this be lifted?  See rxdebug.1 for example. */
1639                 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1640         }
1641
1642         return(erc ? 0 : 1);
1643 }
1644
1645 static int
1646 post_st(POST_ARGS)
1647 {
1648         struct mdoc_node         *ch;
1649         const char               *p;
1650
1651         if (NULL == (ch = mdoc->last->child)) {
1652                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1653                 mdoc_node_delete(mdoc, mdoc->last);
1654                 return(1);
1655         }
1656
1657         assert(MDOC_TEXT == ch->type);
1658
1659         if (NULL == (p = mdoc_a2st(ch->string))) {
1660                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1661                 mdoc_node_delete(mdoc, mdoc->last);
1662         } else {
1663                 free(ch->string);
1664                 ch->string = mandoc_strdup(p);
1665         }
1666
1667         return(1);
1668 }
1669
1670 static int
1671 post_rs(POST_ARGS)
1672 {
1673         struct mdoc_node *nn, *next, *prev;
1674         int               i, j;
1675
1676         switch (mdoc->last->type) {
1677         case (MDOC_HEAD):
1678                 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1679                 return(1);
1680         case (MDOC_BODY):
1681                 if (mdoc->last->child)
1682                         break;
1683                 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1684                 return(1);
1685         default:
1686                 return(1);
1687         }
1688
1689         /*
1690          * Make sure only certain types of nodes are allowed within the
1691          * the `Rs' body.  Delete offending nodes and raise a warning.
1692          * Do this before re-ordering for the sake of clarity.
1693          */
1694
1695         next = NULL;
1696         for (nn = mdoc->last->child; nn; nn = next) {
1697                 for (i = 0; i < RSORD_MAX; i++)
1698                         if (nn->tok == rsord[i])
1699                                 break;
1700
1701                 if (i < RSORD_MAX) {
1702                         if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1703                                 mdoc->last->norm->Rs.quote_T++;
1704                         next = nn->next;
1705                         continue;
1706                 }
1707
1708                 next = nn->next;
1709                 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1710                 mdoc_node_delete(mdoc, nn);
1711         }
1712
1713         /*
1714          * The full `Rs' block needs special handling to order the
1715          * sub-elements according to `rsord'.  Pick through each element
1716          * and correctly order it.  This is a insertion sort.
1717          */
1718
1719         next = NULL;
1720         for (nn = mdoc->last->child->next; nn; nn = next) {
1721                 /* Determine order of `nn'. */
1722                 for (i = 0; i < RSORD_MAX; i++)
1723                         if (rsord[i] == nn->tok)
1724                                 break;
1725
1726                 /* 
1727                  * Remove `nn' from the chain.  This somewhat
1728                  * repeats mdoc_node_unlink(), but since we're
1729                  * just re-ordering, there's no need for the
1730                  * full unlink process.
1731                  */
1732                 
1733                 if (NULL != (next = nn->next))
1734                         next->prev = nn->prev;
1735
1736                 if (NULL != (prev = nn->prev))
1737                         prev->next = nn->next;
1738
1739                 nn->prev = nn->next = NULL;
1740
1741                 /* 
1742                  * Scan back until we reach a node that's
1743                  * ordered before `nn'.
1744                  */
1745
1746                 for ( ; prev ; prev = prev->prev) {
1747                         /* Determine order of `prev'. */
1748                         for (j = 0; j < RSORD_MAX; j++)
1749                                 if (rsord[j] == prev->tok)
1750                                         break;
1751
1752                         if (j <= i)
1753                                 break;
1754                 }
1755
1756                 /*
1757                  * Set `nn' back into its correct place in front
1758                  * of the `prev' node.
1759                  */
1760
1761                 nn->prev = prev;
1762
1763                 if (prev) {
1764                         if (prev->next)
1765                                 prev->next->prev = nn;
1766                         nn->next = prev->next;
1767                         prev->next = nn;
1768                 } else {
1769                         mdoc->last->child->prev = nn;
1770                         nn->next = mdoc->last->child;
1771                         mdoc->last->child = nn;
1772                 }
1773         }
1774
1775         return(1);
1776 }
1777
1778 static int
1779 post_ns(POST_ARGS)
1780 {
1781
1782         if (MDOC_LINE & mdoc->last->flags)
1783                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1784         return(1);
1785 }
1786
1787 static int
1788 post_sh(POST_ARGS)
1789 {
1790
1791         if (MDOC_HEAD == mdoc->last->type)
1792                 return(post_sh_head(mdoc));
1793         if (MDOC_BODY == mdoc->last->type)
1794                 return(post_sh_body(mdoc));
1795
1796         return(1);
1797 }
1798
1799 static int
1800 post_sh_body(POST_ARGS)
1801 {
1802         struct mdoc_node *n;
1803
1804         if (SEC_NAME != mdoc->lastsec)
1805                 return(1);
1806
1807         /*
1808          * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1809          * macros (can have multiple `Nm' and one `Nd').  Note that the
1810          * children of the BODY declaration can also be "text".
1811          */
1812
1813         if (NULL == (n = mdoc->last->child)) {
1814                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1815                 return(1);
1816         }
1817
1818         for ( ; n && n->next; n = n->next) {
1819                 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1820                         continue;
1821                 if (MDOC_TEXT == n->type)
1822                         continue;
1823                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1824         }
1825
1826         assert(n);
1827         if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1828                 return(1);
1829
1830         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1831         return(1);
1832 }
1833
1834 static int
1835 post_sh_head(POST_ARGS)
1836 {
1837         char             buf[BUFSIZ];
1838         enum mdoc_sec    sec;
1839
1840         /*
1841          * Process a new section.  Sections are either "named" or
1842          * "custom".  Custom sections are user-defined, while named ones
1843          * follow a conventional order and may only appear in certain
1844          * manual sections.
1845          */
1846
1847         if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1848                 return(0);
1849
1850         sec = a2sec(buf);
1851
1852         /* The NAME should be first. */
1853
1854         if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1855                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1856
1857         /* The SYNOPSIS gets special attention in other areas. */
1858
1859         if (SEC_SYNOPSIS == sec)
1860                 mdoc->flags |= MDOC_SYNOPSIS;
1861         else
1862                 mdoc->flags &= ~MDOC_SYNOPSIS;
1863
1864         /* Mark our last section. */
1865
1866         mdoc->lastsec = sec;
1867
1868         /* We don't care about custom sections after this. */
1869
1870         if (SEC_CUSTOM == sec)
1871                 return(1);
1872
1873         /*
1874          * Check whether our non-custom section is being repeated or is
1875          * out of order.
1876          */
1877
1878         if (sec == mdoc->lastnamed)
1879                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1880
1881         if (sec < mdoc->lastnamed)
1882                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1883
1884         /* Mark the last named section. */
1885
1886         mdoc->lastnamed = sec;
1887
1888         /* Check particular section/manual conventions. */
1889
1890         assert(mdoc->meta.msec);
1891
1892         switch (sec) {
1893         case (SEC_RETURN_VALUES):
1894                 /* FALLTHROUGH */
1895         case (SEC_ERRORS):
1896                 /* FALLTHROUGH */
1897         case (SEC_LIBRARY):
1898                 if (*mdoc->meta.msec == '2')
1899                         break;
1900                 if (*mdoc->meta.msec == '3')
1901                         break;
1902                 if (*mdoc->meta.msec == '9')
1903                         break;
1904                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1905                 break;
1906         default:
1907                 break;
1908         }
1909
1910         return(1);
1911 }
1912
1913 static int
1914 post_ignpar(POST_ARGS)
1915 {
1916         struct mdoc_node *np;
1917
1918         if (MDOC_BODY != mdoc->last->type)
1919                 return(1);
1920
1921         if (NULL != (np = mdoc->last->child))
1922                 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1923                         mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1924                         mdoc_node_delete(mdoc, np);
1925                 }
1926
1927         if (NULL != (np = mdoc->last->last))
1928                 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1929                         mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1930                         mdoc_node_delete(mdoc, np);
1931                 }
1932
1933         return(1);
1934 }
1935
1936 static int
1937 pre_par(PRE_ARGS)
1938 {
1939
1940         if (NULL == mdoc->last)
1941                 return(1);
1942         if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1943                 return(1);
1944
1945         /* 
1946          * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1947          * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
1948          */
1949
1950         if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1951                 return(1);
1952         if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1953                 return(1);
1954         if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1955                 return(1);
1956         if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1957                 return(1);
1958
1959         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1960         mdoc_node_delete(mdoc, mdoc->last);
1961         return(1);
1962 }
1963
1964 static int
1965 pre_literal(PRE_ARGS)
1966 {
1967
1968         if (MDOC_BODY != n->type)
1969                 return(1);
1970
1971         /*
1972          * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1973          * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1974          */
1975
1976         switch (n->tok) {
1977         case (MDOC_Dl):
1978                 mdoc->flags |= MDOC_LITERAL;
1979                 break;
1980         case (MDOC_Bd):
1981                 if (DISP_literal == n->norm->Bd.type)
1982                         mdoc->flags |= MDOC_LITERAL;
1983                 if (DISP_unfilled == n->norm->Bd.type)
1984                         mdoc->flags |= MDOC_LITERAL;
1985                 break;
1986         default:
1987                 abort();
1988                 /* NOTREACHED */
1989         }
1990         
1991         return(1);
1992 }
1993
1994 static int
1995 post_dd(POST_ARGS)
1996 {
1997         char              buf[DATESIZE];
1998         struct mdoc_node *n;
1999
2000         if (mdoc->meta.date)
2001                 free(mdoc->meta.date);
2002
2003         n = mdoc->last;
2004         if (NULL == n->child || '\0' == n->child->string[0]) {
2005                 mdoc->meta.date = mandoc_normdate
2006                         (mdoc->parse, NULL, n->line, n->pos);
2007                 return(1);
2008         }
2009
2010         if ( ! concat(mdoc, buf, n->child, DATESIZE))
2011                 return(0);
2012
2013         mdoc->meta.date = mandoc_normdate
2014                 (mdoc->parse, buf, n->line, n->pos);
2015
2016         return(1);
2017 }
2018
2019 static int
2020 post_dt(POST_ARGS)
2021 {
2022         struct mdoc_node *nn, *n;
2023         const char       *cp;
2024         char             *p;
2025
2026         n = mdoc->last;
2027
2028         if (mdoc->meta.title)
2029                 free(mdoc->meta.title);
2030         if (mdoc->meta.vol)
2031                 free(mdoc->meta.vol);
2032         if (mdoc->meta.arch)
2033                 free(mdoc->meta.arch);
2034
2035         mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2036
2037         /* First make all characters uppercase. */
2038
2039         if (NULL != (nn = n->child))
2040                 for (p = nn->string; *p; p++) {
2041                         if (toupper((u_char)*p) == *p)
2042                                 continue;
2043
2044                         /* 
2045                          * FIXME: don't be lazy: have this make all
2046                          * characters be uppercase and just warn once.
2047                          */
2048                         mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2049                         break;
2050                 }
2051
2052         /* Handles: `.Dt' 
2053          *   --> title = unknown, volume = local, msec = 0, arch = NULL
2054          */
2055
2056         if (NULL == (nn = n->child)) {
2057                 /* XXX: make these macro values. */
2058                 /* FIXME: warn about missing values. */
2059                 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2060                 mdoc->meta.vol = mandoc_strdup("LOCAL");
2061                 mdoc->meta.msec = mandoc_strdup("1");
2062                 return(1);
2063         }
2064
2065         /* Handles: `.Dt TITLE' 
2066          *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2067          */
2068
2069         mdoc->meta.title = mandoc_strdup
2070                 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2071
2072         if (NULL == (nn = nn->next)) {
2073                 /* FIXME: warn about missing msec. */
2074                 /* XXX: make this a macro value. */
2075                 mdoc->meta.vol = mandoc_strdup("LOCAL");
2076                 mdoc->meta.msec = mandoc_strdup("1");
2077                 return(1);
2078         }
2079
2080         /* Handles: `.Dt TITLE SEC'
2081          *   --> title = TITLE, volume = SEC is msec ? 
2082          *           format(msec) : SEC,
2083          *       msec = SEC is msec ? atoi(msec) : 0,
2084          *       arch = NULL
2085          */
2086
2087         cp = mdoc_a2msec(nn->string);
2088         if (cp) {
2089                 mdoc->meta.vol = mandoc_strdup(cp);
2090                 mdoc->meta.msec = mandoc_strdup(nn->string);
2091         } else {
2092                 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2093                 mdoc->meta.vol = mandoc_strdup(nn->string);
2094                 mdoc->meta.msec = mandoc_strdup(nn->string);
2095         } 
2096
2097         if (NULL == (nn = nn->next))
2098                 return(1);
2099
2100         /* Handles: `.Dt TITLE SEC VOL'
2101          *   --> title = TITLE, volume = VOL is vol ?
2102          *       format(VOL) : 
2103          *           VOL is arch ? format(arch) : 
2104          *               VOL
2105          */
2106
2107         cp = mdoc_a2vol(nn->string);
2108         if (cp) {
2109                 free(mdoc->meta.vol);
2110                 mdoc->meta.vol = mandoc_strdup(cp);
2111         } else {
2112                 /* FIXME: warn about bad arch. */
2113                 cp = mdoc_a2arch(nn->string);
2114                 if (NULL == cp) {
2115                         free(mdoc->meta.vol);
2116                         mdoc->meta.vol = mandoc_strdup(nn->string);
2117                 } else 
2118                         mdoc->meta.arch = mandoc_strdup(cp);
2119         }       
2120
2121         /* Ignore any subsequent parameters... */
2122         /* FIXME: warn about subsequent parameters. */
2123
2124         return(1);
2125 }
2126
2127 static int
2128 post_prol(POST_ARGS)
2129 {
2130         /*
2131          * Remove prologue macros from the document after they're
2132          * processed.  The final document uses mdoc_meta for these
2133          * values and discards the originals.
2134          */
2135
2136         mdoc_node_delete(mdoc, mdoc->last);
2137         if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2138                 mdoc->flags |= MDOC_PBODY;
2139
2140         return(1);
2141 }
2142
2143 static int
2144 post_bx(POST_ARGS)
2145 {
2146         struct mdoc_node        *n;
2147
2148         /* 
2149          * Make `Bx's second argument always start with an uppercase
2150          * letter.  Groff checks if it's an "accepted" term, but we just
2151          * uppercase blindly.
2152          */
2153
2154         n = mdoc->last->child;
2155         if (n && NULL != (n = n->next))
2156                 *n->string = (char)toupper
2157                         ((unsigned char)*n->string);
2158
2159         return(1);
2160 }
2161
2162 static int
2163 post_os(POST_ARGS)
2164 {
2165         struct mdoc_node *n;
2166         char              buf[BUFSIZ];
2167 #ifndef OSNAME
2168         struct utsname    utsname;
2169 #endif
2170
2171         n = mdoc->last;
2172
2173         /*
2174          * Set the operating system by way of the `Os' macro.  Note that
2175          * if an argument isn't provided and -DOSNAME="\"foo\"" is
2176          * provided during compilation, this value will be used instead
2177          * of filling in "sysname release" from uname().
2178          */
2179
2180         if (mdoc->meta.os)
2181                 free(mdoc->meta.os);
2182
2183         if ( ! concat(mdoc, buf, n->child, BUFSIZ))
2184                 return(0);
2185
2186         /* XXX: yes, these can all be dynamically-adjusted buffers, but
2187          * it's really not worth the extra hackery.
2188          */
2189
2190         if ('\0' == buf[0]) {
2191 #ifdef OSNAME
2192                 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2193                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2194                         return(0);
2195                 }
2196 #else /*!OSNAME */
2197                 if (-1 == uname(&utsname)) {
2198                         mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2199                         mdoc->meta.os = mandoc_strdup("UNKNOWN");
2200                         return(post_prol(mdoc));
2201                 }
2202
2203                 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2204                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2205                         return(0);
2206                 }
2207                 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2208                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2209                         return(0);
2210                 }
2211                 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2212                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2213                         return(0);
2214                 }
2215 #endif /*!OSNAME*/
2216         }
2217
2218         mdoc->meta.os = mandoc_strdup(buf);
2219         return(1);
2220 }
2221
2222 static int
2223 post_std(POST_ARGS)
2224 {
2225         struct mdoc_node *nn, *n;
2226
2227         n = mdoc->last;
2228
2229         /*
2230          * Macros accepting `-std' as an argument have the name of the
2231          * current document (`Nm') filled in as the argument if it's not
2232          * provided.
2233          */
2234
2235         if (n->child)
2236                 return(1);
2237
2238         if (NULL == mdoc->meta.name)
2239                 return(1);
2240         
2241         nn = n;
2242         mdoc->next = MDOC_NEXT_CHILD;
2243
2244         if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2245                 return(0);
2246
2247         mdoc->last = nn;
2248         return(1);
2249 }
2250
2251 static int
2252 concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz)
2253 {
2254
2255         p[0] = '\0';
2256
2257         /*
2258          * Concatenate sibling nodes together.  All siblings must be of
2259          * type MDOC_TEXT or an assertion is raised.  Concatenation is
2260          * separated by a single whitespace.  Returns 0 on fatal (string
2261          * overrun) error.
2262          */
2263
2264         for ( ; n; n = n->next) {
2265                 assert(MDOC_TEXT == n->type);
2266
2267                 if (strlcat(p, n->string, sz) >= sz) {
2268                         mdoc_nmsg(m, n, MANDOCERR_MEM);
2269                         return(0);
2270                 }
2271
2272                 if (NULL == n->next)
2273                         continue;
2274
2275                 if (strlcat(p, " ", sz) >= sz) {
2276                         mdoc_nmsg(m, n, MANDOCERR_MEM);
2277                         return(0);
2278                 }
2279         }
2280
2281         return(1);
2282 }
2283
2284 static enum mdoc_sec 
2285 a2sec(const char *p)
2286 {
2287         int              i;
2288
2289         for (i = 0; i < (int)SEC__MAX; i++) 
2290                 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2291                         return((enum mdoc_sec)i);
2292
2293         return(SEC_CUSTOM);
2294 }
2295
2296 static size_t
2297 macro2len(enum mdoct macro)
2298 {
2299
2300         switch (macro) {
2301         case(MDOC_Ad):
2302                 return(12);
2303         case(MDOC_Ao):
2304                 return(12);
2305         case(MDOC_An):
2306                 return(12);
2307         case(MDOC_Aq):
2308                 return(12);
2309         case(MDOC_Ar):
2310                 return(12);
2311         case(MDOC_Bo):
2312                 return(12);
2313         case(MDOC_Bq):
2314                 return(12);
2315         case(MDOC_Cd):
2316                 return(12);
2317         case(MDOC_Cm):
2318                 return(10);
2319         case(MDOC_Do):
2320                 return(10);
2321         case(MDOC_Dq):
2322                 return(12);
2323         case(MDOC_Dv):
2324                 return(12);
2325         case(MDOC_Eo):
2326                 return(12);
2327         case(MDOC_Em):
2328                 return(10);
2329         case(MDOC_Er):
2330                 return(17);
2331         case(MDOC_Ev):
2332                 return(15);
2333         case(MDOC_Fa):
2334                 return(12);
2335         case(MDOC_Fl):
2336                 return(10);
2337         case(MDOC_Fo):
2338                 return(16);
2339         case(MDOC_Fn):
2340                 return(16);
2341         case(MDOC_Ic):
2342                 return(10);
2343         case(MDOC_Li):
2344                 return(16);
2345         case(MDOC_Ms):
2346                 return(6);
2347         case(MDOC_Nm):
2348                 return(10);
2349         case(MDOC_No):
2350                 return(12);
2351         case(MDOC_Oo):
2352                 return(10);
2353         case(MDOC_Op):
2354                 return(14);
2355         case(MDOC_Pa):
2356                 return(32);
2357         case(MDOC_Pf):
2358                 return(12);
2359         case(MDOC_Po):
2360                 return(12);
2361         case(MDOC_Pq):
2362                 return(12);
2363         case(MDOC_Ql):
2364                 return(16);
2365         case(MDOC_Qo):
2366                 return(12);
2367         case(MDOC_So):
2368                 return(12);
2369         case(MDOC_Sq):
2370                 return(12);
2371         case(MDOC_Sy):
2372                 return(6);
2373         case(MDOC_Sx):
2374                 return(16);
2375         case(MDOC_Tn):
2376                 return(10);
2377         case(MDOC_Va):
2378                 return(12);
2379         case(MDOC_Vt):
2380                 return(12);
2381         case(MDOC_Xr):
2382                 return(10);
2383         default:
2384                 break;
2385         };
2386         return(0);
2387 }