Import mdocml-1.12.3
[dragonfly.git] / contrib / mdocml / mdoc_validate.c
1 /*      $Id: mdoc_validate.c,v 1.198 2013/12/15 21:23:52 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010, 2011, 2012, 2013 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         return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
893 }
894
895
896 static int
897 pre_it(PRE_ARGS)
898 {
899
900         if (MDOC_BLOCK != n->type)
901                 return(1);
902
903         return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
904 }
905
906
907 static int
908 pre_an(PRE_ARGS)
909 {
910         int              i;
911
912         if (NULL == n->args)
913                 return(1);
914         
915         for (i = 1; i < (int)n->args->argc; i++)
916                 mdoc_pmsg(mdoc, n->args->argv[i].line, 
917                         n->args->argv[i].pos, MANDOCERR_IGNARGV);
918
919         if (MDOC_Split == n->args->argv[0].arg)
920                 n->norm->An.auth = AUTH_split;
921         else if (MDOC_Nosplit == n->args->argv[0].arg)
922                 n->norm->An.auth = AUTH_nosplit;
923         else
924                 abort();
925
926         return(1);
927 }
928
929 static int
930 pre_std(PRE_ARGS)
931 {
932
933         if (n->args && 1 == n->args->argc)
934                 if (MDOC_Std == n->args->argv[0].arg)
935                         return(1);
936
937         mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
938         return(1);
939 }
940
941 static int
942 pre_dt(PRE_ARGS)
943 {
944
945         if (NULL == mdoc->meta.date || mdoc->meta.os)
946                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
947
948         if (mdoc->meta.title)
949                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
950
951         return(1);
952 }
953
954 static int
955 pre_os(PRE_ARGS)
956 {
957
958         if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
959                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
960
961         if (mdoc->meta.os)
962                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
963
964         return(1);
965 }
966
967 static int
968 pre_dd(PRE_ARGS)
969 {
970
971         if (mdoc->meta.title || mdoc->meta.os)
972                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
973
974         if (mdoc->meta.date)
975                 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
976
977         return(1);
978 }
979
980
981 static int
982 post_bf(POST_ARGS)
983 {
984         struct mdoc_node *np;
985         enum mdocargt     arg;
986
987         /*
988          * Unlike other data pointers, these are "housed" by the HEAD
989          * element, which contains the goods.
990          */
991
992         if (MDOC_HEAD != mdoc->last->type) {
993                 if (ENDBODY_NOT != mdoc->last->end) {
994                         assert(mdoc->last->pending);
995                         np = mdoc->last->pending->parent->head;
996                 } else if (MDOC_BLOCK != mdoc->last->type) {
997                         np = mdoc->last->parent->head;
998                 } else 
999                         np = mdoc->last->head;
1000
1001                 assert(np);
1002                 assert(MDOC_HEAD == np->type);
1003                 assert(MDOC_Bf == np->tok);
1004                 return(1);
1005         }
1006
1007         np = mdoc->last;
1008         assert(MDOC_BLOCK == np->parent->type);
1009         assert(MDOC_Bf == np->parent->tok);
1010
1011         /* 
1012          * Cannot have both argument and parameter.
1013          * If neither is specified, let it through with a warning. 
1014          */
1015
1016         if (np->parent->args && np->child) {
1017                 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1018                 return(0);
1019         } else if (NULL == np->parent->args && NULL == np->child) {
1020                 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1021                 return(1);
1022         }
1023
1024         /* Extract argument into data. */
1025         
1026         if (np->parent->args) {
1027                 arg = np->parent->args->argv[0].arg;
1028                 if (MDOC_Emphasis == arg)
1029                         np->norm->Bf.font = FONT_Em;
1030                 else if (MDOC_Literal == arg)
1031                         np->norm->Bf.font = FONT_Li;
1032                 else if (MDOC_Symbolic == arg)
1033                         np->norm->Bf.font = FONT_Sy;
1034                 else
1035                         abort();
1036                 return(1);
1037         }
1038
1039         /* Extract parameter into data. */
1040
1041         if (0 == strcmp(np->child->string, "Em"))
1042                 np->norm->Bf.font = FONT_Em;
1043         else if (0 == strcmp(np->child->string, "Li"))
1044                 np->norm->Bf.font = FONT_Li;
1045         else if (0 == strcmp(np->child->string, "Sy"))
1046                 np->norm->Bf.font = FONT_Sy;
1047         else 
1048                 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1049
1050         return(1);
1051 }
1052
1053 static int
1054 post_lb(POST_ARGS)
1055 {
1056         const char      *p;
1057         char            *buf;
1058         size_t           sz;
1059
1060         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1061
1062         assert(mdoc->last->child);
1063         assert(MDOC_TEXT == mdoc->last->child->type);
1064
1065         p = mdoc_a2lib(mdoc->last->child->string);
1066
1067         /* If lookup ok, replace with table value. */
1068
1069         if (p) {
1070                 free(mdoc->last->child->string);
1071                 mdoc->last->child->string = mandoc_strdup(p);
1072                 return(1);
1073         }
1074
1075         /* If not, use "library ``xxxx''. */
1076
1077         sz = strlen(mdoc->last->child->string) +
1078                 2 + strlen("\\(lqlibrary\\(rq");
1079         buf = mandoc_malloc(sz);
1080         snprintf(buf, sz, "library \\(lq%s\\(rq", 
1081                         mdoc->last->child->string);
1082         free(mdoc->last->child->string);
1083         mdoc->last->child->string = buf;
1084         return(1);
1085 }
1086
1087 static int
1088 post_eoln(POST_ARGS)
1089 {
1090
1091         if (mdoc->last->child)
1092                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1093         return(1);
1094 }
1095
1096
1097 static int
1098 post_vt(POST_ARGS)
1099 {
1100         const struct mdoc_node *n;
1101
1102         /*
1103          * The Vt macro comes in both ELEM and BLOCK form, both of which
1104          * have different syntaxes (yet more context-sensitive
1105          * behaviour).  ELEM types must have a child, which is already
1106          * guaranteed by the in_line parsing routine; BLOCK types,
1107          * specifically the BODY, should only have TEXT children.
1108          */
1109
1110         if (MDOC_BODY != mdoc->last->type)
1111                 return(1);
1112         
1113         for (n = mdoc->last->child; n; n = n->next)
1114                 if (MDOC_TEXT != n->type) 
1115                         mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1116
1117         return(1);
1118 }
1119
1120
1121 static int
1122 post_nm(POST_ARGS)
1123 {
1124         char             buf[BUFSIZ];
1125         int              c;
1126
1127         if (NULL != mdoc->meta.name)
1128                 return(1);
1129
1130         /* Try to use our children for setting the meta name. */
1131
1132         if (NULL != mdoc->last->child) {
1133                 buf[0] = '\0';
1134                 c = concat(buf, mdoc->last->child, BUFSIZ);
1135         } else
1136                 c = 0;
1137
1138         switch (c) {
1139         case (-1):
1140                 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1141                 return(0);
1142         case (0):
1143                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1144                 mdoc->meta.name = mandoc_strdup("UNKNOWN");
1145                 break;
1146         default:
1147                 mdoc->meta.name = mandoc_strdup(buf);
1148                 break;
1149         }
1150         return(1);
1151 }
1152
1153 static int
1154 post_literal(POST_ARGS)
1155 {
1156         
1157         /*
1158          * The `Dl' (note "el" not "one") and `Bd' macros unset the
1159          * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1160          * this in literal mode, but it doesn't hurt to just switch it
1161          * off in general since displays can't be nested.
1162          */
1163
1164         if (MDOC_BODY == mdoc->last->type)
1165                 mdoc->flags &= ~MDOC_LITERAL;
1166
1167         return(1);
1168 }
1169
1170 static int
1171 post_defaults(POST_ARGS)
1172 {
1173         struct mdoc_node *nn;
1174
1175         /*
1176          * The `Ar' defaults to "file ..." if no value is provided as an
1177          * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1178          * gets an empty string.
1179          */
1180
1181         if (mdoc->last->child)
1182                 return(1);
1183         
1184         nn = mdoc->last;
1185         mdoc->next = MDOC_NEXT_CHILD;
1186
1187         switch (nn->tok) {
1188         case (MDOC_Ar):
1189                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1190                         return(0);
1191                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1192                         return(0);
1193                 break;
1194         case (MDOC_At):
1195                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1196                         return(0);
1197                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1198                         return(0);
1199                 break;
1200         case (MDOC_Li):
1201                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1202                         return(0);
1203                 break;
1204         case (MDOC_Pa):
1205                 /* FALLTHROUGH */
1206         case (MDOC_Mt):
1207                 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1208                         return(0);
1209                 break;
1210         default:
1211                 abort();
1212                 /* NOTREACHED */
1213         } 
1214
1215         mdoc->last = nn;
1216         return(1);
1217 }
1218
1219 static int
1220 post_at(POST_ARGS)
1221 {
1222         const char       *p, *q;
1223         char             *buf;
1224         size_t            sz;
1225
1226         /*
1227          * If we have a child, look it up in the standard keys.  If a
1228          * key exist, use that instead of the child; if it doesn't,
1229          * prefix "AT&T UNIX " to the existing data.
1230          */
1231         
1232         if (NULL == mdoc->last->child)
1233                 return(1);
1234
1235         assert(MDOC_TEXT == mdoc->last->child->type);
1236         p = mdoc_a2att(mdoc->last->child->string);
1237
1238         if (p) {
1239                 free(mdoc->last->child->string);
1240                 mdoc->last->child->string = mandoc_strdup(p);
1241         } else {
1242                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1243                 p = "AT&T UNIX ";
1244                 q = mdoc->last->child->string;
1245                 sz = strlen(p) + strlen(q) + 1;
1246                 buf = mandoc_malloc(sz);
1247                 strlcpy(buf, p, sz);
1248                 strlcat(buf, q, sz);
1249                 free(mdoc->last->child->string);
1250                 mdoc->last->child->string = buf;
1251         }
1252
1253         return(1);
1254 }
1255
1256 static int
1257 post_an(POST_ARGS)
1258 {
1259         struct mdoc_node *np;
1260
1261         np = mdoc->last;
1262         if (AUTH__NONE == np->norm->An.auth) {
1263                 if (0 == np->child)
1264                         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1265         } else if (np->child)
1266                 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1267
1268         return(1);
1269 }
1270
1271
1272 static int
1273 post_it(POST_ARGS)
1274 {
1275         int               i, cols;
1276         enum mdoc_list    lt;
1277         struct mdoc_node *n, *c;
1278         enum mandocerr    er;
1279
1280         if (MDOC_BLOCK != mdoc->last->type)
1281                 return(1);
1282
1283         n = mdoc->last->parent->parent;
1284         lt = n->norm->Bl.type;
1285
1286         if (LIST__NONE == lt) {
1287                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1288                 return(1);
1289         }
1290
1291         switch (lt) {
1292         case (LIST_tag):
1293                 if (mdoc->last->head->child)
1294                         break;
1295                 /* FIXME: give this a dummy value. */
1296                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1297                 break;
1298         case (LIST_hang):
1299                 /* FALLTHROUGH */
1300         case (LIST_ohang):
1301                 /* FALLTHROUGH */
1302         case (LIST_inset):
1303                 /* FALLTHROUGH */
1304         case (LIST_diag):
1305                 if (NULL == mdoc->last->head->child)
1306                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1307                 break;
1308         case (LIST_bullet):
1309                 /* FALLTHROUGH */
1310         case (LIST_dash):
1311                 /* FALLTHROUGH */
1312         case (LIST_enum):
1313                 /* FALLTHROUGH */
1314         case (LIST_hyphen):
1315                 if (NULL == mdoc->last->body->child)
1316                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1317                 /* FALLTHROUGH */
1318         case (LIST_item):
1319                 if (mdoc->last->head->child)
1320                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1321                 break;
1322         case (LIST_column):
1323                 cols = (int)n->norm->Bl.ncols;
1324
1325                 assert(NULL == mdoc->last->head->child);
1326
1327                 if (NULL == mdoc->last->body->child)
1328                         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1329
1330                 for (i = 0, c = mdoc->last->child; c; c = c->next)
1331                         if (MDOC_BODY == c->type)
1332                                 i++;
1333
1334                 if (i < cols)
1335                         er = MANDOCERR_ARGCOUNT;
1336                 else if (i == cols || i == cols + 1)
1337                         break;
1338                 else
1339                         er = MANDOCERR_SYNTARGCOUNT;
1340
1341                 mandoc_vmsg(er, mdoc->parse, mdoc->last->line, 
1342                                 mdoc->last->pos, 
1343                                 "columns == %d (have %d)", cols, i);
1344                 return(MANDOCERR_ARGCOUNT == er);
1345         default:
1346                 break;
1347         }
1348
1349         return(1);
1350 }
1351
1352 static int
1353 post_bl_block(POST_ARGS) 
1354 {
1355         struct mdoc_node *n, *ni, *nc;
1356
1357         /*
1358          * These are fairly complicated, so we've broken them into two
1359          * functions.  post_bl_block_tag() is called when a -tag is
1360          * specified, but no -width (it must be guessed).  The second
1361          * when a -width is specified (macro indicators must be
1362          * rewritten into real lengths).
1363          */
1364
1365         n = mdoc->last;
1366
1367         if (LIST_tag == n->norm->Bl.type && 
1368                         NULL == n->norm->Bl.width) {
1369                 if ( ! post_bl_block_tag(mdoc))
1370                         return(0);
1371                 assert(n->norm->Bl.width);
1372         } else if (NULL != n->norm->Bl.width) {
1373                 if ( ! post_bl_block_width(mdoc))
1374                         return(0);
1375                 assert(n->norm->Bl.width);
1376         }
1377
1378         for (ni = n->body->child; ni; ni = ni->next) {
1379                 if (NULL == ni->body)
1380                         continue;
1381                 nc = ni->body->last;
1382                 while (NULL != nc) {
1383                         switch (nc->tok) {
1384                         case (MDOC_Pp):
1385                                 /* FALLTHROUGH */
1386                         case (MDOC_Lp):
1387                                 /* FALLTHROUGH */
1388                         case (MDOC_br):
1389                                 break;
1390                         default:
1391                                 nc = NULL;
1392                                 continue;
1393                         }
1394                         if (NULL == ni->next) {
1395                                 mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1396                                 if ( ! mdoc_node_relink(mdoc, nc))
1397                                         return(0);
1398                         } else if (0 == n->norm->Bl.comp &&
1399                             LIST_column != n->norm->Bl.type) {
1400                                 mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1401                                 mdoc_node_delete(mdoc, nc);
1402                         } else
1403                                 break;
1404                         nc = ni->body->last;
1405                 }
1406         }
1407         return(1);
1408 }
1409
1410 static int
1411 post_bl_block_width(POST_ARGS)
1412 {
1413         size_t            width;
1414         int               i;
1415         enum mdoct        tok;
1416         struct mdoc_node *n;
1417         char              buf[NUMSIZ];
1418
1419         n = mdoc->last;
1420
1421         /*
1422          * Calculate the real width of a list from the -width string,
1423          * which may contain a macro (with a known default width), a
1424          * literal string, or a scaling width.
1425          *
1426          * If the value to -width is a macro, then we re-write it to be
1427          * the macro's width as set in share/tmac/mdoc/doc-common.
1428          */
1429
1430         if (0 == strcmp(n->norm->Bl.width, "Ds"))
1431                 width = 6;
1432         else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1433                 return(1);
1434         else if (0 == (width = macro2len(tok)))  {
1435                 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1436                 return(1);
1437         }
1438
1439         /* The value already exists: free and reallocate it. */
1440
1441         assert(n->args);
1442
1443         for (i = 0; i < (int)n->args->argc; i++) 
1444                 if (MDOC_Width == n->args->argv[i].arg)
1445                         break;
1446
1447         assert(i < (int)n->args->argc);
1448
1449         snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1450         free(n->args->argv[i].value[0]);
1451         n->args->argv[i].value[0] = mandoc_strdup(buf);
1452
1453         /* Set our width! */
1454         n->norm->Bl.width = n->args->argv[i].value[0];
1455         return(1);
1456 }
1457
1458 static int
1459 post_bl_block_tag(POST_ARGS)
1460 {
1461         struct mdoc_node *n, *nn;
1462         size_t            sz, ssz;
1463         int               i;
1464         char              buf[NUMSIZ];
1465
1466         /*
1467          * Calculate the -width for a `Bl -tag' list if it hasn't been
1468          * provided.  Uses the first head macro.  NOTE AGAIN: this is
1469          * ONLY if the -width argument has NOT been provided.  See
1470          * post_bl_block_width() for converting the -width string.
1471          */
1472
1473         sz = 10;
1474         n = mdoc->last;
1475
1476         for (nn = n->body->child; nn; nn = nn->next) {
1477                 if (MDOC_It != nn->tok)
1478                         continue;
1479
1480                 assert(MDOC_BLOCK == nn->type);
1481                 nn = nn->head->child;
1482
1483                 if (nn == NULL)
1484                         break;
1485
1486                 if (MDOC_TEXT == nn->type) {
1487                         sz = strlen(nn->string) + 1;
1488                         break;
1489                 }
1490
1491                 if (0 != (ssz = macro2len(nn->tok)))
1492                         sz = ssz;
1493
1494                 break;
1495         } 
1496
1497         /* Defaults to ten ens. */
1498
1499         snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1500
1501         /*
1502          * We have to dynamically add this to the macro's argument list.
1503          * We're guaranteed that a MDOC_Width doesn't already exist.
1504          */
1505
1506         assert(n->args);
1507         i = (int)(n->args->argc)++;
1508
1509         n->args->argv = mandoc_realloc(n->args->argv, 
1510                         n->args->argc * sizeof(struct mdoc_argv));
1511
1512         n->args->argv[i].arg = MDOC_Width;
1513         n->args->argv[i].line = n->line;
1514         n->args->argv[i].pos = n->pos;
1515         n->args->argv[i].sz = 1;
1516         n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1517         n->args->argv[i].value[0] = mandoc_strdup(buf);
1518
1519         /* Set our width! */
1520         n->norm->Bl.width = n->args->argv[i].value[0];
1521         return(1);
1522 }
1523
1524
1525 static int
1526 post_bl_head(POST_ARGS) 
1527 {
1528         struct mdoc_node *np, *nn, *nnp;
1529         int               i, j;
1530
1531         if (LIST_column != mdoc->last->norm->Bl.type)
1532                 /* FIXME: this should be ERROR class... */
1533                 return(hwarn_eq0(mdoc));
1534
1535         /*
1536          * Convert old-style lists, where the column width specifiers
1537          * trail as macro parameters, to the new-style ("normal-form")
1538          * lists where they're argument values following -column.
1539          */
1540
1541         /* First, disallow both types and allow normal-form. */
1542
1543         /* 
1544          * TODO: technically, we can accept both and just merge the two
1545          * lists, but I'll leave that for another day.
1546          */
1547
1548         if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1549                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1550                 return(0);
1551         } else if (NULL == mdoc->last->child)
1552                 return(1);
1553
1554         np = mdoc->last->parent;
1555         assert(np->args);
1556
1557         for (j = 0; j < (int)np->args->argc; j++) 
1558                 if (MDOC_Column == np->args->argv[j].arg)
1559                         break;
1560
1561         assert(j < (int)np->args->argc);
1562         assert(0 == np->args->argv[j].sz);
1563
1564         /*
1565          * Accommodate for new-style groff column syntax.  Shuffle the
1566          * child nodes, all of which must be TEXT, as arguments for the
1567          * column field.  Then, delete the head children.
1568          */
1569
1570         np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1571         np->args->argv[j].value = mandoc_malloc
1572                 ((size_t)mdoc->last->nchild * sizeof(char *));
1573
1574         mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1575         mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1576
1577         for (i = 0, nn = mdoc->last->child; nn; i++) {
1578                 np->args->argv[j].value[i] = nn->string;
1579                 nn->string = NULL;
1580                 nnp = nn;
1581                 nn = nn->next;
1582                 mdoc_node_delete(NULL, nnp);
1583         }
1584
1585         mdoc->last->nchild = 0;
1586         mdoc->last->child = NULL;
1587
1588         return(1);
1589 }
1590
1591 static int
1592 post_bl(POST_ARGS)
1593 {
1594         struct mdoc_node        *nparent, *nprev; /* of the Bl block */
1595         struct mdoc_node        *nblock, *nbody;  /* of the Bl */
1596         struct mdoc_node        *nchild, *nnext;  /* of the Bl body */
1597
1598         nbody = mdoc->last;
1599         switch (nbody->type) {
1600         case (MDOC_BLOCK):
1601                 return(post_bl_block(mdoc));
1602         case (MDOC_HEAD):
1603                 return(post_bl_head(mdoc));
1604         case (MDOC_BODY):
1605                 break;
1606         default:
1607                 return(1);
1608         }
1609
1610         nchild = nbody->child;
1611         while (NULL != nchild) {
1612                 if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1613                         nchild = nchild->next;
1614                         continue;
1615                 }
1616
1617                 mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
1618
1619                 /*
1620                  * Move the node out of the Bl block.
1621                  * First, collect all required node pointers.
1622                  */
1623
1624                 nblock  = nbody->parent;
1625                 nprev   = nblock->prev;
1626                 nparent = nblock->parent;
1627                 nnext   = nchild->next;
1628
1629                 /*
1630                  * Unlink this child.
1631                  */
1632
1633                 assert(NULL == nchild->prev);
1634                 if (0 == --nbody->nchild) {
1635                         nbody->child = NULL;
1636                         nbody->last  = NULL;
1637                         assert(NULL == nnext);
1638                 } else {
1639                         nbody->child = nnext;
1640                         nnext->prev = NULL;
1641                 }
1642
1643                 /*
1644                  * Relink this child.
1645                  */
1646
1647                 nchild->parent = nparent;
1648                 nchild->prev   = nprev;
1649                 nchild->next   = nblock;
1650
1651                 nblock->prev = nchild;
1652                 nparent->nchild++;
1653                 if (NULL == nprev)
1654                         nparent->child = nchild;
1655                 else
1656                         nprev->next = nchild;
1657
1658                 nchild = nnext;
1659         }
1660
1661         return(1);
1662 }
1663
1664 static int
1665 ebool(struct mdoc *mdoc)
1666 {
1667
1668         if (NULL == mdoc->last->child) {
1669                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1670                 mdoc_node_delete(mdoc, mdoc->last);
1671                 return(1);
1672         }
1673         check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1674
1675         assert(MDOC_TEXT == mdoc->last->child->type);
1676
1677         if (0 == strcmp(mdoc->last->child->string, "on")) {
1678                 if (MDOC_Sm == mdoc->last->tok)
1679                         mdoc->flags &= ~MDOC_SMOFF;
1680                 return(1);
1681         }
1682         if (0 == strcmp(mdoc->last->child->string, "off")) {
1683                 if (MDOC_Sm == mdoc->last->tok)
1684                         mdoc->flags |= MDOC_SMOFF;
1685                 return(1);
1686         }
1687
1688         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1689         return(1);
1690 }
1691
1692 static int
1693 post_root(POST_ARGS)
1694 {
1695         int               erc;
1696         struct mdoc_node *n;
1697
1698         erc = 0;
1699
1700         /* Check that we have a finished prologue. */
1701
1702         if ( ! (MDOC_PBODY & mdoc->flags)) {
1703                 erc++;
1704                 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1705         }
1706
1707         n = mdoc->first;
1708         assert(n);
1709         
1710         /* Check that we begin with a proper `Sh'. */
1711
1712         if (NULL == n->child) {
1713                 erc++;
1714                 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1715         } else if (MDOC_BLOCK != n->child->type || 
1716                         MDOC_Sh != n->child->tok) {
1717                 erc++;
1718                 /* Can this be lifted?  See rxdebug.1 for example. */
1719                 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1720         }
1721
1722         return(erc ? 0 : 1);
1723 }
1724
1725 static int
1726 post_st(POST_ARGS)
1727 {
1728         struct mdoc_node         *ch;
1729         const char               *p;
1730
1731         if (NULL == (ch = mdoc->last->child)) {
1732                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1733                 mdoc_node_delete(mdoc, mdoc->last);
1734                 return(1);
1735         }
1736
1737         assert(MDOC_TEXT == ch->type);
1738
1739         if (NULL == (p = mdoc_a2st(ch->string))) {
1740                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1741                 mdoc_node_delete(mdoc, mdoc->last);
1742         } else {
1743                 free(ch->string);
1744                 ch->string = mandoc_strdup(p);
1745         }
1746
1747         return(1);
1748 }
1749
1750 static int
1751 post_rs(POST_ARGS)
1752 {
1753         struct mdoc_node *nn, *next, *prev;
1754         int               i, j;
1755
1756         switch (mdoc->last->type) {
1757         case (MDOC_HEAD):
1758                 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1759                 return(1);
1760         case (MDOC_BODY):
1761                 if (mdoc->last->child)
1762                         break;
1763                 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1764                 return(1);
1765         default:
1766                 return(1);
1767         }
1768
1769         /*
1770          * Make sure only certain types of nodes are allowed within the
1771          * the `Rs' body.  Delete offending nodes and raise a warning.
1772          * Do this before re-ordering for the sake of clarity.
1773          */
1774
1775         next = NULL;
1776         for (nn = mdoc->last->child; nn; nn = next) {
1777                 for (i = 0; i < RSORD_MAX; i++)
1778                         if (nn->tok == rsord[i])
1779                                 break;
1780
1781                 if (i < RSORD_MAX) {
1782                         if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1783                                 mdoc->last->norm->Rs.quote_T++;
1784                         next = nn->next;
1785                         continue;
1786                 }
1787
1788                 next = nn->next;
1789                 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1790                 mdoc_node_delete(mdoc, nn);
1791         }
1792
1793         /*
1794          * Nothing to sort if only invalid nodes were found
1795          * inside the `Rs' body.
1796          */
1797
1798         if (NULL == mdoc->last->child)
1799                 return(1);
1800
1801         /*
1802          * The full `Rs' block needs special handling to order the
1803          * sub-elements according to `rsord'.  Pick through each element
1804          * and correctly order it.  This is a insertion sort.
1805          */
1806
1807         next = NULL;
1808         for (nn = mdoc->last->child->next; nn; nn = next) {
1809                 /* Determine order of `nn'. */
1810                 for (i = 0; i < RSORD_MAX; i++)
1811                         if (rsord[i] == nn->tok)
1812                                 break;
1813
1814                 /* 
1815                  * Remove `nn' from the chain.  This somewhat
1816                  * repeats mdoc_node_unlink(), but since we're
1817                  * just re-ordering, there's no need for the
1818                  * full unlink process.
1819                  */
1820                 
1821                 if (NULL != (next = nn->next))
1822                         next->prev = nn->prev;
1823
1824                 if (NULL != (prev = nn->prev))
1825                         prev->next = nn->next;
1826
1827                 nn->prev = nn->next = NULL;
1828
1829                 /* 
1830                  * Scan back until we reach a node that's
1831                  * ordered before `nn'.
1832                  */
1833
1834                 for ( ; prev ; prev = prev->prev) {
1835                         /* Determine order of `prev'. */
1836                         for (j = 0; j < RSORD_MAX; j++)
1837                                 if (rsord[j] == prev->tok)
1838                                         break;
1839
1840                         if (j <= i)
1841                                 break;
1842                 }
1843
1844                 /*
1845                  * Set `nn' back into its correct place in front
1846                  * of the `prev' node.
1847                  */
1848
1849                 nn->prev = prev;
1850
1851                 if (prev) {
1852                         if (prev->next)
1853                                 prev->next->prev = nn;
1854                         nn->next = prev->next;
1855                         prev->next = nn;
1856                 } else {
1857                         mdoc->last->child->prev = nn;
1858                         nn->next = mdoc->last->child;
1859                         mdoc->last->child = nn;
1860                 }
1861         }
1862
1863         return(1);
1864 }
1865
1866 /*
1867  * For some arguments of some macros,
1868  * convert all breakable hyphens into ASCII_HYPH.
1869  */
1870 static int
1871 post_hyph(POST_ARGS)
1872 {
1873         struct mdoc_node        *n, *nch;
1874         char                    *cp;
1875
1876         n = mdoc->last;
1877         switch (n->type) {
1878         case (MDOC_HEAD):
1879                 if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
1880                         break;
1881                 return(1);
1882         case (MDOC_BODY):
1883                 if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
1884                         break;
1885                 return(1);
1886         case (MDOC_ELEM):
1887                 break;
1888         default:
1889                 return(1);
1890         }
1891
1892         for (nch = n->child; nch; nch = nch->next) {
1893                 if (MDOC_TEXT != nch->type)
1894                         continue;
1895                 cp = nch->string;
1896                 if (3 > strnlen(cp, 3))
1897                         continue;
1898                 while ('\0' != *(++cp))
1899                         if ('-' == *cp &&
1900                             isalpha((unsigned char)cp[-1]) &&
1901                             isalpha((unsigned char)cp[1]))
1902                                 *cp = ASCII_HYPH;
1903         }
1904         return(1);
1905 }
1906
1907 static int
1908 post_ns(POST_ARGS)
1909 {
1910
1911         if (MDOC_LINE & mdoc->last->flags)
1912                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1913         return(1);
1914 }
1915
1916 static int
1917 post_sh(POST_ARGS)
1918 {
1919
1920         if (MDOC_HEAD == mdoc->last->type)
1921                 return(post_sh_head(mdoc));
1922         if (MDOC_BODY == mdoc->last->type)
1923                 return(post_sh_body(mdoc));
1924
1925         return(1);
1926 }
1927
1928 static int
1929 post_sh_body(POST_ARGS)
1930 {
1931         struct mdoc_node *n;
1932
1933         if (SEC_NAME != mdoc->lastsec)
1934                 return(1);
1935
1936         /*
1937          * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1938          * macros (can have multiple `Nm' and one `Nd').  Note that the
1939          * children of the BODY declaration can also be "text".
1940          */
1941
1942         if (NULL == (n = mdoc->last->child)) {
1943                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1944                 return(1);
1945         }
1946
1947         for ( ; n && n->next; n = n->next) {
1948                 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1949                         continue;
1950                 if (MDOC_TEXT == n->type)
1951                         continue;
1952                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1953         }
1954
1955         assert(n);
1956         if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1957                 return(1);
1958
1959         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1960         return(1);
1961 }
1962
1963 static int
1964 post_sh_head(POST_ARGS)
1965 {
1966         char             buf[BUFSIZ];
1967         struct mdoc_node *n;
1968         enum mdoc_sec    sec;
1969         int              c;
1970
1971         /*
1972          * Process a new section.  Sections are either "named" or
1973          * "custom".  Custom sections are user-defined, while named ones
1974          * follow a conventional order and may only appear in certain
1975          * manual sections.
1976          */
1977
1978         sec = SEC_CUSTOM;
1979         buf[0] = '\0';
1980         if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1981                 mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1982                 return(0);
1983         } else if (1 == c)
1984                 sec = a2sec(buf);
1985
1986         /* The NAME should be first. */
1987
1988         if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1989                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1990
1991         /* The SYNOPSIS gets special attention in other areas. */
1992
1993         if (SEC_SYNOPSIS == sec) {
1994                 roff_setreg(mdoc->roff, "nS", 1, '=');
1995                 mdoc->flags |= MDOC_SYNOPSIS;
1996         } else {
1997                 roff_setreg(mdoc->roff, "nS", 0, '=');
1998                 mdoc->flags &= ~MDOC_SYNOPSIS;
1999         }
2000
2001         /* Mark our last section. */
2002
2003         mdoc->lastsec = sec;
2004
2005         /*
2006          * Set the section attribute for the current HEAD, for its
2007          * parent BLOCK, and for the HEAD children; the latter can
2008          * only be TEXT nodes, so no recursion is needed.
2009          * For other blocks and elements, including .Sh BODY, this is
2010          * done when allocating the node data structures, but for .Sh
2011          * BLOCK and HEAD, the section is still unknown at that time.
2012          */
2013
2014         mdoc->last->parent->sec = sec;
2015         mdoc->last->sec = sec;
2016         for (n = mdoc->last->child; n; n = n->next)
2017                 n->sec = sec;
2018
2019         /* We don't care about custom sections after this. */
2020
2021         if (SEC_CUSTOM == sec)
2022                 return(1);
2023
2024         /*
2025          * Check whether our non-custom section is being repeated or is
2026          * out of order.
2027          */
2028
2029         if (sec == mdoc->lastnamed)
2030                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
2031
2032         if (sec < mdoc->lastnamed)
2033                 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
2034
2035         /* Mark the last named section. */
2036
2037         mdoc->lastnamed = sec;
2038
2039         /* Check particular section/manual conventions. */
2040
2041         assert(mdoc->meta.msec);
2042
2043         switch (sec) {
2044         case (SEC_RETURN_VALUES):
2045                 /* FALLTHROUGH */
2046         case (SEC_ERRORS):
2047                 /* FALLTHROUGH */
2048         case (SEC_LIBRARY):
2049                 if (*mdoc->meta.msec == '2')
2050                         break;
2051                 if (*mdoc->meta.msec == '3')
2052                         break;
2053                 if (*mdoc->meta.msec == '9')
2054                         break;
2055                 mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse,
2056                                 mdoc->last->line, mdoc->last->pos, buf);
2057                 break;
2058         default:
2059                 break;
2060         }
2061
2062         return(1);
2063 }
2064
2065 static int
2066 post_ignpar(POST_ARGS)
2067 {
2068         struct mdoc_node *np;
2069
2070         if (MDOC_BODY != mdoc->last->type)
2071                 return(1);
2072
2073         if (NULL != (np = mdoc->last->child))
2074                 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2075                         mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2076                         mdoc_node_delete(mdoc, np);
2077                 }
2078
2079         if (NULL != (np = mdoc->last->last))
2080                 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2081                         mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2082                         mdoc_node_delete(mdoc, np);
2083                 }
2084
2085         return(1);
2086 }
2087
2088 static int
2089 pre_par(PRE_ARGS)
2090 {
2091
2092         if (NULL == mdoc->last)
2093                 return(1);
2094         if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2095                 return(1);
2096
2097         /* 
2098          * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2099          * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2100          */
2101
2102         if (MDOC_Pp != mdoc->last->tok &&
2103             MDOC_Lp != mdoc->last->tok &&
2104             MDOC_br != mdoc->last->tok)
2105                 return(1);
2106         if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2107                 return(1);
2108         if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2109                 return(1);
2110         if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2111                 return(1);
2112
2113         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2114         mdoc_node_delete(mdoc, mdoc->last);
2115         return(1);
2116 }
2117
2118 static int
2119 post_par(POST_ARGS)
2120 {
2121
2122         if (MDOC_ELEM != mdoc->last->type &&
2123             MDOC_BLOCK != mdoc->last->type)
2124                 return(1);
2125
2126         if (NULL == mdoc->last->prev) {
2127                 if (MDOC_Sh != mdoc->last->parent->tok &&
2128                     MDOC_Ss != mdoc->last->parent->tok)
2129                         return(1);
2130         } else {
2131                 if (MDOC_Pp != mdoc->last->prev->tok &&
2132                     MDOC_Lp != mdoc->last->prev->tok &&
2133                     (MDOC_br != mdoc->last->tok ||
2134                      (MDOC_sp != mdoc->last->prev->tok &&
2135                       MDOC_br != mdoc->last->prev->tok)))
2136                         return(1);
2137         }
2138
2139         mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2140         mdoc_node_delete(mdoc, mdoc->last);
2141         return(1);
2142 }
2143
2144 static int
2145 pre_literal(PRE_ARGS)
2146 {
2147
2148         if (MDOC_BODY != n->type)
2149                 return(1);
2150
2151         /*
2152          * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2153          * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2154          */
2155
2156         switch (n->tok) {
2157         case (MDOC_Dl):
2158                 mdoc->flags |= MDOC_LITERAL;
2159                 break;
2160         case (MDOC_Bd):
2161                 if (DISP_literal == n->norm->Bd.type)
2162                         mdoc->flags |= MDOC_LITERAL;
2163                 if (DISP_unfilled == n->norm->Bd.type)
2164                         mdoc->flags |= MDOC_LITERAL;
2165                 break;
2166         default:
2167                 abort();
2168                 /* NOTREACHED */
2169         }
2170         
2171         return(1);
2172 }
2173
2174 static int
2175 post_dd(POST_ARGS)
2176 {
2177         char              buf[DATESIZE];
2178         struct mdoc_node *n;
2179         int               c;
2180
2181         if (mdoc->meta.date)
2182                 free(mdoc->meta.date);
2183
2184         n = mdoc->last;
2185         if (NULL == n->child || '\0' == n->child->string[0]) {
2186                 mdoc->meta.date = mandoc_normdate
2187                         (mdoc->parse, NULL, n->line, n->pos);
2188                 return(1);
2189         }
2190
2191         buf[0] = '\0';
2192         if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2193                 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2194                 return(0);
2195         }
2196
2197         assert(c);
2198         mdoc->meta.date = mandoc_normdate
2199                 (mdoc->parse, buf, n->line, n->pos);
2200
2201         return(1);
2202 }
2203
2204 static int
2205 post_dt(POST_ARGS)
2206 {
2207         struct mdoc_node *nn, *n;
2208         const char       *cp;
2209         char             *p;
2210
2211         n = mdoc->last;
2212
2213         if (mdoc->meta.title)
2214                 free(mdoc->meta.title);
2215         if (mdoc->meta.vol)
2216                 free(mdoc->meta.vol);
2217         if (mdoc->meta.arch)
2218                 free(mdoc->meta.arch);
2219
2220         mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2221
2222         /* First make all characters uppercase. */
2223
2224         if (NULL != (nn = n->child))
2225                 for (p = nn->string; *p; p++) {
2226                         if (toupper((unsigned char)*p) == *p)
2227                                 continue;
2228
2229                         /* 
2230                          * FIXME: don't be lazy: have this make all
2231                          * characters be uppercase and just warn once.
2232                          */
2233                         mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2234                         break;
2235                 }
2236
2237         /* Handles: `.Dt' 
2238          *   --> title = unknown, volume = local, msec = 0, arch = NULL
2239          */
2240
2241         if (NULL == (nn = n->child)) {
2242                 /* XXX: make these macro values. */
2243                 /* FIXME: warn about missing values. */
2244                 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2245                 mdoc->meta.vol = mandoc_strdup("LOCAL");
2246                 mdoc->meta.msec = mandoc_strdup("1");
2247                 return(1);
2248         }
2249
2250         /* Handles: `.Dt TITLE' 
2251          *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2252          */
2253
2254         mdoc->meta.title = mandoc_strdup
2255                 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2256
2257         if (NULL == (nn = nn->next)) {
2258                 /* FIXME: warn about missing msec. */
2259                 /* XXX: make this a macro value. */
2260                 mdoc->meta.vol = mandoc_strdup("LOCAL");
2261                 mdoc->meta.msec = mandoc_strdup("1");
2262                 return(1);
2263         }
2264
2265         /* Handles: `.Dt TITLE SEC'
2266          *   --> title = TITLE, volume = SEC is msec ? 
2267          *           format(msec) : SEC,
2268          *       msec = SEC is msec ? atoi(msec) : 0,
2269          *       arch = NULL
2270          */
2271
2272         cp = mandoc_a2msec(nn->string);
2273         if (cp) {
2274                 mdoc->meta.vol = mandoc_strdup(cp);
2275                 mdoc->meta.msec = mandoc_strdup(nn->string);
2276         } else {
2277                 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2278                 mdoc->meta.vol = mandoc_strdup(nn->string);
2279                 mdoc->meta.msec = mandoc_strdup(nn->string);
2280         } 
2281
2282         if (NULL == (nn = nn->next))
2283                 return(1);
2284
2285         /* Handles: `.Dt TITLE SEC VOL'
2286          *   --> title = TITLE, volume = VOL is vol ?
2287          *       format(VOL) : 
2288          *           VOL is arch ? format(arch) : 
2289          *               VOL
2290          */
2291
2292         cp = mdoc_a2vol(nn->string);
2293         if (cp) {
2294                 free(mdoc->meta.vol);
2295                 mdoc->meta.vol = mandoc_strdup(cp);
2296         } else {
2297                 cp = mdoc_a2arch(nn->string);
2298                 if (NULL == cp) {
2299                         mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
2300                         free(mdoc->meta.vol);
2301                         mdoc->meta.vol = mandoc_strdup(nn->string);
2302                 } else 
2303                         mdoc->meta.arch = mandoc_strdup(cp);
2304         }       
2305
2306         /* Ignore any subsequent parameters... */
2307         /* FIXME: warn about subsequent parameters. */
2308
2309         return(1);
2310 }
2311
2312 static int
2313 post_prol(POST_ARGS)
2314 {
2315         /*
2316          * Remove prologue macros from the document after they're
2317          * processed.  The final document uses mdoc_meta for these
2318          * values and discards the originals.
2319          */
2320
2321         mdoc_node_delete(mdoc, mdoc->last);
2322         if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2323                 mdoc->flags |= MDOC_PBODY;
2324
2325         return(1);
2326 }
2327
2328 static int
2329 post_bx(POST_ARGS)
2330 {
2331         struct mdoc_node        *n;
2332
2333         /* 
2334          * Make `Bx's second argument always start with an uppercase
2335          * letter.  Groff checks if it's an "accepted" term, but we just
2336          * uppercase blindly.
2337          */
2338
2339         n = mdoc->last->child;
2340         if (n && NULL != (n = n->next))
2341                 *n->string = (char)toupper
2342                         ((unsigned char)*n->string);
2343
2344         return(1);
2345 }
2346
2347 static int
2348 post_os(POST_ARGS)
2349 {
2350         struct mdoc_node *n;
2351         char              buf[BUFSIZ];
2352         int               c;
2353 #ifndef OSNAME
2354         struct utsname    utsname;
2355 #endif
2356
2357         n = mdoc->last;
2358
2359         /*
2360          * Set the operating system by way of the `Os' macro.
2361          * The order of precedence is:
2362          * 1. the argument of the `Os' macro, unless empty
2363          * 2. the -Ios=foo command line argument, if provided
2364          * 3. -DOSNAME="\"foo\"", if provided during compilation
2365          * 4. "sysname release" from uname(3)
2366          */
2367
2368         free(mdoc->meta.os);
2369
2370         buf[0] = '\0';
2371         if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2372                 mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2373                 return(0);
2374         }
2375
2376         assert(c);
2377
2378         if ('\0' == buf[0]) {
2379                 if (mdoc->defos) {
2380                         mdoc->meta.os = mandoc_strdup(mdoc->defos);
2381                         return(1);
2382                 }
2383 #ifdef OSNAME
2384                 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2385                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2386                         return(0);
2387                 }
2388 #else /*!OSNAME */
2389                 if (-1 == uname(&utsname)) {
2390                         mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2391                         mdoc->meta.os = mandoc_strdup("UNKNOWN");
2392                         return(post_prol(mdoc));
2393                 }
2394
2395                 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2396                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2397                         return(0);
2398                 }
2399                 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2400                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2401                         return(0);
2402                 }
2403                 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2404                         mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2405                         return(0);
2406                 }
2407 #endif /*!OSNAME*/
2408         }
2409
2410         mdoc->meta.os = mandoc_strdup(buf);
2411         return(1);
2412 }
2413
2414 static int
2415 post_std(POST_ARGS)
2416 {
2417         struct mdoc_node *nn, *n;
2418
2419         n = mdoc->last;
2420
2421         /*
2422          * Macros accepting `-std' as an argument have the name of the
2423          * current document (`Nm') filled in as the argument if it's not
2424          * provided.
2425          */
2426
2427         if (n->child)
2428                 return(1);
2429
2430         if (NULL == mdoc->meta.name)
2431                 return(1);
2432         
2433         nn = n;
2434         mdoc->next = MDOC_NEXT_CHILD;
2435
2436         if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2437                 return(0);
2438
2439         mdoc->last = nn;
2440         return(1);
2441 }
2442
2443 /*
2444  * Concatenate a node, stopping at the first non-text.
2445  * Concatenation is separated by a single whitespace.  
2446  * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2447  * encountered, 1 otherwise.
2448  */
2449 static int
2450 concat(char *p, const struct mdoc_node *n, size_t sz)
2451 {
2452
2453         for ( ; NULL != n; n = n->next) {
2454                 if (MDOC_TEXT != n->type) 
2455                         return(0);
2456                 if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2457                         return(-1);
2458                 if (strlcat(p, n->string, sz) >= sz)
2459                         return(-1);
2460                 concat(p, n->child, sz);
2461         }
2462
2463         return(1);
2464 }
2465
2466 static enum mdoc_sec 
2467 a2sec(const char *p)
2468 {
2469         int              i;
2470
2471         for (i = 0; i < (int)SEC__MAX; i++) 
2472                 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2473                         return((enum mdoc_sec)i);
2474
2475         return(SEC_CUSTOM);
2476 }
2477
2478 static size_t
2479 macro2len(enum mdoct macro)
2480 {
2481
2482         switch (macro) {
2483         case(MDOC_Ad):
2484                 return(12);
2485         case(MDOC_Ao):
2486                 return(12);
2487         case(MDOC_An):
2488                 return(12);
2489         case(MDOC_Aq):
2490                 return(12);
2491         case(MDOC_Ar):
2492                 return(12);
2493         case(MDOC_Bo):
2494                 return(12);
2495         case(MDOC_Bq):
2496                 return(12);
2497         case(MDOC_Cd):
2498                 return(12);
2499         case(MDOC_Cm):
2500                 return(10);
2501         case(MDOC_Do):
2502                 return(10);
2503         case(MDOC_Dq):
2504                 return(12);
2505         case(MDOC_Dv):
2506                 return(12);
2507         case(MDOC_Eo):
2508                 return(12);
2509         case(MDOC_Em):
2510                 return(10);
2511         case(MDOC_Er):
2512                 return(17);
2513         case(MDOC_Ev):
2514                 return(15);
2515         case(MDOC_Fa):
2516                 return(12);
2517         case(MDOC_Fl):
2518                 return(10);
2519         case(MDOC_Fo):
2520                 return(16);
2521         case(MDOC_Fn):
2522                 return(16);
2523         case(MDOC_Ic):
2524                 return(10);
2525         case(MDOC_Li):
2526                 return(16);
2527         case(MDOC_Ms):
2528                 return(6);
2529         case(MDOC_Nm):
2530                 return(10);
2531         case(MDOC_No):
2532                 return(12);
2533         case(MDOC_Oo):
2534                 return(10);
2535         case(MDOC_Op):
2536                 return(14);
2537         case(MDOC_Pa):
2538                 return(32);
2539         case(MDOC_Pf):
2540                 return(12);
2541         case(MDOC_Po):
2542                 return(12);
2543         case(MDOC_Pq):
2544                 return(12);
2545         case(MDOC_Ql):
2546                 return(16);
2547         case(MDOC_Qo):
2548                 return(12);
2549         case(MDOC_So):
2550                 return(12);
2551         case(MDOC_Sq):
2552                 return(12);
2553         case(MDOC_Sy):
2554                 return(6);
2555         case(MDOC_Sx):
2556                 return(16);
2557         case(MDOC_Tn):
2558                 return(10);
2559         case(MDOC_Va):
2560                 return(12);
2561         case(MDOC_Vt):
2562                 return(12);
2563         case(MDOC_Xr):
2564                 return(10);
2565         default:
2566                 break;
2567         };
2568         return(0);
2569 }