[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.175

version 1.26, 2008/12/01 09:25:18 version 1.175, 2012/11/19 17:57:23
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, 2011, 2012 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 <stdlib.h>
 #include <stdio.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_cc,
           ROFF_de,
           ROFF_dei,
           ROFF_de1,
           ROFF_ds,
           ROFF_el,
           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_ccond,
           ROFF_USERDEF,
           ROFF_MAX
   };
   
 /* FIXME: roff_layout and roff_text have identical-ish lower bodies. */  enum    roffrule {
           ROFFRULE_ALLOW,
           ROFFRULE_DENY
   };
   
 /* FIXME: NAME section needs specific elements. */  /*
    * A single register entity.  If "set" is zero, the value of the
    * register should be the default one, which is per-register.
    * Registers are assumed to be unsigned ints for now.
    */
   struct  reg {
           int              set; /* whether set or not */
           unsigned int     u; /* unsigned integer */
   };
   
 #define ROFF_MAXARG       32  /*
    * An incredibly-simple string buffer.
    */
   struct  roffstr {
           char            *p; /* nil-terminated buffer */
           size_t           sz; /* saved strlen(p) */
   };
   
 enum    roffd {  /*
         ROFF_ENTER = 0,   * A key-value roffstr pair as part of a singly-linked list.
         ROFF_EXIT   */
   struct  roffkv {
           struct roffstr   key;
           struct roffstr   val;
           struct roffkv   *next; /* next in list */
 };  };
   
 enum    rofftype {  struct  roff {
         ROFF_COMMENT,          enum mparset     parsetype; /* requested parse type */
         ROFF_TEXT,          struct mparse   *parse; /* parse point */
         ROFF_LAYOUT,          struct roffnode *last; /* leaf of stack */
         ROFF_SPECIAL          enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
           char             control; /* control character */
           int              rstackpos; /* position in rstack */
           struct reg       regs[REG__MAX];
           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 */
 };  };
   
 #define ROFFCALL_ARGS \  struct  roffnode {
         int tok, struct rofftree *tree, \          enum rofft       tok; /* type of node */
         char *argv[], enum roffd type          struct roffnode *parent; /* up one in stack */
           int              line; /* parse line */
           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 */
           enum roffrule    rule; /* current evaluation rule */
   };
   
 struct  rofftree;  #define ROFF_ARGS        struct roff *r, /* parse ctx */ \
                            enum rofft tok, /* tok of macro */ \
                            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  rofftok {  typedef enum rofferr (*roffproc)(ROFF_ARGS);
         int             (*cb)(ROFFCALL_ARGS);   /* Callback. */  
         const int        *args;                 /* Args (or NULL). */  
         const int        *parents;  
         const int        *children;  
         int               ctx;  
         enum rofftype     type;                 /* Type of macro. */  
         int               flags;  
 #define ROFF_PARSED      (1 << 0)               /* "Parsed". */  
 #define ROFF_CALLABLE    (1 << 1)               /* "Callable". */  
 #define ROFF_SHALLOW     (1 << 2)               /* Nesting block. */  
 };  
   
 struct  roffarg {  struct  roffmac {
         int               flags;          const char      *name; /* macro name */
 #define ROFF_VALUE       (1 << 0)               /* Has a value. */          roffproc         proc; /* process new macro */
           roffproc         text; /* process as child text of macro */
           roffproc         sub; /* process as child of macro */
           int              flags;
   #define ROFFMAC_STRUCT  (1 << 0) /* always interpret */
           struct roffmac  *next;
 };  };
   
 struct  roffnode {  struct  predef {
         int               tok;                  /* Token id. */          const char      *name; /* predefined input name */
         struct roffnode  *parent;               /* Parent (or NULL). */          const char      *str; /* replacement symbol */
 };  };
   
 struct  rofftree {  #define PREDEF(__name, __str) \
         struct roffnode  *last;                 /* Last parsed node. */          { (__name), (__str) },
         char             *cur;  
   
         time_t            date;                 /* `Dd' results. */  static  enum rofft       roffhash_find(const char *, size_t);
         char              os[64];               /* `Os' results. */  static  void             roffhash_init(void);
         char              title[64];            /* `Dt' results. */  static  void             roffnode_cleanscope(struct roff *);
         char              section[64];          /* `Dt' results. */  static  void             roffnode_pop(struct roff *);
         char              volume[64];           /* `Dt' results. */  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  enum rofferr     roff_ccond(ROFF_ARGS);
   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  enum roffrule    roff_evalcond(const char *, int *);
   static  void             roff_free1(struct roff *);
   static  void             roff_freestr(struct roffkv *);
   static  char            *roff_getname(struct roff *, char **, int, int);
   static  const char      *roff_getstrn(const struct roff *,
                                   const char *, size_t);
   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 *);
   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);
   
         int               state;  /* See roffhash_find() */
 #define ROFF_PRELUDE     (1 << 1)               /* In roff prelude. */  
 #define ROFF_PRELUDE_Os  (1 << 2)               /* `Os' is parsed. */  
 #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;  #define ASCII_HI         126
         void             *arg;  #define ASCII_LO         33
   #define HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
   
   static  struct roffmac  *hash[HASHWIDTH];
   
   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 },
           { "cc", roff_cc, 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 },
           { "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_line_ignore, 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 },
           { "\\}", roff_ccond, NULL, NULL, 0, NULL },
           { NULL, roff_userdef, NULL, NULL, 0, NULL },
 };  };
   
 static  int               roff_Dd(ROFFCALL_ARGS);  const   char *const __mdoc_reserved[] = {
 static  int               roff_Dt(ROFFCALL_ARGS);          "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
 static  int               roff_Os(ROFFCALL_ARGS);          "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
 #ifdef notyet          "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
 static  int               roff_Ns(ROFFCALL_ARGS);          "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
 #endif          "Ds", "Dt", "Dv", "Dx", "D1",
           "Ec", "Ed", "Ef", "Ek", "El", "Em", "em",
           "En", "Eo", "Eq", "Er", "Es", "Ev", "Ex",
           "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
           "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", "LP",
           "Me", "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
           "Oc", "Oo", "Op", "Os", "Ot", "Ox",
           "Pa", "Pc", "Pf", "Po", "Pp", "PP", "pp", "Pq",
           "Qc", "Ql", "Qo", "Qq", "Or", "Rd", "Re", "Rs", "Rv",
           "Sc", "Sf", "Sh", "SH", "Sm", "So", "Sq",
           "Ss", "St", "Sx", "Sy",
           "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
           "%A", "%B", "%D", "%I", "%J", "%N", "%O",
           "%P", "%Q", "%R", "%T", "%U", "%V",
           NULL
   };
   
 static  int               roff_layout(ROFFCALL_ARGS);  const   char *const __man_reserved[] = {
 static  int               roff_text(ROFFCALL_ARGS);          "AT", "B", "BI", "BR", "BT", "DE", "DS", "DT",
 static  int               roff_comment(ROFFCALL_ARGS);          "EE", "EN", "EQ", "EX", "HF", "HP", "I", "IB", "IP", "IR",
 static  int               roff_close(ROFFCALL_ARGS);          "LP", "ME", "MT", "OP", "P", "PD", "PP", "PT",
           "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", "SY",
           "TE", "TH", "TP", "TQ", "TS", "T&", "UC", "UE", "UR", "YS",
           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 void
 static  int               rofffindtok(const char *);  roffhash_init(void)
 static  int               rofffindarg(const char *);  {
 static  int               rofffindcallable(const char *);          struct roffmac   *n;
 static  int               roffargs(const struct rofftree *,          int               buc, i;
                                 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 *);  
   
           for (i = 0; i < (int)ROFF_USERDEF; i++) {
                   assert(roffs[i].name[0] >= ASCII_LO);
                   assert(roffs[i].name[0] <= ASCII_HI);
   
 static  const int roffarg_An[] = { ROFF_Split, ROFF_Nosplit,                  buc = ROFF_HASH(roffs[i].name);
         ROFF_ARGMAX };  
 static  const int roffarg_Bd[] = { ROFF_Ragged, ROFF_Unfilled,  
         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 };                  if (NULL != (n = hash[buc])) {
 static  const int roffchild_Fo[] = { ROFF_Fa, ROFF_Fc, ROFF_MAX };                          for ( ; n->next; n = n->next)
 static  const int roffchild_Oo[] = { ROFF_Op, ROFF_Oc, ROFF_MAX };                                  /* Do nothing. */ ;
 static  const int roffchild_Rs[] = { ROFF_Re, ROFF__A, ROFF__B,                          n->next = &roffs[i];
         ROFF__D, ROFF__I, ROFF__J, ROFF__N, ROFF__O, ROFF__P,                  } else
         ROFF__R, ROFF__T, ROFF__V, ROFF_MAX };                          hash[buc] = &roffs[i];
           }
   }
   
 static  const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };  /*
 static  const int roffparent_Fc[] = { ROFF_Fo, ROFF_Fa, ROFF_MAX };   * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
 static  const int roffparent_Oc[] = { ROFF_Oo, ROFF_Oc, ROFF_MAX };   * the nil-terminated string name could be found.
 static  const int roffparent_It[] = { ROFF_Bl, ROFF_It, ROFF_MAX };   */
 static  const int roffparent_Re[] = { ROFF_Rs, ROFF_MAX };  static enum rofft
   roffhash_find(const char *p, size_t s)
   {
           int              buc;
           struct roffmac  *n;
   
 /* Table of all known tokens. */          /*
 static  const struct rofftok tokens[ROFF_MAX] = {           * libroff has an extremely simple hashtable, for the time
         {roff_comment, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */           * being, which simply keys on the first character, which must
         {     roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */           * be printable, then walks a chain.  It works well enough until
         {     roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */           * optimised.
         {     roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */           */
         { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, 0 }, /* Sh */  
         { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, 0 }, /* Ss */  
         {   roff_text, NULL, NULL, NULL, ROFF_Pp, ROFF_TEXT, 0 }, /* Pp */  
         {   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. */          if (p[0] < ASCII_LO || p[0] > ASCII_HI)
 static  const int tokenargs[ROFF_ARGMAX] = {                  return(ROFF_MAX);
         0,              0,              0,              0,  
         0,              ROFF_VALUE,     ROFF_VALUE,     0,  
         0,              0,              0,              0,  
         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] = {          buc = ROFF_HASH(p);
         "\\\"",         "Dd",           "Dt",           "Os",  
         "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] = {          if (NULL == (n = hash[buc]))
         "split",                "nosplit",              "ragged",                  return(ROFF_MAX);
         "unfilled",             "literal",              "file",          for ( ; n; n = n->next)
         "offset",               "bullet",               "dash",                  if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
         "hyphen",               "item",                 "enum",                          return((enum rofft)(n - roffs));
         "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;          return(ROFF_MAX);
 const   char *const *tokargnames = tokargnamesp;  }
   
   
 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 : ROFFRULE_DENY;
   
           r->last = p;
   }
   
   
   static void
   roff_free1(struct roff *r)
   {
           struct tbl_node *t;
           struct eqn_node *e;
           int              i;
   
           while (NULL != (t = r->first_tbl)) {
                   r->first_tbl = t->next;
                   tbl_free(t);
         }          }
   
         while (tree->last) {          r->first_tbl = r->last_tbl = r->tbl = NULL;
                 t = tree->last->tok;  
                 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))          while (NULL != (e = r->first_eqn)) {
                         goto end;                  r->first_eqn = e->next;
                   eqn_free(e);
         }          }
   
         error = 0;          r->first_eqn = r->last_eqn = r->eqn = NULL;
   
 end:          while (r->last)
                   roffnode_pop(r);
   
         while (tree->last)          roff_freestr(r->strtab);
                 roffnode_free(tree);          roff_freestr(r->xmbtab);
   
         free(tree);          r->strtab = r->xmbtab = NULL;
   
         return(error ? 0 : 1);          if (r->xtab)
                   for (i = 0; i < 128; i++)
                           free(r->xtab[i].p);
   
           free(r->xtab);
           r->xtab = NULL;
 }  }
   
   void
 struct rofftree *  roff_reset(struct roff *r)
 roff_alloc(const struct roffcb *cb, void *args)  
 {  {
         struct rofftree *tree;          int              i;
   
         assert(args);          roff_free1(r);
         assert(cb);  
   
         if (NULL == (tree = calloc(1, sizeof(struct rofftree))))          r->control = 0;
                 err(1, "calloc");          memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
   
         tree->state = ROFF_PRELUDE;          for (i = 0; i < PREDEFS_MAX; i++)
         tree->arg = args;                  roff_setstr(r, predefs[i].name, predefs[i].str, 0);
   }
   
         (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));  
   
         return(tree);  void
   roff_free(struct roff *r)
   {
   
           roff_free1(r);
           free(r);
 }  }
   
   
 int  struct roff *
 roff_engine(struct rofftree *tree, char *buf)  roff_alloc(enum mparset type, struct mparse *parse)
 {  {
           struct roff     *r;
           int              i;
   
         tree->cur = buf;          r = mandoc_calloc(1, sizeof(struct roff));
         assert(buf);          r->parsetype = type;
           r->parse = parse;
           r->rstackpos = -1;
   
           roffhash_init();
   
         if (0 == *buf) {          for (i = 0; i < PREDEFS_MAX; i++)
                 roff_warn(tree, buf, "blank line");                  roff_setstr(r, predefs[i].name, predefs[i].str, 0);
                 return(0);  
         } else if ('.' != *buf)  
                 return(textparse(tree, buf));  
   
         return(roffparse(tree, buf));          return(r);
 }  }
   
   /*
 static int   * Pre-filter each and every line for reserved words (one beginning with
 textparse(const struct rofftree *tree, char *buf)   * `\*', e.g., `\*(ab').  These must be handled before the actual line
    * is processed.
    * This also checks the syntax of regular escapes.
    */
   static enum rofferr
   roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
 {  {
           enum mandoc_esc  esc;
           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 */
           int              i, maxl, expand_count;
           size_t           nsz;
           char            *n;
   
         return((*tree->cb.roffdata)(tree->arg, 1, buf));          expand_count = 0;
 }  
   
   again:
           cp = *bufp + pos;
           while (NULL != (cp = strchr(cp, '\\'))) {
                   stesc = cp++;
   
 static int                  /*
 roffargs(const struct rofftree *tree,                   * The second character must be an asterisk.
                 int tok, char *buf, char **argv)                   * If it isn't, skip it anyway:  It is escaped,
 {                   * so it can't start another escape sequence.
         int              i;                   */
         char            *p;  
   
         assert(tok >= 0 && tok < ROFF_MAX);                  if ('\0' == *cp)
         assert('.' == *buf);                          return(ROFF_CONT);
   
         p = buf;                  if ('*' != *cp) {
                           res = cp;
                           esc = mandoc_escape(&cp, NULL, NULL);
                           if (ESCAPE_ERROR != esc)
                                   continue;
                           cp = res;
                           mandoc_msg
                                   (MANDOCERR_BADESCAPE, r->parse,
                                    ln, (int)(stesc - *bufp), NULL);
                           return(ROFF_CONT);
                   }
   
         /* LINTED */                  cp++;
         for (i = 0; *buf && i < ROFF_MAXARG; i++) {  
                 if ('\"' == *buf) {                  /*
                         argv[i] = ++buf;                   * The third character decides the length
                         while (*buf && '\"' != *buf)                   * of the name of the string.
                                 buf++;                   * Save a pointer to the name.
                         if (0 == *buf) {                   */
                                 roff_err(tree, argv[i], "unclosed "  
                                                 "quote in argument "                  switch (*cp) {
                                                 "list for `%s'",                  case ('\0'):
                                                 toknames[tok]);                          return(ROFF_CONT);
                                 return(0);                  case ('('):
                           cp++;
                           maxl = 2;
                           break;
                   case ('['):
                           cp++;
                           maxl = 0;
                           break;
                   default:
                           maxl = 1;
                           break;
                   }
                   stnam = cp;
   
                   /* Advance to the end of the name. */
   
                   for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
                           if ('\0' == *cp) {
                                   mandoc_msg
                                           (MANDOCERR_BADESCAPE,
                                            r->parse, ln,
                                            (int)(stesc - *bufp), NULL);
                                   return(ROFF_CONT);
                         }                          }
                 } else {                          if (0 == maxl && ']' == *cp)
                         argv[i] = buf++;                                  break;
                         while (*buf && ! isspace(*buf))  
                                 buf++;  
                         if (0 == *buf)  
                                 continue;  
                 }                  }
                 *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;                  /*
         return(1);                   * Retrieve the replacement string; if it is
 }                   * undefined, resume searching for escapes.
                    */
   
                   res = roff_getstrn(r, stnam, (size_t)i);
   
 static int                  if (NULL == res) {
 roffscan(int tok, const int *tokv)                          mandoc_msg
 {                                  (MANDOCERR_BADESCAPE, r->parse,
                                    ln, (int)(stesc - *bufp), NULL);
                           res = "";
                   }
   
         if (NULL == tokv)                  /* Replace the escape sequence by the string. */
                 return(1);  
   
         for ( ; ROFF_MAX != *tokv; tokv++)                  pos = stesc - *bufp;
                 if (tok == *tokv)  
                         return(1);  
   
         return(0);                  nsz = *szp + strlen(res) + 1;
 }                  n = mandoc_malloc(nsz);
   
                   strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
                   strlcat(n, res, nsz);
                   strlcat(n, cp + (maxl ? 0 : 1), nsz);
   
 static int                  free(*bufp);
 roffparse(struct rofftree *tree, char *buf)  
 {  
         int               tok, t;  
         struct roffnode  *n;  
         char             *argv[ROFF_MAXARG];  
         char            **argvp;  
   
         if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))                  *bufp = n;
                 if (0 == strncmp(buf, ".\\\"", 3))                  *szp = nsz;
                         return(1);  
   
         if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {                  if (EXPAND_LIMIT >= ++expand_count)
                 roff_err(tree, buf + 1, "bogus line macro");                          goto again;
                 return(0);  
         } else if (NULL == tokens[tok].cb) {                  /* Just leave the string unexpanded. */
                 roff_err(tree, buf + 1, "unsupported macro `%s'",                  mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL);
                                 toknames[tok]);                  return(ROFF_IGN);
                 return(0);  
         }          }
           return(ROFF_CONT);
   }
   
         assert(ROFF___ != tok);  /*
         if ( ! roffargs(tree, tok, buf, argv))   * Process text streams: convert all breakable hyphens into ASCII_HYPH.
                 return(0);   */
   static enum rofferr
   roff_parsetext(char *p)
   {
           size_t           sz;
           const char      *start;
           enum mandoc_esc  esc;
   
         argvp = (char **)argv;          start = p;
   
         /*          while ('\0' != *p) {
          * Prelude macros break some assumptions, so branch now.                  sz = strcspn(p, "-\\");
          */                  p += sz;
   
         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);                  if ('\0' == *p)
                           break;
   
         /*                  if ('\\' == *p) {
          * First check that our possible parents and parent's possible                          /* Skip over escapes. */
          * children are satisfied.                          p++;
                           esc = mandoc_escape
                                   ((const char **)&p, NULL, NULL);
                           if (ESCAPE_ERROR == esc)
                                   break;
                           continue;
                   } else if (p == start) {
                           p++;
                           continue;
                   }
   
                   if (isalpha((unsigned char)p[-1]) &&
                       isalpha((unsigned char)p[1]))
                           *p = ASCII_HYPH;
                   p++;
           }
   
           return(ROFF_CONT);
   }
   
   enum rofferr
   roff_parseln(struct roff *r, int ln, char **bufp,
                   size_t *szp, int pos, int *offs)
   {
           enum rofft       t;
           enum rofferr     e;
           int              ppos, ctl;
   
           /*
            * Run the reserved-word filter only if we have some reserved
            * words to fill in.
          */           */
   
         if ( ! roffscan(tree->last->tok, tokens[tok].parents)) {          e = roff_res(r, bufp, szp, ln, pos);
                 roff_err(tree, *argvp, "`%s' has invalid parent `%s'",          if (ROFF_IGN == e)
                                 toknames[tok],                  return(e);
                                 toknames[tree->last->tok]);          assert(ROFF_CONT == e);
                 return(0);  
         }  
   
         if ( ! roffscan(tok, tokens[tree->last->tok].children)) {          ppos = pos;
                 roff_err(tree, *argvp, "`%s' is invalid child of `%s'",          ctl = roff_getcontrol(r, *bufp, &pos);
                                 toknames[tok],  
                                 toknames[tree->last->tok]);  
                 return(0);  
         }  
   
         /*          /*
          * Branch if we're not a layout token.           * 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 (ROFF_LAYOUT != tokens[tok].type)          if (r->last && ! ctl) {
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));                  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, pos, offs));
                   if (r->tbl)
                           return(tbl_read(r->tbl, ln, *bufp, pos));
                   return(roff_parsetext(*bufp + pos));
           } else if ( ! ctl) {
                   if (r->eqn)
                           return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
                   if (r->tbl)
                           return(tbl_read(r->tbl, ln, *bufp, pos));
                   return(roff_parsetext(*bufp + pos));
           } else if (r->eqn)
                   return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
   
         /*          /*
          * Check our scope rules.           * If a scope is open, go to the child handler for that macro,
            * as it may want to preprocess before doing anything with it.
            * Don't do so if an equation is open.
          */           */
   
         if (0 == tokens[tok].ctx)          if (r->last) {
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));                  t = r->last->tok;
                   assert(roffs[t].sub);
                   return((*roffs[t].sub)
                                   (r, t, bufp, szp,
                                    ln, ppos, pos, offs));
           }
   
         /*          /*
          * First consider implicit-end tags, like as follows:           * Lastly, as we've no scope open, try to look up and execute
          *      .Sh SECTION 1           * the new macro.  If no macro is found, simply return and let
          *      .Sh SECTION 2           * the compilers handle it.
          * In this, we want to close the scope of the NAME section.  If  
          * there's an intermediary implicit-end tag, such as  
          *      .Sh SECTION 1  
          *      .Ss Subsection 1  
          *      .Sh SECTION 2  
          * then it must be closed as well.  
          */           */
   
         if (tok == tokens[tok].ctx) {          if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
                 /*                  return(ROFF_CONT);
                  * First search up to the point where we must close.  
                  * If one doesn't exist, then we can open a new scope.  
                  */  
   
                 for (n = tree->last; n; n = n->parent) {          assert(roffs[t].proc);
                         assert(0 == tokens[n->tok].ctx ||          return((*roffs[t].proc)
                                         n->tok == tokens[n->tok].ctx);                          (r, t, bufp, szp,
                         if (n->tok == tok)                           ln, ppos, pos, offs));
                                 break;  }
                         if (ROFF_SHALLOW & tokens[tok].flags) {  
                                 n = NULL;  
                                 break;  
                         }  
                 }  
   
                 /*  
                  * Create a new scope, as no previous one exists to  
                  * close out.  
                  */  
   
                 if (NULL == n)  void
                         return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));  roff_endparse(struct roff *r)
   {
   
                 /*          if (r->last)
                  * Close out all intermediary scoped blocks, then hang                  mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
                  * the current scope from our predecessor's parent.                                  r->last->line, r->last->col, NULL);
                  */  
   
                 do {          if (r->eqn) {
                         t = tree->last->tok;                  mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
                         if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))                                  r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
                                 return(0);                  eqn_end(&r->eqn);
                 } while (t != tok);          }
   
                 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));          if (r->tbl) {
                   mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
                                   r->tbl->line, r->tbl->pos, NULL);
                   tbl_end(&r->tbl);
         }          }
   }
   
   /*
    * Parse a roff node's type from the input buffer.  This must be in the
    * form of ".foo xxx" in the usual way.
    */
   static enum rofft
   roff_parse(struct roff *r, const char *buf, int *pos)
   {
           const char      *mac;
           size_t           maclen;
           enum rofft       t;
   
           if ('\0' == buf[*pos] || '"' == buf[*pos] ||
                           '\t' == buf[*pos] || ' ' == buf[*pos])
                   return(ROFF_MAX);
   
         /*          /*
          * Now consider explicit-end tags, where we want to close back           * We stop the macro parse at an escape, tab, space, or nil.
          * to a specific tag.  Example:           * However, `\}' is also a valid macro, so make sure we don't
          *      .Bl           * clobber it by seeing the `\' as the end of token.
          *      .It Item.  
          *      .El  
          * In this, the `El' tag closes out the scope of `Bl'.  
          */           */
   
         assert(tree->last);          mac = buf + *pos;
         assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);          maclen = strcspn(mac + 1, " \\\t\0") + 1;
   
         /* LINTED */          t = (r->current_string = roff_getstrn(r, mac, maclen))
         do {              ? ROFF_USERDEF : roffhash_find(mac, maclen);
                 t = tree->last->tok;  
                 if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))  
                         return(0);  
         } while (t != tokens[tok].ctx);  
   
         assert(tree->last);          *pos += (int)maclen;
         return(1);  
 }  
   
           while (buf[*pos] && ' ' == buf[*pos])
                   (*pos)++;
   
 static int          return(t);
 rofffindarg(const char *name)  }
   
   /* ARGSUSED */
   static enum rofferr
   roff_cblock(ROFF_ARGS)
 {  {
         size_t           i;  
   
         /* FIXME: use a table, this is slow but ok for now. */          /*
            * A block-close `..' should only be invoked as a child of an
            * ignore macro, otherwise raise a warning and just ignore it.
            */
   
         /* LINTED */          if (NULL == r->last) {
         for (i = 0; i < ROFF_ARGMAX; i++)                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                 /* LINTED */                  return(ROFF_IGN);
                 if (0 == strcmp(name, tokargnames[i]))          }
                         return((int)i);  
   
         return(ROFF_ARGMAX);  
 }  
   
           switch (r->last->tok) {
           case (ROFF_am):
                   /* FALLTHROUGH */
           case (ROFF_ami):
                   /* FALLTHROUGH */
           case (ROFF_am1):
                   /* FALLTHROUGH */
           case (ROFF_de):
                   /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
                   /* FALLTHROUGH */
           case (ROFF_dei):
                   /* FALLTHROUGH */
           case (ROFF_ig):
                   break;
           default:
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                   return(ROFF_IGN);
           }
   
 static int          if ((*bufp)[pos])
 rofffindtok(const char *buf)                  mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
 {  
         char             token[4];  
         int              i;  
   
         for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)          roffnode_pop(r);
                 token[i] = *buf;          roffnode_cleanscope(r);
           return(ROFF_IGN);
   
         if (i == 3)  }
                 return(ROFF_MAX);  
   
         token[i] = 0;  
   
         /* FIXME: use a table, this is slow but ok for now. */  static void
   roffnode_cleanscope(struct roff *r)
   {
   
         /* LINTED */          while (r->last) {
         for (i = 0; i < ROFF_MAX; i++)                  if (--r->last->endspan != 0)
                 /* LINTED */                          break;
                 if (0 == strcmp(toknames[i], token))                  roffnode_pop(r);
                         return((int)i);          }
   
         return(ROFF_MAX);  
 }  }
   
   
 static int  /* ARGSUSED */
 roffispunct(const char *p)  static enum rofferr
   roff_ccond(ROFF_ARGS)
 {  {
   
         if (0 == *p)          if (NULL == r->last) {
                 return(0);                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
         if (0 != *(p + 1))                  return(ROFF_IGN);
                 return(0);          }
   
         switch (*p) {          switch (r->last->tok) {
         case('{'):          case (ROFF_el):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case('.'):          case (ROFF_ie):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case(','):          case (ROFF_if):
                 /* FALLTHROUGH */  
         case(';'):  
                 /* FALLTHROUGH */  
         case(':'):  
                 /* FALLTHROUGH */  
         case('?'):  
                 /* 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 (r->last->endspan > -1) {
                   mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                   return(ROFF_IGN);
           }
   
           if ((*bufp)[pos])
                   mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
   
           roffnode_pop(r);
           roffnode_cleanscope(r);
           return(ROFF_IGN);
 }  }
   
   
 static int  /* ARGSUSED */
 rofffindcallable(const char *name)  static enum rofferr
   roff_block(ROFF_ARGS)
 {  {
         int              c;          int             sv;
           size_t          sz;
           char            *name;
   
         if (ROFF_MAX == (c = rofffindtok(name)))          name = NULL;
                 return(ROFF_MAX);  
         assert(c >= 0 && c < ROFF_MAX);  
         return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);  
 }  
   
           if (ROFF_ig != tok) {
                   if ('\0' == (*bufp)[pos]) {
                           mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
                           return(ROFF_IGN);
                   }
   
 static struct roffnode *                  /*
 roffnode_new(int tokid, struct rofftree *tree)                   * Re-write `de1', since we don't really care about
 {                   * groff's strange compatibility mode, into `de'.
         struct roffnode *p;                   */
   
         if (NULL == (p = malloc(sizeof(struct roffnode))))  
                 err(1, "malloc");  
   
         p->tok = tokid;                  if (ROFF_de1 == tok)
         p->parent = tree->last;                          tok = ROFF_de;
         tree->last = p;                  if (ROFF_de == tok)
                           name = *bufp + pos;
                   else
                           mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
                               roffs[tok].name);
   
         return(p);                  while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
                           pos++;
   
                   while (isspace((unsigned char)(*bufp)[pos]))
                           (*bufp)[pos++] = '\0';
           }
   
           roffnode_push(r, tok, name, ln, ppos);
   
           /*
            * At the beginning of a `de' macro, clear the existing string
            * with the same name, if there is one.  New content will be
            * added from roff_block_text() in multiline mode.
            */
   
           if (ROFF_de == tok)
                   roff_setstr(r, name, "", 0);
   
           if ('\0' == (*bufp)[pos])
                   return(ROFF_IGN);
   
           /* If present, process the custom end-of-line marker. */
   
           sv = pos;
           while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
                   pos++;
   
           /*
            * 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.
            */
   
           assert(pos > sv);
           sz = (size_t)(pos - sv);
   
           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);
 }  }
   
   
 static int  /* ARGSUSED */
 roffargok(int tokid, int argid)  static enum rofferr
   roff_block_sub(ROFF_ARGS)
 {  {
         const int       *c;          enum rofft      t;
           int             i, j;
   
         if (NULL == (c = tokens[tokid].args))          /*
                 return(0);           * First check whether a custom macro exists at this level.  If
            * it does, then check against it.  This is some of groff's
            * 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.
            */
   
         for ( ; ROFF_ARGMAX != *c; c++)          if (r->last->end) {
                 if (argid == *c)                  for (i = pos, j = 0; r->last->end[j]; j++, i++)
                         return(1);                          if ((*bufp)[i] != r->last->end[j])
                                   break;
   
         return(0);                  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);
                   }
           }
   
           /*
            * If we have no custom end-query or lookup failed, then try
            * pulling it out of the hashtable.
            */
   
           t = roff_parse(r, *bufp, &pos);
   
           /*
            * Macros other than block-end are only significant
            * in `de' blocks; elsewhere, simply throw them away.
            */
           if (ROFF_cblock != t) {
                   if (ROFF_de == tok)
                           roff_setstr(r, r->last->name, *bufp + ppos, 1);
                   return(ROFF_IGN);
           }
   
           assert(roffs[t].proc);
           return((*roffs[t].proc)(r, t, bufp, szp,
                                   ln, ppos, pos, offs));
 }  }
   
   
 static void  /* ARGSUSED */
 roffnode_free(struct rofftree *tree)  static enum rofferr
   roff_block_text(ROFF_ARGS)
 {  {
         struct roffnode *p;  
   
         assert(tree->last);          if (ROFF_de == tok)
                   roff_setstr(r, r->last->name, *bufp + pos, 1);
   
         p = tree->last;          return(ROFF_IGN);
         tree->last = tree->last->parent;  
         free(p);  
 }  }
   
   
 static int  /* ARGSUSED */
 roffnextopt(const struct rofftree *tree, int tok,  static enum rofferr
                 char ***in, char **val)  roff_cond_sub(ROFF_ARGS)
 {  {
         char            *arg, **argv;          enum rofft       t;
         int              v;          enum roffrule    rr;
           char            *ep;
   
         *val = NULL;          rr = r->last->rule;
         argv = *in;          roffnode_cleanscope(r);
         assert(argv);  
   
         if (NULL == (arg = *argv))          /*
                 return(-1);           * If the macro is unknown, first check if it contains a closing
         if ('-' != *arg)           * delimiter `\}'.  If it does, close out our scope and return
                 return(-1);           * the currently-scoped rule (ignore or continue).  Else, drop
            * into the currently-scoped rule.
            */
   
         if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {          if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
                 roff_warn(tree, arg, "argument-like parameter `%s' to "                  ep = &(*bufp)[pos];
                                 "`%s'", &arg[1], toknames[tok]);                  for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
                 return(-1);                          ep++;
         }                          if ('}' != *ep)
                                   continue;
         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;                          /*
                            * Make the \} go away.
                            * This is a little haphazard, as it's not quite
                            * clear how nroff does this.
                            * If we're at the end of line, then just chop
                            * off the \} and resize the buffer.
                            * If we aren't, then conver it to spaces.
                            */
   
         if (NULL == *argv) {                          if ('\0' == *(ep + 1)) {
                 roff_err(tree, arg, "empty value of `%s' for `%s'",                                  *--ep = '\0';
                                 tokargnames[v], toknames[tok]);                                  *szp -= 2;
                 return(ROFF_ARGMAX);                          } else
                                   *(ep - 1) = *ep = ' ';
   
                           roff_ccond(r, ROFF_ccond, bufp, szp,
                                           ln, pos, pos + 2, offs);
                           break;
                   }
                   return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
         }          }
   
         return(v);          /*
 }           * A denied conditional must evaluate its children if and only
            * if they're either structurally required (such as loops and
            * conditionals) or a closing macro.
            */
   
           if (ROFFRULE_DENY == rr)
                   if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
                           if (ROFF_ccond != t)
                                   return(ROFF_IGN);
   
           assert(roffs[t].proc);
           return((*roffs[t].proc)(r, t, bufp, szp,
                                   ln, ppos, pos, offs));
   }
   
 /* ARGSUSED */  /* ARGSUSED */
 static  int  static enum rofferr
 roff_Dd(ROFFCALL_ARGS)  roff_cond_text(ROFF_ARGS)
 {  {
           char            *ep;
           enum roffrule    rr;
   
         if (ROFF_BODY & tree->state) {          rr = r->last->rule;
                 assert( ! (ROFF_PRELUDE & tree->state));          roffnode_cleanscope(r);
                 assert(ROFF_PRELUDE_Dd & tree->state);  
                 return(roff_text(tok, tree, argv, type));          ep = &(*bufp)[pos];
           for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
                   ep++;
                   if ('}' != *ep)
                           continue;
                   *ep = '&';
                   roff_ccond(r, ROFF_ccond, bufp, szp,
                                   ln, pos, pos + 2, offs);
         }          }
           return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
   }
   
         assert(ROFF_PRELUDE & tree->state);  static enum roffrule
         assert( ! (ROFF_BODY & tree->state));  roff_evalcond(const char *v, int *pos)
   {
   
         if (ROFF_PRELUDE_Dd & tree->state) {          switch (v[*pos]) {
                 roff_err(tree, *argv, "repeated `Dd' in prelude");          case ('n'):
                 return(0);                  (*pos)++;
         } else if (ROFF_PRELUDE_Dt & tree->state) {                  return(ROFFRULE_ALLOW);
                 roff_err(tree, *argv, "out-of-order `Dd' in prelude");          case ('e'):
                 return(0);                  /* FALLTHROUGH */
           case ('o'):
                   /* FALLTHROUGH */
           case ('t'):
                   (*pos)++;
                   return(ROFFRULE_DENY);
           default:
                   break;
         }          }
   
         /* TODO: parse date. */          while (v[*pos] && ' ' != v[*pos])
                   (*pos)++;
           return(ROFFRULE_DENY);
   }
   
         assert(NULL == tree->last);  /* ARGSUSED */
         tree->state |= ROFF_PRELUDE_Dd;  static enum rofferr
   roff_line_ignore(ROFF_ARGS)
   {
   
         return(1);          if (ROFF_it == tok)
                   mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
   
           return(ROFF_IGN);
 }  }
   
   
 /* ARGSUSED */  /* ARGSUSED */
 static  int  static enum rofferr
 roff_Dt(ROFFCALL_ARGS)  roff_cond(ROFF_ARGS)
 {  {
   
         if (ROFF_BODY & tree->state) {          roffnode_push(r, tok, NULL, ln, ppos);
                 assert( ! (ROFF_PRELUDE & tree->state));  
                 assert(ROFF_PRELUDE_Dt & tree->state);          /*
                 return(roff_text(tok, tree, argv, type));           * 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 ?
                    ROFFRULE_DENY : 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);
                   }
                   r->rstack[++r->rstackpos] =
                           ROFFRULE_DENY == r->last->rule ?
                           ROFFRULE_ALLOW : ROFFRULE_DENY;
         }          }
   
         assert(ROFF_PRELUDE & tree->state);          /* If the parent has false as its rule, then so do we. */
         assert( ! (ROFF_BODY & tree->state));  
   
         if ( ! (ROFF_PRELUDE_Dd & tree->state)) {          if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
                 roff_err(tree, *argv, "out-of-order `Dt' in prelude");                  r->last->rule = ROFFRULE_DENY;
                 return(0);  
         } else if (ROFF_PRELUDE_Dt & tree->state) {          /*
                 roff_err(tree, *argv, "repeated `Dt' in prelude");           * Determine scope.
                 return(0);           * If there is nothing on the line after the conditional,
            * not even whitespace, use next-line scope.
            */
   
           if ('\0' == (*bufp)[pos]) {
                   r->last->endspan = 2;
                   goto out;
         }          }
   
         /* TODO: parse date. */          while (' ' == (*bufp)[pos])
                   pos++;
   
         assert(NULL == tree->last);          /* An opening brace requests multiline scope. */
         tree->state |= ROFF_PRELUDE_Dt;  
   
         return(1);          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 ('\0' == (*bufp)[pos])
                   mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
   
           r->last->endspan = 1;
   
   out:
           *offs = pos;
           return(ROFF_RERUN);
 }  }
   
   
 /* ARGSUSED */  /* ARGSUSED */
 static  int  static enum rofferr
 roff_Os(ROFFCALL_ARGS)  roff_ds(ROFF_ARGS)
 {  {
           char            *name, *string;
   
         if (ROFF_EXIT == type) {          /*
                 roffnode_free(tree);           * A symbol is named by the first word following the macro
                 return((*tree->cb.rofftail)(tree->arg));           * invocation up to a space.  Its value is anything after the
         } else if (ROFF_BODY & tree->state) {           * name's trailing whitespace and optional double-quote.  Thus,
                 assert( ! (ROFF_PRELUDE & tree->state));           *
                 assert(ROFF_PRELUDE_Os & tree->state);           *  [.ds foo "bar  "     ]
                 return(roff_text(tok, tree, argv, type));           *
         }           * will have `bar  "     ' as its value.
            */
   
         assert(ROFF_PRELUDE & tree->state);          string = *bufp + pos;
         if ( ! (ROFF_PRELUDE_Dt & tree->state) ||          name = roff_getname(r, &string, ln, pos);
                         ! (ROFF_PRELUDE_Dd & tree->state)) {          if ('\0' == *name)
                 roff_err(tree, *argv, "out-of-order `Os' in prelude");                  return(ROFF_IGN);
                 return(0);  
         }  
   
         /* TODO: extract OS. */          /* Read past initial double-quote. */
           if ('"' == *string)
                   string++;
   
         tree->state |= ROFF_PRELUDE_Os;          /* The rest is the value. */
         tree->state &= ~ROFF_PRELUDE;          roff_setstr(r, name, string, 0);
         tree->state |= ROFF_BODY;          return(ROFF_IGN);
   }
   
         assert(NULL == tree->last);  int
   roff_regisset(const struct roff *r, enum regs reg)
   {
   
         if (NULL == roffnode_new(tok, tree))          return(r->regs[(int)reg].set);
                 return(0);  }
   
         return((*tree->cb.roffhead)(tree->arg));  unsigned int
   roff_regget(const struct roff *r, enum regs reg)
   {
   
           return(r->regs[(int)reg].u);
 }  }
   
   void
   roff_regunset(struct roff *r, enum regs reg)
   {
   
           r->regs[(int)reg].set = 0;
   }
   
 /* ARGSUSED */  /* ARGSUSED */
 static int  static enum rofferr
 roff_layout(ROFFCALL_ARGS)  roff_nr(ROFF_ARGS)
 {  {
         int              i, c, argcp[ROFF_MAXARG];          const char      *key;
         char            *v, *argvp[ROFF_MAXARG];          char            *val;
           int              iv;
   
         if (ROFF_PRELUDE & tree->state) {          val = *bufp + pos;
                 roff_err(tree, *argv, "`%s' disallowed in prelude",          key = roff_getname(r, &val, ln, pos);
                                 toknames[tok]);  
                 return(0);          if (0 == strcmp(key, "nS")) {
                   r->regs[(int)REG_nS].set = 1;
                   if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
                           r->regs[(int)REG_nS].u = (unsigned)iv;
                   else
                           r->regs[(int)REG_nS].u = 0u;
         }          }
   
         if (ROFF_EXIT == type) {          return(ROFF_IGN);
                 roffnode_free(tree);  }
                 return((*tree->cb.roffblkout)(tree->arg, tok));  
         }  
   
         i = 0;  /* ARGSUSED */
         argv++;  static enum rofferr
   roff_rm(ROFF_ARGS)
   {
           const char       *name;
           char             *cp;
   
         while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {          cp = *bufp + pos;
                 if (ROFF_ARGMAX == c)          while ('\0' != *cp) {
                         return(0);                  name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
                   if ('\0' != *name)
                 argcp[i] = c;                          roff_setstr(r, name, NULL, 0);
                 argvp[i] = v;  
                 i++;  
                 argv++;  
         }          }
           return(ROFF_IGN);
   }
   
         argcp[i] = ROFF_ARGMAX;  /* ARGSUSED */
         argvp[i] = NULL;  static enum rofferr
   roff_Dd(ROFF_ARGS)
   {
           const char *const       *cp;
   
         if (NULL == roffnode_new(tok, tree))          if (MPARSE_MDOC != r->parsetype)
                 return(0);                  for (cp = __mdoc_reserved; *cp; cp++)
                           roff_setstr(r, *cp, NULL, 0);
   
         if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp, argvp))          return(ROFF_CONT);
                 return(0);  }
   
         if (NULL == *argv)  /* ARGSUSED */
                 return(1);  static enum rofferr
   roff_TH(ROFF_ARGS)
   {
           const char *const       *cp;
   
         if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))          if (MPARSE_MDOC != r->parsetype)
                 return(0);                  for (cp = __man_reserved; *cp; cp++)
                           roff_setstr(r, *cp, NULL, 0);
   
         if ( ! (ROFF_PARSED & tokens[tok].flags)) {          return(ROFF_CONT);
                 i = 0;  }
                 while (*argv) {  
                         if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))  
                                 return(0);  
                         i = 1;  
                 }  
                 return((*tree->cb.roffout)(tree->arg, tok));  
         }  
   
         i = 0;  /* ARGSUSED */
         while (*argv) {  static enum rofferr
                 if (ROFF_MAX == (c = rofffindcallable(*argv))) {  roff_TE(ROFF_ARGS)
                         assert(tree->arg);  {
                         if ( ! (*tree->cb.roffdata)  
                                         (tree->arg, i, *argv++))  
                                 return(0);  
                         i = 1;  
                         continue;  
                 }  
   
                 if (NULL == tokens[c].cb) {          if (NULL == r->tbl)
                         roff_err(tree, *argv, "unsupported macro `%s'",                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                                         toknames[c]);          else
                         return(0);                  tbl_end(&r->tbl);
                 }  
   
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))          return(ROFF_IGN);
                         return(0);  }
   
                 break;  /* ARGSUSED */
         }  static enum rofferr
   roff_T_(ROFF_ARGS)
   {
   
         /*          if (NULL == r->tbl)
          * If we're the first parser (*argv == tree->cur) then purge out                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
          * any additional punctuation, should there be any remaining at          else
          * the end of line.                  tbl_restart(ppos, ln, r->tbl);
          */  
   
         if ( ! (ROFF_PARSED & tokens[tok].flags && *argv))          return(ROFF_IGN);
                 return((*tree->cb.roffout)(tree->arg, tok));  }
   
         i = 0;  #if 0
         while (argv[i])  static int
                 i++;  roff_closeeqn(struct roff *r)
   {
   
         assert(i > 0);          return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
         if ( ! roffispunct(argv[--i]))  }
                 return((*tree->cb.roffout)(tree->arg, tok));  #endif
   
         while (i >= 0 && roffispunct(argv[i]))  static void
                 i--;  roff_openeqn(struct roff *r, const char *name, int line,
                   int offs, const char *buf)
   {
           struct eqn_node *e;
           int              poff;
   
         assert(0 != i);          assert(NULL == r->eqn);
         i++;          e = eqn_alloc(name, offs, line, r->parse);
   
         /* LINTED */          if (r->last_eqn)
         while (argv[i])                  r->last_eqn->next = e;
                 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))          else
                         return(0);                  r->first_eqn = r->last_eqn = e;
   
         return((*tree->cb.roffout)(tree->arg, tok));          r->eqn = r->last_eqn = e;
   
           if (buf) {
                   poff = 0;
                   eqn_read(&r->eqn, line, buf, offs, &poff);
           }
 }  }
   
   /* ARGSUSED */
   static enum rofferr
   roff_EQ(ROFF_ARGS)
   {
   
           roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
           return(ROFF_IGN);
   }
   
 /* ARGSUSED */  /* ARGSUSED */
 static int  static enum rofferr
 roff_text(ROFFCALL_ARGS)  roff_EN(ROFF_ARGS)
 {  {
         int              i, j, first, c, argcp[ROFF_MAXARG];  
         char            *v, *argvp[ROFF_MAXARG];  
   
         if (ROFF_PRELUDE & tree->state) {          mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
                 roff_err(tree, *argv, "`%s' disallowed in prelude",          return(ROFF_IGN);
                                 toknames[tok]);  }
                 return(0);  
   /* ARGSUSED */
   static enum rofferr
   roff_TS(ROFF_ARGS)
   {
           struct tbl_node *t;
   
           if (r->tbl) {
                   mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
                   tbl_end(&r->tbl);
         }          }
   
         /* FIXME: breaks if passed from roff_layout. */          t = tbl_alloc(ppos, ln, r->parse);
         first = *argv == tree->cur;  
   
         i = 0;          if (r->last_tbl)
         argv++;                  r->last_tbl->next = t;
           else
                   r->first_tbl = r->last_tbl = t;
   
         while (-1 != (c = roffnextopt(tree, tok, &argv, &v))) {          r->tbl = r->last_tbl = t;
                 if (ROFF_ARGMAX == c)          return(ROFF_IGN);
                         return(0);  }
   
                 argcp[i] = c;  /* ARGSUSED */
                 argvp[i] = v;  static enum rofferr
                 i++;  roff_cc(ROFF_ARGS)
                 argv++;  {
         }          const char      *p;
   
         argcp[i] = ROFF_ARGMAX;          p = *bufp + pos;
         argvp[i] = NULL;  
   
         if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp, argvp))          if ('\0' == *p || '.' == (r->control = *p++))
                 return(0);                  r->control = 0;
   
         if ( ! (ROFF_PARSED & tokens[tok].flags)) {          if ('\0' != *p)
                 i = 0;                  mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
                 while (*argv) {  
                         if ( ! (*tree->cb.roffdata)(tree->arg, i, *argv++))          return(ROFF_IGN);
                                 return(0);  }
                         i = 1;  
                 }  /* ARGSUSED */
                 return((*tree->cb.roffout)(tree->arg, tok));  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);
         }          }
   
         i = 0;          while ('\0' != *p) {
         while (*argv) {                  fsz = ssz = 1;
                 if (ROFF_MAX == (c = rofffindcallable(*argv))) {  
                         /*                  first = p++;
                          * If all that remains is roff punctuation, then                  if ('\\' == *first) {
                          * close out our scope and return.                          esc = mandoc_escape(&p, NULL, NULL);
                          */                          if (ESCAPE_ERROR == esc) {
                         if (roffispunct(*argv)) {                                  mandoc_msg
                                 for (j = 0; argv[j]; j++)                                          (MANDOCERR_BADESCAPE, r->parse,
                                         if ( ! roffispunct(argv[j]))                                           ln, (int)(p - *bufp), NULL);
                                                 break;                                  return(ROFF_IGN);
                                 if (NULL == argv[j])  
                                         break;  
                                 i = 1;  
                         }                          }
                           fsz = (size_t)(p - first);
                         if ( ! (*tree->cb.roffdata)                  }
                                         (tree->arg, i, *argv++))  
                                 return(0);  
   
                         i = 1;                  second = p++;
                   if ('\\' == *second) {
                           esc = mandoc_escape(&p, NULL, NULL);
                           if (ESCAPE_ERROR == esc) {
                                   mandoc_msg
                                           (MANDOCERR_BADESCAPE, r->parse,
                                            ln, (int)(p - *bufp), NULL);
                                   return(ROFF_IGN);
                           }
                           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 == r->xtab)
                  * A sub-command has been found.  Execute it and                          r->xtab = mandoc_calloc
                  * discontinue parsing for arguments.                                  (128, sizeof(struct roffstr));
                  */  
   
                 if (NULL == tokens[c].cb) {                  free(r->xtab[(int)*first].p);
                         roff_err(tree, *argv, "unsupported macro `%s'",                  r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
                                         toknames[c]);                  r->xtab[(int)*first].sz = ssz;
                         return(0);          }
                 }  
   
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))  
                         return(0);  
   
                 break;          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);
         }          }
   
         if ( ! (*tree->cb.roffout)(tree->arg, tok))          *offs = pos;
                 return(0);          return(ROFF_SO);
   }
   
         /*  /* ARGSUSED */
          * If we're the first parser (*argv == tree->cur) then purge out  static enum rofferr
          * any additional punctuation, should there be any remaining at  roff_userdef(ROFF_ARGS)
          * the end of line.  {
           const char       *arg[9];
           char             *cp, *n1, *n2;
           int               i;
   
           /*
            * Collect pointers to macro argument strings
            * and null-terminate them.
          */           */
           cp = *bufp + pos;
           for (i = 0; i < 9; i++)
                   arg[i] = '\0' == *cp ? "" :
                       mandoc_getarg(r->parse, &cp, ln, &pos);
   
         if ( ! (first && *argv))          /*
                 return(1);           * 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;
                   }
   
         i = 0;                  *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
         while (argv[i])                  n2 = mandoc_malloc(*szp);
                 i++;  
   
         assert(i > 0);                  strlcpy(n2, n1, (size_t)(cp - n1 + 1));
         if ( ! roffispunct(argv[--i]))                  strlcat(n2, arg[i], *szp);
                 return(1);                  strlcat(n2, cp + 3, *szp);
   
         while (i >= 0 && roffispunct(argv[i]))                  cp = n2 + (cp - n1);
                 i--;                  free(n1);
                   n1 = n2;
           }
   
         assert(0 != i);          /*
         i++;           * Replace the macro invocation
            * by the expanded macro.
            */
           free(*bufp);
           *bufp = n1;
           if (0 == *szp)
                   *szp = strlen(*bufp) + 1;
   
         /* LINTED */          return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
         while (argv[i])             ROFF_REPARSE : ROFF_APPEND);
                 if ( ! (*tree->cb.roffdata)(tree->arg, 0, argv[i++]))  }
                         return(0);  
   
         return(1);  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.
    * In multiline mode, append to an existing entry and append '\n';
    * else replace the existing entry, if there is one.
    * To clear an existing entry, call with (*r, *name, NULL, 0).
    */
   static void
   roff_setstr(struct roff *r, const char *name, const char *string,
           int multiline)
   {
   
 /* ARGSUSED */          roff_setstrn(&r->strtab, name, strlen(name), string,
 static int                          string ? strlen(string) : 0, multiline);
 roff_comment(ROFFCALL_ARGS)  }
   
   static void
   roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
                   const char *string, size_t stringsz, int multiline)
 {  {
           struct roffkv   *n;
           char            *c;
           int              i;
           size_t           oldch, newch;
   
         return(1);          /* 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 == multiline) {
                   /* In multiline mode, append; else replace. */
                   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 + (multiline ? 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 (multiline)
                   *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;
   
 /* ARGSUSED */          for (n = r->strtab; n; n = n->next)
 static int                  if (0 == strncmp(name, n->key.p, len) &&
 roff_close(ROFFCALL_ARGS)                                  '\0' == n->key.p[(int)len])
                           return(n->val.p);
   
           return(NULL);
   }
   
   static void
   roff_freestr(struct roffkv *r)
 {  {
           struct roffkv    *n, *nn;
   
         return(1);          for (n = r; n; n = nn) {
                   free(n->key.p);
                   free(n->val.p);
                   nn = n->next;
                   free(n);
           }
 }  }
   
   const struct tbl_span *
   roff_span(const struct roff *r)
   {
   
           return(r->tbl ? tbl_span(r->tbl) : NULL);
   }
   
 #if notyet  const struct eqn *
 /* ARGSUSED */  roff_eqn(const struct roff *r)
 static int  
 roff_Ns(ROFFCALL_ARGS)  
 {  {
         int              c;  
           return(r->last_eqn ? &r->last_eqn->eqn : NULL);
   }
   
         argv++;  /*
    * 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 (ROFF_MAX != (c = rofffindcallable(*argv))) {          if (NULL == r->xmbtab && NULL == r->xtab)
                 if (NULL == tokens[c].cb) {                  return(mandoc_strdup(p));
                         roff_err(tree, *argv, "unsupported macro `%s'",          else if ('\0' == *p)
                                         toknames[c]);                  return(mandoc_strdup(""));
                         return(0);  
           /*
            * 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;
                 }                  }
                 if ( ! (*tree->cb.roffspecial)(tree->arg, tok))  
                         return(0);  
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))  
                         return(0);  
   
                 return(1);                  /* Search for term matches. */
         } else if ( ! (*tree->cb.roffdata)(tree->arg, 0, *argv++))                  for (cp = r->xmbtab; cp; cp = cp->next)
                 return(0);                          if (0 == strncmp(p, cp->key.p, cp->key.sz))
                                   break;
         while (*argv) {  
                 if (ROFF_MAX == (c = rofffindcallable(*argv))) {                  if (NULL != cp) {
                         assert(tree->arg);                          /*
                         if ( ! (*tree->cb.roffdata)                           * A match has been found.
                                         (tree->arg, 1, *argv++))                           * Append the match to the array and move
                                 return(0);                           * 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;                          continue;
                 }                  }
                 if (NULL == tokens[c].cb) {  
                         roff_err(tree, *argv, "unsupported macro `%s'",  
                                         toknames[c]);  
                         return(0);  
                 }  
                 if ( ! (*tokens[c].cb)(c, tree, argv, ROFF_ENTER))  
                         return(0);  
   
                 break;                  /*
                    * 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;
         }          }
   
         return(1);          res[(int)ssz] = '\0';
           return(res);
 }  }
 #endif  
   
   /*
 static void   * Find out whether a line is a macro line or not.
 roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)   * 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)
 {  {
         va_list          ap;          int             pos;
         char             buf[128];  
   
         va_start(ap, fmt);          pos = *ppos;
         (void)vsnprintf(buf, sizeof(buf), fmt, ap);  
         va_end(ap);  
   
         (*tree->cb.roffmsg)(tree->arg,          if (0 != r->control && cp[pos] == r->control)
                         ROFF_WARN, tree->cur, pos, buf);                  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++;
   
 static void          *ppos = pos;
 roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)          return(1);
 {  
         va_list          ap;  
         char             buf[128];  
   
         va_start(ap, fmt);  
         (void)vsnprintf(buf, sizeof(buf), fmt, ap);  
         va_end(ap);  
   
         (*tree->cb.roffmsg)(tree->arg,  
                         ROFF_ERROR, tree->cur, pos, buf);  
 }  }

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

CVSweb