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