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