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