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