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