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