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