[BACK]Return to roff.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Diff for /mandoc/roff.c between version 1.26 and 1.200

version 1.26, 2008/12/01 09:25:18 version 1.200, 2014/03/20 02:57:28
Line 1 
Line 1 
 /* $Id$ */  /*      $Id$ */
 /*  /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>   * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
    * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the   * purpose with or without fee is hereby granted, provided that the above
  * above copyright notice and this permission notice appear in all   * copyright notice and this permission notice appear in all copies.
  * copies.  
  *   *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * PERFORMANCE OF THIS SOFTWARE.  
  */   */
   #ifdef HAVE_CONFIG_H
   #include "config.h"
   #endif
   
 #include <assert.h>  #include <assert.h>
 #include <ctype.h>  #include <ctype.h>
 #include <err.h>  
 #include <stdarg.h>  
 #include <stdlib.h>  
 #include <stdio.h>  #include <stdio.h>
   #include <stdlib.h>
 #include <string.h>  #include <string.h>
 #include <time.h>  
   
 #include "libmdocml.h"  #include "mandoc.h"
 #include "private.h"  #include "libroff.h"
   #include "libmandoc.h"
   
 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */  /* Maximum number of nested if-else conditionals. */
   #define RSTACK_MAX      128
   
 /* FIXME: warn about "X section only" macros. */  /* Maximum number of string expansions per line, to break infinite loops. */
   #define EXPAND_LIMIT    1000
   
 /* FIXME: warn about empty lists. */  enum    rofft {
           ROFF_ad,
           ROFF_am,
           ROFF_ami,
           ROFF_am1,
           ROFF_as,
           ROFF_cc,
           ROFF_ce,
           ROFF_de,
           ROFF_dei,
           ROFF_de1,
           ROFF_ds,
           ROFF_el,
           ROFF_fam,
           ROFF_hw,
           ROFF_hy,
           ROFF_ie,
           ROFF_if,
           ROFF_ig,
           ROFF_it,
           ROFF_ne,
           ROFF_nh,
           ROFF_nr,
           ROFF_ns,
           ROFF_ps,
           ROFF_rm,
           ROFF_so,
           ROFF_ta,
           ROFF_tr,
           ROFF_Dd,
           ROFF_TH,
           ROFF_TS,
           ROFF_TE,
           ROFF_T_,
           ROFF_EQ,
           ROFF_EN,
           ROFF_cblock,
           ROFF_USERDEF,
           ROFF_MAX
   };
   
 /* FIXME: roff_layout and roff_text have identical-ish lower bodies. */  /*
    * An incredibly-simple string buffer.
    */
   struct  roffstr {
           char            *p; /* nil-terminated buffer */
           size_t           sz; /* saved strlen(p) */
   };
   
 /* FIXME: NAME section needs specific elements. */  /*
    * A key-value roffstr pair as part of a singly-linked list.
    */
   struct  roffkv {
           struct roffstr   key;
           struct roffstr   val;
           struct roffkv   *next; /* next in list */
   };
   
 #define ROFF_MAXARG       32  /*
    * A single number register as part of a singly-linked list.
    */
   struct  roffreg {
           struct roffstr   key;
           int              val;
           struct roffreg  *next;
   };
   
 enum    roffd {  struct  roff {
         ROFF_ENTER = 0,          struct mparse   *parse; /* parse point */
         ROFF_EXIT          int              options; /* parse options */
           struct roffnode *last; /* leaf of stack */
           int              rstack[RSTACK_MAX]; /* stack of !`ie' rules */
           char             control; /* control character */
           int              rstackpos; /* position in rstack */
           struct roffreg  *regtab; /* number registers */
           struct roffkv   *strtab; /* user-defined strings & macros */
           struct roffkv   *xmbtab; /* multi-byte trans table (`tr') */
           struct roffstr  *xtab; /* single-byte trans table (`tr') */
           const char      *current_string; /* value of last called user macro */
           struct tbl_node *first_tbl; /* first table parsed */
           struct tbl_node *last_tbl; /* last table parsed */
           struct tbl_node *tbl; /* current table being parsed */
           struct eqn_node *last_eqn; /* last equation parsed */
           struct eqn_node *first_eqn; /* first equation parsed */
           struct eqn_node *eqn; /* current equation being parsed */
 };  };
   
 enum    rofftype {  struct  roffnode {
         ROFF_COMMENT,          enum rofft       tok; /* type of node */
         ROFF_TEXT,          struct roffnode *parent; /* up one in stack */
         ROFF_LAYOUT,          int              line; /* parse line */
         ROFF_SPECIAL          int              col; /* parse col */
           char            *name; /* node name, e.g. macro name */
           char            *end; /* end-rules: custom token */
           int              endspan; /* end-rules: next-line or infty */
           int              rule; /* current evaluation rule */
 };  };
   
 #define ROFFCALL_ARGS \  #define ROFF_ARGS        struct roff *r, /* parse ctx */ \
         int tok, struct rofftree *tree, \                           enum rofft tok, /* tok of macro */ \
         char *argv[], enum roffd type                           char **bufp, /* input buffer */ \
                            size_t *szp, /* size of input buffer */ \
                            int ln, /* parse line */ \
                            int ppos, /* original pos in buffer */ \
                            int pos, /* current pos in buffer */ \
                            int *offs /* reset offset of buffer data */
   
 struct  rofftree;  typedef enum rofferr (*roffproc)(ROFF_ARGS);
   
 struct  rofftok {  struct  roffmac {
         int             (*cb)(ROFFCALL_ARGS);   /* Callback. */          const char      *name; /* macro name */
         const int        *args;                 /* Args (or NULL). */          roffproc         proc; /* process new macro */
         const int        *parents;          roffproc         text; /* process as child text of macro */
         const int        *children;          roffproc         sub; /* process as child of macro */
         int               ctx;          int              flags;
         enum rofftype     type;                 /* Type of macro. */  #define ROFFMAC_STRUCT  (1 << 0) /* always interpret */
         int               flags;          struct roffmac  *next;
 #define ROFF_PARSED      (1 << 0)               /* "Parsed". */  
 #define ROFF_CALLABLE    (1 << 1)               /* "Callable". */  
 #define ROFF_SHALLOW     (1 << 2)               /* Nesting block. */  
 };  };
   
 struct  roffarg {  struct  predef {
         int               flags;          const char      *name; /* predefined input name */
 #define ROFF_VALUE       (1 << 0)               /* Has a value. */          const char      *str; /* replacement symbol */
 };  };
   
 struct  roffnode {  #define PREDEF(__name, __str) \
         int               tok;                  /* Token id. */          { (__name), (__str) },
         struct roffnode  *parent;               /* Parent (or NULL). */  
 };  
   
 struct  rofftree {  static  enum rofft       roffhash_find(const char *, size_t);
         struct roffnode  *last;                 /* Last parsed node. */  static  void             roffhash_init(void);
         char             *cur;  static  void             roffnode_cleanscope(struct roff *);
   static  void             roffnode_pop(struct roff *);
   static  void             roffnode_push(struct roff *, enum rofft,
                                   const char *, int, int);
   static  enum rofferr     roff_block(ROFF_ARGS);
   static  enum rofferr     roff_block_text(ROFF_ARGS);
   static  enum rofferr     roff_block_sub(ROFF_ARGS);
   static  enum rofferr     roff_cblock(ROFF_ARGS);
   static  enum rofferr     roff_cc(ROFF_ARGS);
   static  void             roff_ccond(struct roff *, int, int);
   static  enum rofferr     roff_cond(ROFF_ARGS);
   static  enum rofferr     roff_cond_text(ROFF_ARGS);
   static  enum rofferr     roff_cond_sub(ROFF_ARGS);
   static  enum rofferr     roff_ds(ROFF_ARGS);
   static  int              roff_evalcond(const char *, int *);
   static  int              roff_evalstrcond(const char *, int *);
   static  void             roff_free1(struct roff *);
   static  void             roff_freereg(struct roffreg *);
   static  void             roff_freestr(struct roffkv *);
   static  char            *roff_getname(struct roff *, char **, int, int);
   static  int              roff_getnum(const char *, int *, int *);
   static  int              roff_getop(const char *, int *, char *);
   static  int              roff_getregn(const struct roff *,
                                   const char *, size_t);
   static  int              roff_getregro(const char *name);
   static  const char      *roff_getstrn(const struct roff *,
                                   const char *, size_t);
   static  enum rofferr     roff_it(ROFF_ARGS);
   static  enum rofferr     roff_line_ignore(ROFF_ARGS);
   static  enum rofferr     roff_nr(ROFF_ARGS);
   static  void             roff_openeqn(struct roff *, const char *,
                                   int, int, const char *);
   static  enum rofft       roff_parse(struct roff *, const char *, int *);
   static  enum rofferr     roff_parsetext(char **, size_t *, int, int *);
   static  enum rofferr     roff_res(struct roff *,
                                   char **, size_t *, int, int);
   static  enum rofferr     roff_rm(ROFF_ARGS);
   static  void             roff_setstr(struct roff *,
                                   const char *, const char *, int);
   static  void             roff_setstrn(struct roffkv **, const char *,
                                   size_t, const char *, size_t, int);
   static  enum rofferr     roff_so(ROFF_ARGS);
   static  enum rofferr     roff_tr(ROFF_ARGS);
   static  enum rofferr     roff_Dd(ROFF_ARGS);
   static  enum rofferr     roff_TH(ROFF_ARGS);
   static  enum rofferr     roff_TE(ROFF_ARGS);
   static  enum rofferr     roff_TS(ROFF_ARGS);
   static  enum rofferr     roff_EQ(ROFF_ARGS);
   static  enum rofferr     roff_EN(ROFF_ARGS);
   static  enum rofferr     roff_T_(ROFF_ARGS);
   static  enum rofferr     roff_userdef(ROFF_ARGS);
   
         time_t            date;                 /* `Dd' results. */  /* See roffhash_find() */
         char              os[64];               /* `Os' results. */  
         char              title[64];            /* `Dt' results. */  
         char              section[64];          /* `Dt' results. */  
         char              volume[64];           /* `Dt' results. */  
   
         int               state;  #define ASCII_HI         126
 #define ROFF_PRELUDE     (1 << 1)               /* In roff prelude. */  #define ASCII_LO         33
 #define ROFF_PRELUDE_Os  (1 << 2)               /* `Os' is parsed. */  #define HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
 #define ROFF_PRELUDE_Dt  (1 << 3)               /* `Dt' is parsed. */  
 #define ROFF_PRELUDE_Dd  (1 << 4)               /* `Dd' is parsed. */  
 #define ROFF_BODY        (1 << 5)               /* In roff body. */  
   
         struct roffcb     cb;  static  struct roffmac  *hash[HASHWIDTH];
         void             *arg;  
   static  struct roffmac   roffs[ROFF_MAX] = {
           { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
           { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "as", roff_ds, NULL, NULL, 0, NULL },
           { "cc", roff_cc, NULL, NULL, 0, NULL },
           { "ce", roff_line_ignore, NULL, NULL, 0, NULL },
           { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "ds", roff_ds, NULL, NULL, 0, NULL },
           { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
           { "fam", roff_line_ignore, NULL, NULL, 0, NULL },
           { "hw", roff_line_ignore, NULL, NULL, 0, NULL },
           { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
           { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
           { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
           { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
           { "it", roff_it, NULL, NULL, 0, NULL },
           { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
           { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
           { "nr", roff_nr, NULL, NULL, 0, NULL },
           { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
           { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
           { "rm", roff_rm, NULL, NULL, 0, NULL },
           { "so", roff_so, NULL, NULL, 0, NULL },
           { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
           { "tr", roff_tr, NULL, NULL, 0, NULL },
           { "Dd", roff_Dd, NULL, NULL, 0, NULL },
           { "TH", roff_TH, NULL, NULL, 0, NULL },
           { "TS", roff_TS, NULL, NULL, 0, NULL },
           { "TE", roff_TE, NULL, NULL, 0, NULL },
           { "T&", roff_T_, NULL, NULL, 0, NULL },
           { "EQ", roff_EQ, NULL, NULL, 0, NULL },
           { "EN", roff_EN, NULL, NULL, 0, NULL },
           { ".", roff_cblock, NULL, NULL, 0, NULL },
           { NULL, roff_userdef, NULL, NULL, 0, NULL },
 };  };
   
 static  int               roff_Dd(ROFFCALL_ARGS);  /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
 static  int               roff_Dt(ROFFCALL_ARGS);  const   char *const __mdoc_reserved[] = {
 static  int               roff_Os(ROFFCALL_ARGS);          "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
 #ifdef notyet          "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
 static  int               roff_Ns(ROFFCALL_ARGS);          "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
 #endif          "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
           "Dt", "Dv", "Dx", "D1",
           "Ec", "Ed", "Ef", "Ek", "El", "Em",
           "En", "Eo", "Er", "Es", "Ev", "Ex",
           "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
           "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
           "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
           "Oc", "Oo", "Op", "Os", "Ot", "Ox",
           "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
           "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
           "Sc", "Sh", "Sm", "So", "Sq",
           "Ss", "St", "Sx", "Sy",
           "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
           "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
           "%P", "%Q", "%R", "%T", "%U", "%V",
           NULL
   };
   
 static  int               roff_layout(ROFFCALL_ARGS);  /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
 static  int               roff_text(ROFFCALL_ARGS);  const   char *const __man_reserved[] = {
 static  int               roff_comment(ROFFCALL_ARGS);          "AT", "B", "BI", "BR", "DT",
 static  int               roff_close(ROFFCALL_ARGS);          "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
           "LP", "OP", "P", "PD", "PP",
           "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
           "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
           NULL
   };
   
 static  struct roffnode  *roffnode_new(int, struct rofftree *);  /* Array of injected predefined strings. */
 static  void              roffnode_free(struct rofftree *);  #define PREDEFS_MAX      38
   static  const struct predef predefs[PREDEFS_MAX] = {
   #include "predefs.in"
   };
   
 static  void              roff_warn(const struct rofftree *,  /* See roffhash_find() */
                                 const char *, char *, ...);  #define ROFF_HASH(p)    (p[0] - ASCII_LO)
 static  void              roff_err(const struct rofftree *,  
                                 const char *, char *, ...);  
   
 static  int               roffscan(int, const int *);  static  int      roffit_lines;  /* number of lines to delay */
 static  int               rofffindtok(const char *);  static  char    *roffit_macro;  /* nil-terminated macro line */
 static  int               rofffindarg(const char *);  
 static  int               rofffindcallable(const char *);  
 static  int               roffargs(const struct rofftree *,  
                                 int, char *, char **);  
 static  int               roffargok(int, int);  
 static  int               roffnextopt(const struct rofftree *,  
                                 int, char ***, char **);  
 static  int               roffparse(struct rofftree *, char *);  
 static  int               textparse(const struct rofftree *, char *);  
   
   static void
   roffhash_init(void)
   {
           struct roffmac   *n;
           int               buc, i;
   
 static  const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit,          for (i = 0; i < (int)ROFF_USERDEF; i++) {
         ROFF_ARGMAX };                  assert(roffs[i].name[0] >= ASCII_LO);
 static  const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled,                  assert(roffs[i].name[0] <= ASCII_HI);
         ROFF_Literal, ROFF_File, ROFF_Offset, ROFF_Filled,  
         ROFF_Compact, ROFF_ARGMAX };  
 static  const int roffarg_Bk[] = { ROFF_Words, ROFF_ARGMAX };  
 static  const int roffarg_Ex[] = { ROFF_Std, ROFF_ARGMAX };  
 static  const int roffarg_Rv[] = { ROFF_Std, ROFF_ARGMAX };  
 static  const int roffarg_Bl[] = { ROFF_Bullet, ROFF_Dash,  
         ROFF_Hyphen, ROFF_Item, ROFF_Enum, ROFF_Tag, ROFF_Diag,  
         ROFF_Hang, ROFF_Ohang, ROFF_Inset, ROFF_Column, ROFF_Offset,  
         ROFF_Width, ROFF_Compact, ROFF_ARGMAX };  
 static  const int roffarg_St[] = {  
         ROFF_p1003_1_88, ROFF_p1003_1_90, ROFF_p1003_1_96,  
         ROFF_p1003_1_2001, ROFF_p1003_1_2004, ROFF_p1003_1,  
         ROFF_p1003_1b, ROFF_p1003_1b_93, ROFF_p1003_1c_95,  
         ROFF_p1003_1g_2000, ROFF_p1003_2_92, ROFF_p1387_2_95,  
         ROFF_p1003_2, ROFF_p1387_2, ROFF_isoC_90, ROFF_isoC_amd1,  
         ROFF_isoC_tcor1, ROFF_isoC_tcor2, ROFF_isoC_99, ROFF_ansiC,  
         ROFF_ansiC_89, ROFF_ansiC_99, ROFF_ieee754, ROFF_iso8802_3,  
         ROFF_xpg3, ROFF_xpg4, ROFF_xpg4_2, ROFF_xpg4_3, ROFF_xbd5,  
         ROFF_xcu5, ROFF_xsh5, ROFF_xns5, ROFF_xns5_2d2_0,  
         ROFF_xcurses4_2, ROFF_susv2, ROFF_susv3, ROFF_svid4,  
         ROFF_ARGMAX };  
   
 static  const int roffchild_Bl[] = { ROFF_It, ROFF_El, ROFF_MAX };                  buc = ROFF_HASH(roffs[i].name);
 static  const int roffchild_Fo[] = { ROFF_Fa, ROFF_Fc, ROFF_MAX };  
 static  const int roffchild_Oo[] = { ROFF_Op, ROFF_Oc, ROFF_MAX };  
 static  const int roffchild_Rs[] = { ROFF_Re, ROFF__A, ROFF__B,  
         ROFF__D, ROFF__I, ROFF__J, ROFF__N, ROFF__O, ROFF__P,  
         ROFF__R, ROFF__T, ROFF__V, ROFF_MAX };  
   
 static  const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };                  if (NULL != (n = hash[buc])) {
 static  const int roffparent_Fc[] = { ROFF_Fo, ROFF_Fa, ROFF_MAX };                          for ( ; n->next; n = n->next)
 static  const int roffparent_Oc[] = { ROFF_Oo, ROFF_Oc, ROFF_MAX };                                  /* Do nothing. */ ;
 static  const int roffparent_It[] = { ROFF_Bl, ROFF_It, ROFF_MAX };                          n->next = &roffs[i];
 static  const int roffparent_Re[] = { ROFF_Rs, ROFF_MAX };                  } else
                           hash[buc] = &roffs[i];
           }
   }
   
 /* Table of all known tokens. */  /*
 static  const struct rofftok tokens[ROFF_MAX] = {   * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
         {roff_comment, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */   * the nil-terminated string name could be found.
         {     roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */   */
         {     roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */  static enum rofft
         {     roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */  roffhash_find(const char *p, size_t s)
         { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, 0 }, /* Sh */  {
         { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, 0 }, /* Ss */          int              buc;
         {   roff_text, NULL, NULL, NULL, ROFF_Pp, ROFF_TEXT, 0 }, /* Pp */          struct roffmac  *n;
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* D1 */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Dl */  
         { roff_layout, roffarg_Bd, NULL, NULL, 0, ROFF_LAYOUT, 0 },     /* Bd */  
         {  roff_close, NULL, NULL, NULL, ROFF_Bd, ROFF_LAYOUT, 0 }, /* Ed */  
         { roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */  
         {  roff_close, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */  
         { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, ROFF_PARSED | ROFF_SHALLOW }, /* It */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */ /* FIXME */  
         {   roff_text, roffarg_An, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Cd */ /* XXX man.4 only */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ev */ /* XXX needs arg */  
         {   roff_text, roffarg_Ex, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ex */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fa */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fd */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fl */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fn */ /* XXX needs arg */ /* FIXME */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ft */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */ /* FIXME */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Op */  
         {   NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ot */ /* XXX deprecated */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pa */  
         {   roff_text, roffarg_Rv, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rv */  
         {   roff_text, roffarg_St, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* St */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Va */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Vt */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xr */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %A */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %B */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %D */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %I */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %J */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %N */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %O */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %P */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %R */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %T */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %V */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ac */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ao */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Aq */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* At */ /* XXX at most 2 args */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bc */  
         {   NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 },  /* Bf */ /* FIXME */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bo */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bq */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */  
         {        NULL, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 },  /* Db */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dc */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Do */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dq */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ec */  
         {   NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 },  /* Ef */ /* FIXME */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Em */ /* XXX needs arg */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Eo */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Fx */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ms */  
         {   NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* No */  
         {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ns */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Nx */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ox */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pc */  
         {        NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Pf */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_LAYOUT, ROFF_PARSED | ROFF_CALLABLE }, /* Po */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pq */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qc */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ql */  
         { roff_layout, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qo */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qq */  
         {  roff_close, NULL, roffparent_Re, NULL, ROFF_Rs, ROFF_LAYOUT, 0 }, /* Re */  
         { roff_layout, NULL, NULL, roffchild_Rs, 0, ROFF_LAYOUT, 0 },   /* Rs */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sc */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* So */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sq */  
         {        NULL, NULL, NULL, NULL, 0, ROFF_SPECIAL, 0 }, /* Sm */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sy */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */  
         {   roff_text, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */  
         {   NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */  
         {   NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */  
         { roff_layout, NULL, NULL, roffchild_Fo, 0, ROFF_LAYOUT, 0 }, /* Fo */  
         {  roff_close, NULL, roffparent_Fc, NULL, ROFF_Fo, ROFF_LAYOUT, 0 }, /* Fc */  
         { roff_layout, NULL, NULL, roffchild_Oo, 0, ROFF_LAYOUT, 0 }, /* Oo */  
         {  roff_close, NULL, roffparent_Oc, NULL, ROFF_Oo, ROFF_LAYOUT, 0 }, /* Oc */  
         { roff_layout, roffarg_Bk, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bk */  
         {  roff_close, NULL, NULL, NULL, ROFF_Bk, ROFF_LAYOUT, 0 }, /* Ek */  
         };  
   
 /* Table of all known token arguments. */          /*
 static  const int tokenargs[ROFF_ARGMAX] = {           * libroff has an extremely simple hashtable, for the time
         0,              0,              0,              0,           * being, which simply keys on the first character, which must
         0,              ROFF_VALUE,     ROFF_VALUE,     0,           * be printable, then walks a chain.  It works well enough until
         0,              0,              0,              0,           * optimised.
         0,              0,              0,              0,           */
         0,              0,              ROFF_VALUE,     0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         0,              0,              0,              0,  
         };  
   
 const   char *const toknamesp[ROFF_MAX] = {          if (p[0] < ASCII_LO || p[0] > ASCII_HI)
         "\\\"",         "Dd",           "Dt",           "Os",                  return(ROFF_MAX);
         "Sh",           "Ss",           "Pp",           "D1",  
         "Dl",           "Bd",           "Ed",           "Bl",  
         "El",           "It",           "Ad",           "An",  
         "Ar",           "Cd",           "Cm",           "Dv",  
         "Er",           "Ev",           "Ex",           "Fa",  
         "Fd",           "Fl",           "Fn",           "Ft",  
         "Ic",           "In",           "Li",           "Nd",  
         "Nm",           "Op",           "Ot",           "Pa",  
         "Rv",           "St",           "Va",           "Vt",  
         /* LINTED */  
         "Xr",           "\%A",          "\%B",          "\%D",  
         /* LINTED */  
         "\%I",          "\%J",          "\%N",          "\%O",  
         /* LINTED */  
         "\%P",          "\%R",          "\%T",          "\%V",  
         "Ac",           "Ao",           "Aq",           "At",  
         "Bc",           "Bf",           "Bo",           "Bq",  
         "Bsx",          "Bx",           "Db",           "Dc",  
         "Do",           "Dq",           "Ec",           "Ef",  
         "Em",           "Eo",           "Fx",           "Ms",  
         "No",           "Ns",           "Nx",           "Ox",  
         "Pc",           "Pf",           "Po",           "Pq",  
         "Qc",           "Ql",           "Qo",           "Qq",  
         "Re",           "Rs",           "Sc",           "So",  
         "Sq",           "Sm",           "Sx",           "Sy",  
         "Tn",           "Ux",           "Xc",           "Xo",  
         "Fo",           "Fc",           "Oo",           "Oc",  
         "Bk",           "Ek",  
         };  
   
 const   char *const tokargnamesp[ROFF_ARGMAX] = {          buc = ROFF_HASH(p);
         "split",                "nosplit",              "ragged",  
         "unfilled",             "literal",              "file",  
         "offset",               "bullet",               "dash",  
         "hyphen",               "item",                 "enum",  
         "tag",                  "diag",                 "hang",  
         "ohang",                "inset",                "column",  
         "width",                "compact",              "std",  
         "p1003.1-88",           "p1003.1-90",           "p1003.1-96",  
         "p1003.1-2001",         "p1003.1-2004",         "p1003.1",  
         "p1003.1b",             "p1003.1b-93",          "p1003.1c-95",  
         "p1003.1g-2000",        "p1003.2-92",           "p1387.2-95",  
         "p1003.2",              "p1387.2",              "isoC-90",  
         "isoC-amd1",            "isoC-tcor1",           "isoC-tcor2",  
         "isoC-99",              "ansiC",                "ansiC-89",  
         "ansiC-99",             "ieee754",              "iso8802-3",  
         "xpg3",                 "xpg4",                 "xpg4.2",  
         "xpg4.3",               "xbd5",                 "xcu5",  
         "xsh5",                 "xns5",                 "xns5.2d2.0",  
         "xcurses4.2",           "susv2",                "susv3",  
         "svid4",                "filled",               "words",  
         };  
   
 const   char *const *toknames = toknamesp;          if (NULL == (n = hash[buc]))
 const   char *const *tokargnames = tokargnamesp;                  return(ROFF_MAX);
           for ( ; n; n = n->next)
                   if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
                           return((enum rofft)(n - roffs));
   
           return(ROFF_MAX);
   }
   
 int  
 roff_free(struct rofftree *tree, int flush)  /*
    * Pop the current node off of the stack of roff instructions currently
    * pending.
    */
   static void
   roffnode_pop(struct roff *r)
 {  {
         int              error, t;          struct roffnode *p;
         struct roffnode *n;  
   
         error = 0;          assert(r->last);
           p = r->last;
   
         if ( ! flush)          r->last = r->last->parent;
                 goto end;          free(p->name);
           free(p->end);
           free(p);
   }
   
         error = 1;  
   
         if (ROFF_PRELUDE & tree->state) {  /*
                 roff_warn(tree, NULL, "prelude never finished");   * Push a roff node onto the instruction stack.  This must later be
                 goto end;   * removed with roffnode_pop().
         }   */
   static void
   roffnode_push(struct roff *r, enum rofft tok, const char *name,
                   int line, int col)
   {
           struct roffnode *p;
   
         for (n = tree->last; n->parent; n = n->parent) {          p = mandoc_calloc(1, sizeof(struct roffnode));
                 if (0 != tokens[n->tok].ctx)          p->tok = tok;
                         continue;          if (name)
                 roff_warn(tree, NULL, "closing explicit scope `%s'",                  p->name = mandoc_strdup(name);
                                 toknames[n->tok]);          p->parent = r->last;
                 goto end;          p->line = line;
         }          p->col = col;
           p->rule = p->parent ? p->parent->rule : 0;
   
         while (tree->last) {          r->last = p;
                 t = tree->last->tok;  }
                 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))  
                         goto end;  
         }  
   
         error = 0;  
   
 end:  static void
   roff_free1(struct roff *r)
   {
           struct tbl_node *tbl;
           struct eqn_node *e;
           int              i;
   
         while (tree->last)          while (NULL != (tbl = r->first_tbl)) {
                 roffnode_free(tree);                  r->first_tbl = tbl->next;
                   tbl_free(tbl);
           }
   
         free(tree);          r->first_tbl = r->last_tbl = r->tbl = NULL;
   
         return(error ? 0 : 1);          while (NULL != (e = r->first_eqn)) {
 }                  r->first_eqn = e->next;
                   eqn_free(e);
           }
   
           r->first_eqn = r->last_eqn = r->eqn = NULL;
   
 struct rofftree *          while (r->last)
 roff_alloc(const struct roffcb *cb, void *args)                  roffnode_pop(r);
 {  
         struct rofftree *tree;  
   
         assert(args);          roff_freestr(r->strtab);
         assert(cb);          roff_freestr(r->xmbtab);
   
         if (NULL == (tree = calloc(1, sizeof(struct rofftree))))          r->strtab = r->xmbtab = NULL;
                 err(1, "calloc");  
   
         tree->state = ROFF_PRELUDE;          roff_freereg(r->regtab);
         tree->arg = args;  
   
         (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));          r->regtab = NULL;
   
         return(tree);          if (r->xtab)
                   for (i = 0; i < 128; i++)
                           free(r->xtab[i].p);
   
           free(r->xtab);
           r->xtab = NULL;
 }  }
   
   void
 int  roff_reset(struct roff *r)
 roff_engine(struct rofftree *tree, char *buf)  
 {  {
   
         tree->cur = buf;          roff_free1(r);
         assert(buf);          r->control = 0;
   }
   
         if (0 == *buf) {  
                 roff_warn(tree, buf, "blank line");  
                 return(0);  
         } else if ('.' != *buf)  
                 return(textparse(tree, buf));  
   
         return(roffparse(tree, buf));  void
   roff_free(struct roff *r)
   {
   
           roff_free1(r);
           free(r);
 }  }
   
   
 static int  struct roff *
 textparse(const struct rofftree *tree, char *buf)  roff_alloc(struct mparse *parse, int options)
 {  {
           struct roff     *r;
   
         return((*tree->cb.roffdata)(tree->arg, 1, buf));          r = mandoc_calloc(1, sizeof(struct roff));
           r->parse = parse;
           r->options = options;
           r->rstackpos = -1;
   
           roffhash_init();
   
           return(r);
 }  }
   
   /*
 static int   * In the current line, expand user-defined strings ("\*")
 roffargs(const struct rofftree *tree,   * and references to number registers ("\n").
                 int tok, char *buf, char **argv)   * Also check the syntax of other escape sequences.
    */
   static enum rofferr
   roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
 {  {
         int              i;          char             ubuf[12]; /* buffer to print the number */
         char            *p;          const char      *stesc; /* start of an escape sequence ('\\') */
           const char      *stnam; /* start of the name, after "[(*" */
           const char      *cp;    /* end of the name, e.g. before ']' */
           const char      *res;   /* the string to be substituted */
           char            *nbuf;  /* new buffer to copy bufp to */
           size_t           nsz;   /* size of the new buffer */
           size_t           maxl;  /* expected length of the escape name */
           size_t           naml;  /* actual length of the escape name */
           int              expand_count;  /* to avoid infinite loops */
   
         assert(tok >= 0 && tok < ROFF_MAX);          expand_count = 0;
         assert('.' == *buf);  
   
         p = buf;  again:
           cp = *bufp + pos;
           while (NULL != (cp = strchr(cp, '\\'))) {
                   stesc = cp++;
   
         /* LINTED */                  /*
         for (i = 0; *buf && i < ROFF_MAXARG; i++) {                   * The second character must be an asterisk or an n.
                 if ('\"' == *buf) {                   * If it isn't, skip it anyway:  It is escaped,
                         argv[i] = ++buf;                   * so it can't start another escape sequence.
                         while (*buf && '\"' != *buf)                   */
                                 buf++;  
                         if (0 == *buf) {                  if ('\0' == *cp)
                                 roff_err(tree, argv[i], "unclosed "                          return(ROFF_CONT);
                                                 "quote in argument "  
                                                 "list for `%s'",                  switch (*cp) {
                                                 toknames[tok]);                  case ('*'):
                                 return(0);                          res = NULL;
                         }                          break;
                 } else {                  case ('n'):
                         argv[i] = buf++;                          res = ubuf;
                         while (*buf && ! isspace(*buf))                          break;
                                 buf++;                  default:
                         if (0 == *buf)                          if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL))
                                 continue;                                  continue;
                           mandoc_msg
                                   (MANDOCERR_BADESCAPE, r->parse,
                                    ln, (int)(stesc - *bufp), NULL);
                           return(ROFF_CONT);
                 }                  }
                 *buf++ = 0;  
                 while (*buf && isspace(*buf))  
                         buf++;  
         }  
   
         assert(i > 0);  
         if (ROFF_MAXARG == i && *buf) {  
                 roff_err(tree, p, "too many arguments for `%s'", toknames  
                                 [tok]);  
                 return(0);  
         }  
   
         argv[i] = NULL;                  cp++;
         return(1);  
 }  
   
                   /*
                    * The third character decides the length
                    * of the name of the string or register.
                    * Save a pointer to the name.
                    */
   
 static int                  switch (*cp) {
 roffscan(int tok, const int *tokv)                  case ('\0'):
 {                          return(ROFF_CONT);
                   case ('('):
                           cp++;
                           maxl = 2;
                           break;
                   case ('['):
                           cp++;
                           maxl = 0;
                           break;
                   default:
                           maxl = 1;
                           break;
                   }
                   stnam = cp;
   
         if (NULL == tokv)                  /* Advance to the end of the name. */
                 return(1);  
   
         for ( ; ROFF_MAX != *tokv; tokv++)                  for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
                 if (tok == *tokv)                          if ('\0' == *cp) {
                         return(1);                                  mandoc_msg
                                           (MANDOCERR_BADESCAPE,
                                            r->parse, ln,
                                            (int)(stesc - *bufp), NULL);
                                   return(ROFF_CONT);
                           }
                           if (0 == maxl && ']' == *cp)
                                   break;
                   }
   
         return(0);                  /*
 }                   * Retrieve the replacement string; if it is
                    * undefined, resume searching for escapes.
                    */
   
                   if (NULL == res)
                           res = roff_getstrn(r, stnam, naml);
                   else
                           snprintf(ubuf, sizeof(ubuf), "%d",
                               roff_getregn(r, stnam, naml));
   
 static int                  if (NULL == res) {
 roffparse(struct rofftree *tree, char *buf)                          mandoc_msg
 {                                  (MANDOCERR_BADESCAPE, r->parse,
         int               tok, t;                                   ln, (int)(stesc - *bufp), NULL);
         struct roffnode  *n;                          res = "";
         char             *argv[ROFF_MAXARG];                  }
         char            **argvp;  
   
         if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))                  /* Replace the escape sequence by the string. */
                 if (0 == strncmp(buf, ".\\\"", 3))  
                         return(1);  
   
         if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {                  pos = stesc - *bufp;
                 roff_err(tree, buf + 1, "bogus line macro");  
                 return(0);  
         } else if (NULL == tokens[tok].cb) {  
                 roff_err(tree, buf + 1, "unsupported macro `%s'",  
                                 toknames[tok]);  
                 return(0);  
         }  
   
         assert(ROFF___ != tok);                  nsz = *szp + strlen(res) + 1;
         if ( ! roffargs(tree, tok, buf, argv))                  nbuf = mandoc_malloc(nsz);
                 return(0);  
   
         argvp = (char **)argv;                  strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1));
                   strlcat(nbuf, res, nsz);
                   strlcat(nbuf, cp + (maxl ? 0 : 1), nsz);
   
         /*                  free(*bufp);
          * Prelude macros break some assumptions, so branch now.  
          */  
   
         if (ROFF_PRELUDE & tree->state) {  
                 assert(NULL == tree->last);  
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));  
         } else  
                 assert(tree->last);  
   
         assert(ROFF_BODY & tree->state);                  *bufp = nbuf;
                   *szp = nsz;
   
         /*                  if (EXPAND_LIMIT >= ++expand_count)
          * First check that our possible parents and parent's possible                          goto again;
          * children are satisfied.  
          */  
   
         if ( ! roffscan(tree->last->tok, tokens[tok].parents)) {                  /* Just leave the string unexpanded. */
                 roff_err(tree, *argvp, "`%s' has invalid parent `%s'",                  mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL);
                                 toknames[tok],                  return(ROFF_IGN);
                                 toknames[tree->last->tok]);  
                 return(0);  
         }  
   
         if ( ! roffscan(tok, tokens[tree->last->tok].children)) {  
                 roff_err(tree, *argvp, "`%s' is invalid child of `%s'",  
                                 toknames[tok],  
                                 toknames[tree->last->tok]);  
                 return(0);  
         }          }
           return(ROFF_CONT);
   }
   
         /*  /*
          * Branch if we're not a layout token.   * Process text streams:
          */   * Convert all breakable hyphens into ASCII_HYPH.
    * Decrement and spring input line trap.
    */
   static enum rofferr
   roff_parsetext(char **bufp, size_t *szp, int pos, int *offs)
   {
           size_t           sz;
           const char      *start;
           char            *p;
           int              isz;
           enum mandoc_esc  esc;
   
         if (ROFF_LAYOUT != tokens[tok].type)          start = p = *bufp + pos;
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));  
   
         /*          while ('\0' != *p) {
          * Check our scope rules.                  sz = strcspn(p, "-\\");
          */                  p += sz;
   
         if (0 == tokens[tok].ctx)                  if ('\0' == *p)
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));                          break;
   
         /*                  if ('\\' == *p) {
          * First consider implicit-end tags, like as follows:                          /* Skip over escapes. */
          *      .Sh SECTION 1                          p++;
          *      .Sh SECTION 2                          esc = mandoc_escape((const char **)&p, NULL, NULL);
          * In this, we want to close the scope of the NAME section.  If                          if (ESCAPE_ERROR == esc)
          * there's an intermediary implicit-end tag, such as                                  break;
          *      .Sh SECTION 1                          continue;
          *      .Ss Subsection 1                  } else if (p == start) {
          *      .Sh SECTION 2                          p++;
          * then it must be closed as well.                          continue;
          */                  }
   
         if (tok == tokens[tok].ctx) {                  if (isalpha((unsigned char)p[-1]) &&
                 /*                      isalpha((unsigned char)p[1]))
                  * First search up to the point where we must close.                          *p = ASCII_HYPH;
                  * If one doesn't exist, then we can open a new scope.                  p++;
                  */          }
   
                 for (n = tree->last; n; n = n->parent) {          /* Spring the input line trap. */
                         assert(0 == tokens[n->tok].ctx ||          if (1 == roffit_lines) {
                                         n->tok == tokens[n->tok].ctx);                  isz = asprintf(&p, "%s\n.%s", *bufp, roffit_macro);
                         if (n->tok == tok)                  if (-1 == isz) {
                                 break;                          perror(NULL);
                         if (ROFF_SHALLOW & tokens[tok].flags) {                          exit((int)MANDOCLEVEL_SYSERR);
                                 n = NULL;  
                                 break;  
                         }  
                 }                  }
                   free(*bufp);
                   *bufp = p;
                   *szp = isz + 1;
                   *offs = 0;
                   free(roffit_macro);
                   roffit_lines = 0;
                   return(ROFF_REPARSE);
           } else if (1 < roffit_lines)
                   --roffit_lines;
           return(ROFF_CONT);
   }
   
                 /*  enum rofferr
                  * Create a new scope, as no previous one exists to  roff_parseln(struct roff *r, int ln, char **bufp,
                  * close out.                  size_t *szp, int pos, int *offs)
                  */  {
           enum rofft       t;
           enum rofferr     e;
           int              ppos, ctl;
   
                 if (NULL == n)          /*
                         return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));           * Run the reserved-word filter only if we have some reserved
            * words to fill in.
            */
   
                 /*          e = roff_res(r, bufp, szp, ln, pos);
                  * Close out all intermediary scoped blocks, then hang          if (ROFF_IGN == e)
                  * the current scope from our predecessor's parent.                  return(e);
                  */          assert(ROFF_CONT == e);
   
                 do {          ppos = pos;
                         t = tree->last->tok;          ctl = roff_getcontrol(r, *bufp, &pos);
                         if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))  
                                 return(0);  
                 } while (t != tok);  
   
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));          /*
            * First, if a scope is open and we're not a macro, pass the
            * text through the macro's filter.  If a scope isn't open and
            * we're not a macro, just let it through.
            * Finally, if there's an equation scope open, divert it into it
            * no matter our state.
            */
   
           if (r->last && ! ctl) {
                   t = r->last->tok;
                   assert(roffs[t].text);
                   e = (*roffs[t].text)
                           (r, t, bufp, szp, ln, pos, pos, offs);
                   assert(ROFF_IGN == e || ROFF_CONT == e);
                   if (ROFF_CONT != e)
                           return(e);
         }          }
           if (r->eqn)
                   return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
           if ( ! ctl) {
                   if (r->tbl)
                           return(tbl_read(r->tbl, ln, *bufp, pos));
                   return(roff_parsetext(bufp, szp, pos, offs));
           }
   
         /*          /*
          * Now consider explicit-end tags, where we want to close back           * If a scope is open, go to the child handler for that macro,
          * to a specific tag.  Example:           * as it may want to preprocess before doing anything with it.
          *      .Bl           * Don't do so if an equation is open.
          *      .It Item.  
          *      .El  
          * In this, the `El' tag closes out the scope of `Bl'.  
          */           */
   
         assert(tree->last);          if (r->last) {
         assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);                  t = r->last->tok;
                   assert(roffs[t].sub);
                   return((*roffs[t].sub)
                                   (r, t, bufp, szp,
                                    ln, ppos, pos, offs));
           }
   
         /* LINTED */          /*
         do {           * Lastly, as we've no scope open, try to look up and execute
                 t = tree->last->tok;           * the new macro.  If no macro is found, simply return and let
                 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))           * the compilers handle it.
                         return(0);           */
         } while (t != tokens[tok].ctx);  
   
         assert(tree->last);          if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
         return(1);                  return(ROFF_CONT);
   
           assert(roffs[t].proc);
           return((*roffs[t].proc)
                           (r, t, bufp, szp,
                            ln, ppos, pos, offs));
 }  }
   
   
 static int  void
 rofffindarg(const char *name)  roff_endparse(struct roff *r)
 {  {
         size_t           i;  
   
         /* FIXME: use a table, this is slow but ok for now. */          if (r->last)
                   mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
                                   r->last->line, r->last->col, NULL);
   
         /* LINTED */          if (r->eqn) {
         for (i = 0; i < ROFF_ARGMAX; i++)                  mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
                 /* LINTED */                                  r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
                 if (0 == strcmp(name, tokargnames[i]))                  eqn_end(&r->eqn);
                         return((int)i);          }
   
         return(ROFF_ARGMAX);          if (r->tbl) {
                   mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
                                   r->tbl->line, r->tbl->pos, NULL);
                   tbl_end(&r->tbl);
           }
 }  }
   
   /*
 static int   * Parse a roff node's type from the input buffer.  This must be in the
 rofffindtok(const char *buf)   * form of ".foo xxx" in the usual way.
    */
   static enum rofft
   roff_parse(struct roff *r, const char *buf, int *pos)
 {  {
         char             token[4];          const char      *mac;
         int              i;          size_t           maclen;
           enum rofft       t;
   
         for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)          if ('\0' == buf[*pos] || '"' == buf[*pos] ||
                 token[i] = *buf;                          '\t' == buf[*pos] || ' ' == buf[*pos])
   
         if (i == 3)  
                 return(ROFF_MAX);                  return(ROFF_MAX);
   
         token[i] = 0;          /* We stop the macro parse at an escape, tab, space, or nil. */
   
         /* FIXME: use a table, this is slow but ok for now. */          mac = buf + *pos;
           maclen = strcspn(mac, " \\\t\0");
   
         /* LINTED */          t = (r->current_string = roff_getstrn(r, mac, maclen))
         for (i = 0; i < ROFF_MAX; i++)              ? ROFF_USERDEF : roffhash_find(mac, maclen);
                 /* LINTED */  
                 if (0 == strcmp(toknames[i], token))  
                         return((int)i);  
   
         return(ROFF_MAX);          *pos += (int)maclen;
 }  
   
           while (buf[*pos] && ' ' == buf[*pos])
                   (*pos)++;
   
 static int          return(t);
 roffispunct(const char *p)  }
   
   /* ARGSUSED */
   static enum rofferr
   roff_cblock(ROFF_ARGS)
 {  {
   
         if (0 == *p)          /*
                 return(0);           * A block-close `..' should only be invoked as a child of an
         if (0 != *(p + 1))           * ignore macro, otherwise raise a warning and just ignore it.
                 return(0);           */
   
         switch (*p) {          if (NULL == r->last) {
         case('{'):                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                   return(ROFF_IGN);
           }
   
           switch (r->last->tok) {
           case (ROFF_am):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case('.'):          case (ROFF_ami):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case(','):          case (ROFF_am1):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case(';'):          case (ROFF_de):
                   /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case(':'):          case (ROFF_dei):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case('?'):          case (ROFF_ig):
                 /* FALLTHROUGH */  
         case('!'):  
                 /* FALLTHROUGH */  
         case('('):  
                 /* FALLTHROUGH */  
         case(')'):  
                 /* FALLTHROUGH */  
         case('['):  
                 /* FALLTHROUGH */  
         case(']'):  
                 /* FALLTHROUGH */  
         case('}'):  
                 return(1);  
         default:  
                 break;                  break;
           default:
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                   return(ROFF_IGN);
         }          }
   
         return(0);          if ((*bufp)[pos])
                   mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
   
           roffnode_pop(r);
           roffnode_cleanscope(r);
           return(ROFF_IGN);
   
 }  }
   
   
 static int  static void
 rofffindcallable(const char *name)  roffnode_cleanscope(struct roff *r)
 {  {
         int              c;  
   
         if (ROFF_MAX == (c = rofffindtok(name)))          while (r->last) {
                 return(ROFF_MAX);                  if (--r->last->endspan != 0)
         assert(c >= 0 && c < ROFF_MAX);                          break;
         return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);                  roffnode_pop(r);
           }
 }  }
   
   
 static struct roffnode *  static void
 roffnode_new(int tokid, struct rofftree *tree)  roff_ccond(struct roff *r, int ln, int ppos)
 {  {
         struct roffnode *p;  
   
         if (NULL == (p = malloc(sizeof(struct roffnode))))  
                 err(1, "malloc");  
   
         p->tok = tokid;          if (NULL == r->last) {
         p->parent = tree->last;                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
         tree->last = p;                  return;
           }
   
         return(p);          switch (r->last->tok) {
           case (ROFF_el):
                   /* FALLTHROUGH */
           case (ROFF_ie):
                   /* FALLTHROUGH */
           case (ROFF_if):
                   break;
           default:
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                   return;
           }
   
           if (r->last->endspan > -1) {
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                   return;
           }
   
           roffnode_pop(r);
           roffnode_cleanscope(r);
           return;
 }  }
   
   
 static int  /* ARGSUSED */
 roffargok(int tokid, int argid)  static enum rofferr
   roff_block(ROFF_ARGS)
 {  {
         const int       *c;          int             sv;
           size_t          sz;
           char            *name;
   
         if (NULL == (c = tokens[tokid].args))          name = NULL;
                 return(0);  
   
         for ( ; ROFF_ARGMAX != *c; c++)          if (ROFF_ig != tok) {
                 if (argid == *c)                  if ('\0' == (*bufp)[pos]) {
                         return(1);                          mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
                           return(ROFF_IGN);
                   }
   
         return(0);                  /*
 }                   * Re-write `de1', since we don't really care about
                    * groff's strange compatibility mode, into `de'.
                    */
   
                   if (ROFF_de1 == tok)
                           tok = ROFF_de;
                   if (ROFF_de == tok)
                           name = *bufp + pos;
                   else
                           mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
                               roffs[tok].name);
   
 static void                  while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
 roffnode_free(struct rofftree *tree)                          pos++;
 {  
         struct roffnode *p;  
   
         assert(tree->last);                  while (isspace((unsigned char)(*bufp)[pos]))
                           (*bufp)[pos++] = '\0';
           }
   
         p = tree->last;          roffnode_push(r, tok, name, ln, ppos);
         tree->last = tree->last->parent;  
         free(p);  
 }  
   
           /*
            * At the beginning of a `de' macro, clear the existing string
            * with the same name, if there is one.  New content will be
            * appended from roff_block_text() in multiline mode.
            */
   
 static int          if (ROFF_de == tok)
 roffnextopt(const struct rofftree *tree, int tok,                  roff_setstr(r, name, "", 0);
                 char ***in, char **val)  
 {  
         char            *arg, **argv;  
         int              v;  
   
         *val = NULL;          if ('\0' == (*bufp)[pos])
         argv = *in;                  return(ROFF_IGN);
         assert(argv);  
   
         if (NULL == (arg = *argv))          /* If present, process the custom end-of-line marker. */
                 return(-1);  
         if ('-' != *arg)  
                 return(-1);  
   
         if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {          sv = pos;
                 roff_warn(tree, arg, "argument-like parameter `%s' to "          while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
                                 "`%s'", &arg[1], toknames[tok]);                  pos++;
                 return(-1);  
         }  
   
         if ( ! roffargok(tok, v)) {  
                 roff_warn(tree, arg, "invalid argument parameter `%s' to "  
                                 "`%s'", tokargnames[v], toknames[tok]);  
                 return(-1);  
         }  
   
         if ( ! (ROFF_VALUE & tokenargs[v]))  
                 return(v);  
   
         *in = ++argv;          /*
            * Note: groff does NOT like escape characters in the input.
            * Instead of detecting this, we're just going to let it fly and
            * to hell with it.
            */
   
         if (NULL == *argv) {          assert(pos > sv);
                 roff_err(tree, arg, "empty value of `%s' for `%s'",          sz = (size_t)(pos - sv);
                                 tokargnames[v], toknames[tok]);  
                 return(ROFF_ARGMAX);  
         }  
   
         return(v);          if (1 == sz && '.' == (*bufp)[sv])
                   return(ROFF_IGN);
   
           r->last->end = mandoc_malloc(sz + 1);
   
           memcpy(r->last->end, *bufp + sv, sz);
           r->last->end[(int)sz] = '\0';
   
           if ((*bufp)[pos])
                   mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
   
           return(ROFF_IGN);
 }  }
   
   
 /* ARGSUSED */  /* ARGSUSED */
 static  int  static enum rofferr
 roff_Dd(ROFFCALL_ARGS)  roff_block_sub(ROFF_ARGS)
 {  {
           enum rofft      t;
           int             i, j;
   
         if (ROFF_BODY & tree->state) {          /*
                 assert( ! (ROFF_PRELUDE & tree->state));           * First check whether a custom macro exists at this level.  If
                 assert(ROFF_PRELUDE_Dd & tree->state);           * it does, then check against it.  This is some of groff's
                 return(roff_text(tok, tree, argv, type));           * stranger behaviours.  If we encountered a custom end-scope
            * tag and that tag also happens to be a "real" macro, then we
            * need to try interpreting it again as a real macro.  If it's
            * not, then return ignore.  Else continue.
            */
   
           if (r->last->end) {
                   for (i = pos, j = 0; r->last->end[j]; j++, i++)
                           if ((*bufp)[i] != r->last->end[j])
                                   break;
   
                   if ('\0' == r->last->end[j] &&
                                   ('\0' == (*bufp)[i] ||
                                    ' ' == (*bufp)[i] ||
                                    '\t' == (*bufp)[i])) {
                           roffnode_pop(r);
                           roffnode_cleanscope(r);
   
                           while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
                                   i++;
   
                           pos = i;
                           if (ROFF_MAX != roff_parse(r, *bufp, &pos))
                                   return(ROFF_RERUN);
                           return(ROFF_IGN);
                   }
         }          }
   
         assert(ROFF_PRELUDE & tree->state);          /*
         assert( ! (ROFF_BODY & tree->state));           * If we have no custom end-query or lookup failed, then try
            * pulling it out of the hashtable.
            */
   
         if (ROFF_PRELUDE_Dd & tree->state) {          t = roff_parse(r, *bufp, &pos);
                 roff_err(tree, *argv, "repeated `Dd' in prelude");  
                 return(0);          /*
         } else if (ROFF_PRELUDE_Dt & tree->state) {           * Macros other than block-end are only significant
                 roff_err(tree, *argv, "out-of-order `Dd' in prelude");           * in `de' blocks; elsewhere, simply throw them away.
                 return(0);           */
           if (ROFF_cblock != t) {
                   if (ROFF_de == tok)
                           roff_setstr(r, r->last->name, *bufp + ppos, 2);
                   return(ROFF_IGN);
         }          }
   
         /* TODO: parse date. */          assert(roffs[t].proc);
           return((*roffs[t].proc)(r, t, bufp, szp,
                                   ln, ppos, pos, offs));
   }
   
         assert(NULL == tree->last);  
         tree->state |= ROFF_PRELUDE_Dd;  
   
         return(1);  /* ARGSUSED */
   static enum rofferr
   roff_block_text(ROFF_ARGS)
   {
   
           if (ROFF_de == tok)
                   roff_setstr(r, r->last->name, *bufp + pos, 2);
   
           return(ROFF_IGN);
 }  }
   
   
 /* ARGSUSED */  /* ARGSUSED */
 static  int  static enum rofferr
 roff_Dt(ROFFCALL_ARGS)  roff_cond_sub(ROFF_ARGS)
 {  {
           enum rofft       t;
           char            *ep;
           int              rr;
   
         if (ROFF_BODY & tree->state) {          rr = r->last->rule;
                 assert( ! (ROFF_PRELUDE & tree->state));          roffnode_cleanscope(r);
                 assert(ROFF_PRELUDE_Dt & tree->state);          t = roff_parse(r, *bufp, &pos);
                 return(roff_text(tok, tree, argv, type));  
         }  
   
         assert(ROFF_PRELUDE & tree->state);          /*
         assert( ! (ROFF_BODY & tree->state));           * Fully handle known macros when they are structurally
            * required or when the conditional evaluated to true.
            */
   
         if ( ! (ROFF_PRELUDE_Dd & tree->state)) {          if ((ROFF_MAX != t) &&
                 roff_err(tree, *argv, "out-of-order `Dt' in prelude");              (rr || ROFFMAC_STRUCT & roffs[t].flags)) {
                 return(0);                  assert(roffs[t].proc);
         } else if (ROFF_PRELUDE_Dt & tree->state) {                  return((*roffs[t].proc)(r, t, bufp, szp,
                 roff_err(tree, *argv, "repeated `Dt' in prelude");                                          ln, ppos, pos, offs));
                 return(0);  
         }          }
   
         /* TODO: parse date. */          /*
            * If `\}' occurs on a macro line without a preceding macro,
            * drop the line completely.
            */
   
         assert(NULL == tree->last);          ep = *bufp + pos;
         tree->state |= ROFF_PRELUDE_Dt;          if ('\\' == ep[0] && '}' == ep[1])
                   rr = 0;
   
         return(1);          /* Always check for the closing delimiter `\}'. */
   
           while (NULL != (ep = strchr(ep, '\\'))) {
                   if ('}' == *(++ep)) {
                           *ep = '&';
                           roff_ccond(r, ln, ep - *bufp - 1);
                   }
                   ++ep;
           }
           return(rr ? ROFF_CONT : ROFF_IGN);
 }  }
   
   
 /* ARGSUSED */  /* ARGSUSED */
 static  int  static enum rofferr
 roff_Os(ROFFCALL_ARGS)  roff_cond_text(ROFF_ARGS)
 {  {
           char            *ep;
           int              rr;
   
         if (ROFF_EXIT == type) {          rr = r->last->rule;
                 roffnode_free(tree);          roffnode_cleanscope(r);
                 return((*tree->cb.rofftail)(tree->arg));  
         } else if (ROFF_BODY & tree->state) {  
                 assert( ! (ROFF_PRELUDE & tree->state));  
                 assert(ROFF_PRELUDE_Os & tree->state);  
                 return(roff_text(tok, tree, argv, type));  
         }  
   
         assert(ROFF_PRELUDE & tree->state);          ep = *bufp + pos;
         if ( ! (ROFF_PRELUDE_Dt & tree->state) ||          while (NULL != (ep = strchr(ep, '\\'))) {
                         ! (ROFF_PRELUDE_Dd & tree->state)) {                  if ('}' == *(++ep)) {
                 roff_err(tree, *argv, "out-of-order `Os' in prelude");                          *ep = '&';
                 return(0);                          roff_ccond(r, ln, ep - *bufp - 1);
                   }
                   ++ep;
         }          }
           return(rr ? ROFF_CONT : ROFF_IGN);
   }
   
         /* TODO: extract OS. */  static int
   roff_getnum(const char *v, int *pos, int *res)
   {
           int p, n;
   
         tree->state |= ROFF_PRELUDE_Os;          p = *pos;
         tree->state &= ~ROFF_PRELUDE;          n = v[p] == '-';
         tree->state |= ROFF_BODY;          if (n)
                   p++;
   
         assert(NULL == tree->last);          for (*res = 0; isdigit((unsigned char)v[p]); p++)
                   *res += 10 * *res + v[p] - '0';
           if (p == *pos + n)
                   return 0;
   
         if (NULL == roffnode_new(tok, tree))          if (n)
                 return(0);                  *res = -*res;
   
         return((*tree->cb.roffhead)(tree->arg));          *pos = p;
           return 1;
 }  }
   
   
 /* ARGSUSED */  
 static int  static int
 roff_layout(ROFFCALL_ARGS)  roff_getop(const char *v, int *pos, char *res)
 {  {
         int              i, c, argcp[ROFF_MAXARG];          int e;
         char            *v, *argvp[ROFF_MAXARG];  
   
         if (ROFF_PRELUDE & tree->state) {          *res = v[*pos];
                 roff_err(tree, *argv, "`%s' disallowed in prelude",          e = v[*pos + 1] == '=';
                                 toknames[tok]);  
           switch (*res) {
           case '=':
                   break;
           case '>':
                   if (e)
                           *res = 'g';
                   break;
           case '<':
                   if (e)
                           *res = 'l';
                   break;
           default:
                 return(0);                  return(0);
         }          }
   
         if (ROFF_EXIT == type) {          *pos += 1 + e;
                 roffnode_free(tree);  
                 return((*tree->cb.roffblkout)(tree->arg, tok));  
         }  
   
         i = 0;          return(*res);
         argv++;  }
   
         while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {  /*
                 if (ROFF_ARGMAX == c)   * Evaluate a string comparison condition.
                         return(0);   * The first character is the delimiter.
    * Succeed if the string up to its second occurrence
    * matches the string up to its third occurence.
    * Advance the cursor after the third occurrence
    * or lacking that, to the end of the line.
    */
   static int
   roff_evalstrcond(const char *v, int *pos)
   {
           const char      *s1, *s2, *s3;
           int              match;
   
                 argcp[i] = c;          match = 0;
                 argvp[i] = v;          s1 = v + *pos;          /* initial delimiter */
                 i++;          s2 = s1 + 1;            /* for scanning the first string */
                 argv++;          s3 = strchr(s2, *s1);   /* for scanning the second string */
   
           if (NULL == s3)         /* found no middle delimiter */
                   goto out;
   
           while ('\0' != *++s3) {
                   if (*s2 != *s3) {  /* mismatch */
                           s3 = strchr(s3, *s1);
                           break;
                   }
                   if (*s3 == *s1) {  /* found the final delimiter */
                           match = 1;
                           break;
                   }
                   s2++;
         }          }
   
         argcp[i] = ROFF_ARGMAX;  out:
         argvp[i] = NULL;          if (NULL == s3)
                   s3 = strchr(s2, '\0');
           else
                   s3++;
           *pos = s3 - v;
           return(match);
   }
   
         if (NULL == roffnode_new(tok, tree))  static int
   roff_evalcond(const char *v, int *pos)
   {
           int      wanttrue, lh, rh;
           char     op;
   
           if ('!' == v[*pos]) {
                   wanttrue = 0;
                   (*pos)++;
           } else
                   wanttrue = 1;
   
           switch (v[*pos]) {
           case ('n'):
                   /* FALLTHROUGH */
           case ('o'):
                   (*pos)++;
                   return(wanttrue);
           case ('c'):
                   /* FALLTHROUGH */
           case ('d'):
                   /* FALLTHROUGH */
           case ('e'):
                   /* FALLTHROUGH */
           case ('r'):
                   /* FALLTHROUGH */
           case ('t'):
                   (*pos)++;
                   return(!wanttrue);
           default:
                   break;
           }
   
           if (!roff_getnum(v, pos, &lh))
                   return(roff_evalstrcond(v, pos) == wanttrue);
           if (!roff_getop(v, pos, &op))
                   return((lh > 0) == wanttrue);
           if (!roff_getnum(v, pos, &rh))
                 return(0);                  return(0);
   
         if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))          switch (op) {
           case 'g':
                   return((lh >= rh) == wanttrue);
           case 'l':
                   return((lh <= rh) == wanttrue);
           case '=':
                   return((lh == rh) == wanttrue);
           case '>':
                   return((lh > rh) == wanttrue);
           case '<':
                   return((lh < rh) == wanttrue);
           default:
                 return(0);                  return(0);
           }
   }
   
         if (NULL == *argv)  /* ARGSUSED */
                 return(1);  static enum rofferr
   roff_line_ignore(ROFF_ARGS)
   {
   
         if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))          return(ROFF_IGN);
                 return(0);  }
   
         if ( ! (ROFF_PARSED & tokens[tok].flags)) {  /* ARGSUSED */
                 i = 0;  static enum rofferr
                 while (*argv) {  roff_cond(ROFF_ARGS)
                         if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))  {
                                 return(0);  
                         i = 1;          roffnode_push(r, tok, NULL, ln, ppos);
   
           /*
            * An `.el' has no conditional body: it will consume the value
            * of the current rstack entry set in prior `ie' calls or
            * defaults to DENY.
            *
            * If we're not an `el', however, then evaluate the conditional.
            */
   
           r->last->rule = ROFF_el == tok ?
                   (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
                   roff_evalcond(*bufp, &pos);
   
           /*
            * An if-else will put the NEGATION of the current evaluated
            * conditional into the stack of rules.
            */
   
           if (ROFF_ie == tok) {
                   if (r->rstackpos == RSTACK_MAX - 1) {
                           mandoc_msg(MANDOCERR_MEM,
                                   r->parse, ln, ppos, NULL);
                           return(ROFF_ERR);
                 }                  }
                 return((*tree->cb.roffout)(tree->arg, tok));                  r->rstack[++r->rstackpos] = !r->last->rule;
         }          }
   
         i = 0;          /* If the parent has false as its rule, then so do we. */
         while (*argv) {  
                 if (ROFF_MAX == (c = rofffindcallable(*argv))) {  
                         assert(tree->arg);  
                         if ( ! (*tree->cb.roffdata)  
                                         (tree->arg, i, *argv++))  
                                 return(0);  
                         i = 1;  
                         continue;  
                 }  
   
                 if (NULL == tokens[c].cb) {          if (r->last->parent && !r->last->parent->rule)
                         roff_err(tree, *argv, "unsupported macro `%s'",                  r->last->rule = 0;
                                         toknames[c]);  
                         return(0);  
                 }  
   
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))          /*
                         return(0);           * Determine scope.
            * If there is nothing on the line after the conditional,
            * not even whitespace, use next-line scope.
            */
   
                 break;          if ('\0' == (*bufp)[pos]) {
                   r->last->endspan = 2;
                   goto out;
         }          }
   
         /*          while (' ' == (*bufp)[pos])
          * If we're the first parser (*argv == tree->cur) then purge out                  pos++;
          * any additional punctuation, should there be any remaining at  
          * the end of line.          /* An opening brace requests multiline scope. */
   
           if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
                   r->last->endspan = -1;
                   pos += 2;
                   goto out;
           }
   
           /*
            * Anything else following the conditional causes
            * single-line scope.  Warn if the scope contains
            * nothing but trailing whitespace.
          */           */
   
         if ( ! (ROFF_PARSED & tokens[tok].flags && *argv))          if ('\0' == (*bufp)[pos])
                 return((*tree->cb.roffout)(tree->arg, tok));                  mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
   
         i = 0;          r->last->endspan = 1;
         while (argv[i])  
                 i++;  
   
         assert(i > 0);  out:
         if ( ! roffispunct(argv[--i]))          *offs = pos;
                 return((*tree->cb.roffout)(tree->arg, tok));          return(ROFF_RERUN);
   }
   
         while (i >= 0 && roffispunct(argv[i]))  
                 i--;  
   
         assert(0 != i);  /* ARGSUSED */
         i++;  static enum rofferr
   roff_ds(ROFF_ARGS)
   {
           char            *name, *string;
   
         /* LINTED */          /*
         while (argv[i])           * A symbol is named by the first word following the macro
                 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))           * invocation up to a space.  Its value is anything after the
                         return(0);           * name's trailing whitespace and optional double-quote.  Thus,
            *
            *  [.ds foo "bar  "     ]
            *
            * will have `bar  "     ' as its value.
            */
   
         return((*tree->cb.roffout)(tree->arg, tok));          string = *bufp + pos;
           name = roff_getname(r, &string, ln, pos);
           if ('\0' == *name)
                   return(ROFF_IGN);
   
           /* Read past initial double-quote. */
           if ('"' == *string)
                   string++;
   
           /* The rest is the value. */
           roff_setstr(r, name, string, ROFF_as == tok);
           return(ROFF_IGN);
 }  }
   
   void
   roff_setreg(struct roff *r, const char *name, int val, char sign)
   {
           struct roffreg  *reg;
   
 /* ARGSUSED */          /* Search for an existing register with the same name. */
           reg = r->regtab;
   
           while (reg && strcmp(name, reg->key.p))
                   reg = reg->next;
   
           if (NULL == reg) {
                   /* Create a new register. */
                   reg = mandoc_malloc(sizeof(struct roffreg));
                   reg->key.p = mandoc_strdup(name);
                   reg->key.sz = strlen(name);
                   reg->val = 0;
                   reg->next = r->regtab;
                   r->regtab = reg;
           }
   
           if ('+' == sign)
                   reg->val += val;
           else if ('-' == sign)
                   reg->val -= val;
           else
                   reg->val = val;
   }
   
   /*
    * Handle some predefined read-only number registers.
    * For now, return -1 if the requested register is not predefined;
    * in case a predefined read-only register having the value -1
    * were to turn up, another special value would have to be chosen.
    */
 static int  static int
 roff_text(ROFFCALL_ARGS)  roff_getregro(const char *name)
 {  {
         int              i, j, first, c, argcp[ROFF_MAXARG];  
         char            *v, *argvp[ROFF_MAXARG];  
   
         if (ROFF_PRELUDE & tree->state) {          switch (*name) {
                 roff_err(tree, *argv, "`%s' disallowed in prelude",          case ('A'):  /* ASCII approximation mode is always off. */
                                 toknames[tok]);  
                 return(0);                  return(0);
           case ('g'):  /* Groff compatibility mode is always on. */
                   return(1);
           case ('H'):  /* Fixed horizontal resolution. */
                   return (24);
           case ('j'):  /* Always adjust left margin only. */
                   return(0);
           case ('T'):  /* Some output device is always defined. */
                   return(1);
           case ('V'):  /* Fixed vertical resolution. */
                   return (40);
           default:
                   return (-1);
         }          }
   }
   
         /* FIXME: breaks if passed from roff_layout. */  int
         first = *argv == tree->cur;  roff_getreg(const struct roff *r, const char *name)
   {
           struct roffreg  *reg;
           int              val;
   
         i = 0;          if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
         argv++;                  val = roff_getregro(name + 1);
                   if (-1 != val)
                           return (val);
           }
   
         while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {          for (reg = r->regtab; reg; reg = reg->next)
                 if (ROFF_ARGMAX == c)                  if (0 == strcmp(name, reg->key.p))
                         return(0);                          return(reg->val);
   
                 argcp[i] = c;          return(0);
                 argvp[i] = v;  }
                 i++;  
                 argv++;  static int
   roff_getregn(const struct roff *r, const char *name, size_t len)
   {
           struct roffreg  *reg;
           int              val;
   
           if ('.' == name[0] && 2 == len) {
                   val = roff_getregro(name + 1);
                   if (-1 != val)
                           return (val);
         }          }
   
         argcp[i] = ROFF_ARGMAX;          for (reg = r->regtab; reg; reg = reg->next)
         argvp[i] = NULL;                  if (len == reg->key.sz &&
                       0 == strncmp(name, reg->key.p, len))
                           return(reg->val);
   
         if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))          return(0);
                 return(0);  }
   
         if ( ! (ROFF_PARSED & tokens[tok].flags)) {  static void
                 i = 0;  roff_freereg(struct roffreg *reg)
                 while (*argv) {  {
                         if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))          struct roffreg  *old_reg;
                                 return(0);  
                         i = 1;          while (NULL != reg) {
                 }                  free(reg->key.p);
                 return((*tree->cb.roffout)(tree->arg, tok));                  old_reg = reg;
                   reg = reg->next;
                   free(old_reg);
         }          }
   }
   
         i = 0;  /* ARGSUSED */
         while (*argv) {  static enum rofferr
                 if (ROFF_MAX == (c = rofffindcallable(*argv))) {  roff_nr(ROFF_ARGS)
                         /*  {
                          * If all that remains is roff punctuation, then          const char      *key;
                          * close out our scope and return.          char            *val;
                          */          size_t           sz;
                         if (roffispunct(*argv)) {          int              iv;
                                 for (j = 0; argv[j]; j++)          char             sign;
                                         if ( ! roffispunct(argv[j]))  
                                                 break;  
                                 if (NULL == argv[j])  
                                         break;  
                                 i = 1;  
                         }  
   
                         if ( ! (*tree->cb.roffdata)  
                                         (tree->arg, i, *argv++))  
                                 return(0);  
   
                         i = 1;          val = *bufp + pos;
                         continue;          key = roff_getname(r, &val, ln, pos);
                 }  
   
                 /*          sign = *val;
                  * A sub-command has been found.  Execute it and          if ('+' == sign || '-' == sign)
                  * discontinue parsing for arguments.                  val++;
                  */  
   
                 if (NULL == tokens[c].cb) {          sz = strspn(val, "0123456789");
                         roff_err(tree, *argv, "unsupported macro `%s'",          iv = sz ? mandoc_strntoi(val, sz, 10) : 0;
                                         toknames[c]);  
                         return(0);  
                 }  
   
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))  
                         return(0);  
   
                 break;          roff_setreg(r, key, iv, sign);
   
           return(ROFF_IGN);
   }
   
   /* ARGSUSED */
   static enum rofferr
   roff_rm(ROFF_ARGS)
   {
           const char       *name;
           char             *cp;
   
           cp = *bufp + pos;
           while ('\0' != *cp) {
                   name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
                   if ('\0' != *name)
                           roff_setstr(r, name, NULL, 0);
         }          }
           return(ROFF_IGN);
   }
   
         if ( ! (*tree->cb.roffout)(tree->arg, tok))  /* ARGSUSED */
                 return(0);  static enum rofferr
   roff_it(ROFF_ARGS)
   {
           char            *cp;
           size_t           len;
           int              iv;
   
         /*          /* Parse the number of lines. */
          * If we're the first parser (*argv == tree->cur) then purge out          cp = *bufp + pos;
          * any additional punctuation, should there be any remaining at          len = strcspn(cp, " \t");
          * the end of line.          cp[len] = '\0';
          */          if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
                   mandoc_msg(MANDOCERR_NUMERIC, r->parse,
                                   ln, ppos, *bufp + 1);
                   return(ROFF_IGN);
           }
           cp += len + 1;
   
         if ( ! (first && *argv))          /* Arm the input line trap. */
                 return(1);          roffit_lines = iv;
           roffit_macro = mandoc_strdup(cp);
           return(ROFF_IGN);
   }
   
         i = 0;  /* ARGSUSED */
         while (argv[i])  static enum rofferr
                 i++;  roff_Dd(ROFF_ARGS)
   {
           const char *const       *cp;
   
         assert(i > 0);          if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options))
         if ( ! roffispunct(argv[--i]))                  for (cp = __mdoc_reserved; *cp; cp++)
                 return(1);                          roff_setstr(r, *cp, NULL, 0);
   
         while (i >= 0 && roffispunct(argv[i]))          return(ROFF_CONT);
                 i--;  }
   
         assert(0 != i);  /* ARGSUSED */
         i++;  static enum rofferr
   roff_TH(ROFF_ARGS)
   {
           const char *const       *cp;
   
         /* LINTED */          if (0 == (MPARSE_QUICK & r->options))
         while (argv[i])                  for (cp = __man_reserved; *cp; cp++)
                 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))                          roff_setstr(r, *cp, NULL, 0);
                         return(0);  
   
         return(1);          return(ROFF_CONT);
 }  }
   
   /* ARGSUSED */
   static enum rofferr
   roff_TE(ROFF_ARGS)
   {
   
           if (NULL == r->tbl)
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
           else
                   tbl_end(&r->tbl);
   
           return(ROFF_IGN);
   }
   
 /* ARGSUSED */  /* ARGSUSED */
   static enum rofferr
   roff_T_(ROFF_ARGS)
   {
   
           if (NULL == r->tbl)
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
           else
                   tbl_restart(ppos, ln, r->tbl);
   
           return(ROFF_IGN);
   }
   
   #if 0
 static int  static int
 roff_comment(ROFFCALL_ARGS)  roff_closeeqn(struct roff *r)
 {  {
   
         return(1);          return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
 }  }
   #endif
   
   static void
   roff_openeqn(struct roff *r, const char *name, int line,
                   int offs, const char *buf)
   {
           struct eqn_node *e;
           int              poff;
   
           assert(NULL == r->eqn);
           e = eqn_alloc(name, offs, line, r->parse);
   
           if (r->last_eqn)
                   r->last_eqn->next = e;
           else
                   r->first_eqn = r->last_eqn = e;
   
           r->eqn = r->last_eqn = e;
   
           if (buf) {
                   poff = 0;
                   eqn_read(&r->eqn, line, buf, offs, &poff);
           }
   }
   
 /* ARGSUSED */  /* ARGSUSED */
 static int  static enum rofferr
 roff_close(ROFFCALL_ARGS)  roff_EQ(ROFF_ARGS)
 {  {
   
         return(1);          roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
           return(ROFF_IGN);
 }  }
   
   /* ARGSUSED */
   static enum rofferr
   roff_EN(ROFF_ARGS)
   {
   
 #if notyet          mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
           return(ROFF_IGN);
   }
   
 /* ARGSUSED */  /* ARGSUSED */
 static int  static enum rofferr
 roff_Ns(ROFFCALL_ARGS)  roff_TS(ROFF_ARGS)
 {  {
         int              c;          struct tbl_node *tbl;
   
         argv++;          if (r->tbl) {
                   mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
                   tbl_end(&r->tbl);
           }
   
         if (ROFF_MAX != (c = rofffindcallable(*argv))) {          tbl = tbl_alloc(ppos, ln, r->parse);
                 if (NULL == tokens[c].cb) {  
                         roff_err(tree, *argv, "unsupported macro `%s'",          if (r->last_tbl)
                                         toknames[c]);                  r->last_tbl->next = tbl;
                         return(0);          else
                   r->first_tbl = r->last_tbl = tbl;
   
           r->tbl = r->last_tbl = tbl;
           return(ROFF_IGN);
   }
   
   /* ARGSUSED */
   static enum rofferr
   roff_cc(ROFF_ARGS)
   {
           const char      *p;
   
           p = *bufp + pos;
   
           if ('\0' == *p || '.' == (r->control = *p++))
                   r->control = 0;
   
           if ('\0' != *p)
                   mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
   
           return(ROFF_IGN);
   }
   
   /* ARGSUSED */
   static enum rofferr
   roff_tr(ROFF_ARGS)
   {
           const char      *p, *first, *second;
           size_t           fsz, ssz;
           enum mandoc_esc  esc;
   
           p = *bufp + pos;
   
           if ('\0' == *p) {
                   mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
                   return(ROFF_IGN);
           }
   
           while ('\0' != *p) {
                   fsz = ssz = 1;
   
                   first = p++;
                   if ('\\' == *first) {
                           esc = mandoc_escape(&p, NULL, NULL);
                           if (ESCAPE_ERROR == esc) {
                                   mandoc_msg
                                           (MANDOCERR_BADESCAPE, r->parse,
                                            ln, (int)(p - *bufp), NULL);
                                   return(ROFF_IGN);
                           }
                           fsz = (size_t)(p - first);
                 }                  }
                 if ( ! (*tree->cb.roffspecial)(tree->arg, tok))  
                         return(0);  
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))  
                         return(0);  
   
                 return(1);                  second = p++;
         } else if ( ! (*tree->cb.roffdata)(tree->arg, 0, *argv++))                  if ('\\' == *second) {
                 return(0);                          esc = mandoc_escape(&p, NULL, NULL);
                           if (ESCAPE_ERROR == esc) {
         while (*argv) {                                  mandoc_msg
                 if (ROFF_MAX == (c = rofffindcallable(*argv))) {                                          (MANDOCERR_BADESCAPE, r->parse,
                         assert(tree->arg);                                           ln, (int)(p - *bufp), NULL);
                         if ( ! (*tree->cb.roffdata)                                  return(ROFF_IGN);
                                         (tree->arg, 1, *argv++))                          }
                                 return(0);                          ssz = (size_t)(p - second);
                   } else if ('\0' == *second) {
                           mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
                                           ln, (int)(p - *bufp), NULL);
                           second = " ";
                           p--;
                   }
   
                   if (fsz > 1) {
                           roff_setstrn(&r->xmbtab, first,
                                           fsz, second, ssz, 0);
                         continue;                          continue;
                 }                  }
                 if (NULL == tokens[c].cb) {  
                         roff_err(tree, *argv, "unsupported macro `%s'",                  if (NULL == r->xtab)
                                         toknames[c]);                          r->xtab = mandoc_calloc
                         return(0);                                  (128, sizeof(struct roffstr));
   
                   free(r->xtab[(int)*first].p);
                   r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
                   r->xtab[(int)*first].sz = ssz;
           }
   
           return(ROFF_IGN);
   }
   
   /* ARGSUSED */
   static enum rofferr
   roff_so(ROFF_ARGS)
   {
           char *name;
   
           mandoc_msg(MANDOCERR_SO, r->parse, ln, ppos, NULL);
   
           /*
            * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
            * opening anything that's not in our cwd or anything beneath
            * it.  Thus, explicitly disallow traversing up the file-system
            * or using absolute paths.
            */
   
           name = *bufp + pos;
           if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
                   mandoc_msg(MANDOCERR_SOPATH, r->parse, ln, pos, NULL);
                   return(ROFF_ERR);
           }
   
           *offs = pos;
           return(ROFF_SO);
   }
   
   /* ARGSUSED */
   static enum rofferr
   roff_userdef(ROFF_ARGS)
   {
           const char       *arg[9];
           char             *cp, *n1, *n2;
           int               i;
   
           /*
            * Collect pointers to macro argument strings
            * and NUL-terminate them.
            */
           cp = *bufp + pos;
           for (i = 0; i < 9; i++)
                   arg[i] = '\0' == *cp ? "" :
                       mandoc_getarg(r->parse, &cp, ln, &pos);
   
           /*
            * Expand macro arguments.
            */
           *szp = 0;
           n1 = cp = mandoc_strdup(r->current_string);
           while (NULL != (cp = strstr(cp, "\\$"))) {
                   i = cp[2] - '1';
                   if (0 > i || 8 < i) {
                           /* Not an argument invocation. */
                           cp += 2;
                           continue;
                 }                  }
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))  
                         return(0);  
   
                 break;                  *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
                   n2 = mandoc_malloc(*szp);
   
                   strlcpy(n2, n1, (size_t)(cp - n1 + 1));
                   strlcat(n2, arg[i], *szp);
                   strlcat(n2, cp + 3, *szp);
   
                   cp = n2 + (cp - n1);
                   free(n1);
                   n1 = n2;
         }          }
   
         return(1);          /*
            * Replace the macro invocation
            * by the expanded macro.
            */
           free(*bufp);
           *bufp = n1;
           if (0 == *szp)
                   *szp = strlen(*bufp) + 1;
   
           return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
              ROFF_REPARSE : ROFF_APPEND);
 }  }
 #endif  
   
   static char *
   roff_getname(struct roff *r, char **cpp, int ln, int pos)
   {
           char     *name, *cp;
   
           name = *cpp;
           if ('\0' == *name)
                   return(name);
   
           /* Read until end of name. */
           for (cp = name; '\0' != *cp && ' ' != *cp; cp++) {
                   if ('\\' != *cp)
                           continue;
                   cp++;
                   if ('\\' == *cp)
                           continue;
                   mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL);
                   *cp = '\0';
                   name = cp;
           }
   
           /* Nil-terminate name. */
           if ('\0' != *cp)
                   *(cp++) = '\0';
   
           /* Read past spaces. */
           while (' ' == *cp)
                   cp++;
   
           *cpp = cp;
           return(name);
   }
   
   /*
    * Store *string into the user-defined string called *name.
    * To clear an existing entry, call with (*r, *name, NULL, 0).
    * append == 0: replace mode
    * append == 1: single-line append mode
    * append == 2: multiline append mode, append '\n' after each call
    */
 static void  static void
 roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)  roff_setstr(struct roff *r, const char *name, const char *string,
           int append)
 {  {
         va_list          ap;  
         char             buf[128];  
   
         va_start(ap, fmt);          roff_setstrn(&r->strtab, name, strlen(name), string,
         (void)vsnprintf(buf, sizeof(buf), fmt, ap);                          string ? strlen(string) : 0, append);
         va_end(ap);  }
   
         (*tree->cb.roffmsg)(tree->arg,  static void
                         ROFF_WARN, tree->cur, pos, buf);  roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
                   const char *string, size_t stringsz, int append)
   {
           struct roffkv   *n;
           char            *c;
           int              i;
           size_t           oldch, newch;
   
           /* Search for an existing string with the same name. */
           n = *r;
   
           while (n && strcmp(name, n->key.p))
                   n = n->next;
   
           if (NULL == n) {
                   /* Create a new string table entry. */
                   n = mandoc_malloc(sizeof(struct roffkv));
                   n->key.p = mandoc_strndup(name, namesz);
                   n->key.sz = namesz;
                   n->val.p = NULL;
                   n->val.sz = 0;
                   n->next = *r;
                   *r = n;
           } else if (0 == append) {
                   free(n->val.p);
                   n->val.p = NULL;
                   n->val.sz = 0;
           }
   
           if (NULL == string)
                   return;
   
           /*
            * One additional byte for the '\n' in multiline mode,
            * and one for the terminating '\0'.
            */
           newch = stringsz + (1 < append ? 2u : 1u);
   
           if (NULL == n->val.p) {
                   n->val.p = mandoc_malloc(newch);
                   *n->val.p = '\0';
                   oldch = 0;
           } else {
                   oldch = n->val.sz;
                   n->val.p = mandoc_realloc(n->val.p, oldch + newch);
           }
   
           /* Skip existing content in the destination buffer. */
           c = n->val.p + (int)oldch;
   
           /* Append new content to the destination buffer. */
           i = 0;
           while (i < (int)stringsz) {
                   /*
                    * Rudimentary roff copy mode:
                    * Handle escaped backslashes.
                    */
                   if ('\\' == string[i] && '\\' == string[i + 1])
                           i++;
                   *c++ = string[i++];
           }
   
           /* Append terminating bytes. */
           if (1 < append)
                   *c++ = '\n';
   
           *c = '\0';
           n->val.sz = (int)(c - n->val.p);
 }  }
   
   static const char *
   roff_getstrn(const struct roff *r, const char *name, size_t len)
   {
           const struct roffkv *n;
           int i;
   
           for (n = r->strtab; n; n = n->next)
                   if (0 == strncmp(name, n->key.p, len) &&
                                   '\0' == n->key.p[(int)len])
                           return(n->val.p);
   
           for (i = 0; i < PREDEFS_MAX; i++)
                   if (0 == strncmp(name, predefs[i].name, len) &&
                                   '\0' == predefs[i].name[(int)len])
                           return(predefs[i].str);
   
           return(NULL);
   }
   
 static void  static void
 roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)  roff_freestr(struct roffkv *r)
 {  {
         va_list          ap;          struct roffkv    *n, *nn;
         char             buf[128];  
   
         va_start(ap, fmt);          for (n = r; n; n = nn) {
         (void)vsnprintf(buf, sizeof(buf), fmt, ap);                  free(n->key.p);
         va_end(ap);                  free(n->val.p);
                   nn = n->next;
                   free(n);
           }
   }
   
         (*tree->cb.roffmsg)(tree->arg,  const struct tbl_span *
                         ROFF_ERROR, tree->cur, pos, buf);  roff_span(const struct roff *r)
   {
   
           return(r->tbl ? tbl_span(r->tbl) : NULL);
   }
   
   const struct eqn *
   roff_eqn(const struct roff *r)
   {
   
           return(r->last_eqn ? &r->last_eqn->eqn : NULL);
   }
   
   /*
    * Duplicate an input string, making the appropriate character
    * conversations (as stipulated by `tr') along the way.
    * Returns a heap-allocated string with all the replacements made.
    */
   char *
   roff_strdup(const struct roff *r, const char *p)
   {
           const struct roffkv *cp;
           char            *res;
           const char      *pp;
           size_t           ssz, sz;
           enum mandoc_esc  esc;
   
           if (NULL == r->xmbtab && NULL == r->xtab)
                   return(mandoc_strdup(p));
           else if ('\0' == *p)
                   return(mandoc_strdup(""));
   
           /*
            * Step through each character looking for term matches
            * (remember that a `tr' can be invoked with an escape, which is
            * a glyph but the escape is multi-character).
            * We only do this if the character hash has been initialised
            * and the string is >0 length.
            */
   
           res = NULL;
           ssz = 0;
   
           while ('\0' != *p) {
                   if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
                           sz = r->xtab[(int)*p].sz;
                           res = mandoc_realloc(res, ssz + sz + 1);
                           memcpy(res + ssz, r->xtab[(int)*p].p, sz);
                           ssz += sz;
                           p++;
                           continue;
                   } else if ('\\' != *p) {
                           res = mandoc_realloc(res, ssz + 2);
                           res[ssz++] = *p++;
                           continue;
                   }
   
                   /* Search for term matches. */
                   for (cp = r->xmbtab; cp; cp = cp->next)
                           if (0 == strncmp(p, cp->key.p, cp->key.sz))
                                   break;
   
                   if (NULL != cp) {
                           /*
                            * A match has been found.
                            * Append the match to the array and move
                            * forward by its keysize.
                            */
                           res = mandoc_realloc
                                   (res, ssz + cp->val.sz + 1);
                           memcpy(res + ssz, cp->val.p, cp->val.sz);
                           ssz += cp->val.sz;
                           p += (int)cp->key.sz;
                           continue;
                   }
   
                   /*
                    * Handle escapes carefully: we need to copy
                    * over just the escape itself, or else we might
                    * do replacements within the escape itself.
                    * Make sure to pass along the bogus string.
                    */
                   pp = p++;
                   esc = mandoc_escape(&p, NULL, NULL);
                   if (ESCAPE_ERROR == esc) {
                           sz = strlen(pp);
                           res = mandoc_realloc(res, ssz + sz + 1);
                           memcpy(res + ssz, pp, sz);
                           break;
                   }
                   /*
                    * We bail out on bad escapes.
                    * No need to warn: we already did so when
                    * roff_res() was called.
                    */
                   sz = (int)(p - pp);
                   res = mandoc_realloc(res, ssz + sz + 1);
                   memcpy(res + ssz, pp, sz);
                   ssz += sz;
           }
   
           res[(int)ssz] = '\0';
           return(res);
   }
   
   /*
    * Find out whether a line is a macro line or not.
    * If it is, adjust the current position and return one; if it isn't,
    * return zero and don't change the current position.
    * If the control character has been set with `.cc', then let that grain
    * precedence.
    * This is slighly contrary to groff, where using the non-breaking
    * control character when `cc' has been invoked will cause the
    * non-breaking macro contents to be printed verbatim.
    */
   int
   roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
   {
           int             pos;
   
           pos = *ppos;
   
           if (0 != r->control && cp[pos] == r->control)
                   pos++;
           else if (0 != r->control)
                   return(0);
           else if ('\\' == cp[pos] && '.' == cp[pos + 1])
                   pos += 2;
           else if ('.' == cp[pos] || '\'' == cp[pos])
                   pos++;
           else
                   return(0);
   
           while (' ' == cp[pos] || '\t' == cp[pos])
                   pos++;
   
           *ppos = pos;
           return(1);
 }  }

Legend:
Removed from v.1.26  
changed lines
  Added in v.1.200

CVSweb