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