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

Diff for /mandoc/eqn.c between version 1.49 and 1.64

version 1.49, 2014/10/10 09:12:44 version 1.64, 2017/06/21 18:04:34
Line 1 
Line 1 
 /*      $Id$ */  /*      $Id$ */
 /*  /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
    * Copyright (c) 2014, 2015, 2017 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 above   * purpose with or without fee is hereby granted, provided that the above
Line 30 
Line 31 
 #include "libmandoc.h"  #include "libmandoc.h"
 #include "libroff.h"  #include "libroff.h"
   
 #define EQN_MSG(t, x) \  
         mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)  
 #define EQN_NEST_MAX     128 /* maximum nesting of defines */  #define EQN_NEST_MAX     128 /* maximum nesting of defines */
 #define STRNEQ(p1, sz1, p2, sz2) \  #define STRNEQ(p1, sz1, p2, sz2) \
         ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))          ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
 #define EQNSTREQ(x, p, sz) \  
         STRNEQ((x)->name, (x)->sz, (p), (sz))  
   
 enum    eqn_tok {  enum    eqn_tok {
         EQN_TOK_DYAD = 0,          EQN_TOK_DYAD = 0,
Line 83  enum eqn_tok {
Line 80  enum eqn_tok {
         EQN_TOK_TDEFINE,          EQN_TOK_TDEFINE,
         EQN_TOK_NDEFINE,          EQN_TOK_NDEFINE,
         EQN_TOK_UNDEF,          EQN_TOK_UNDEF,
         EQN_TOK_EOF,  
         EQN_TOK_ABOVE,          EQN_TOK_ABOVE,
         EQN_TOK__MAX          EQN_TOK__MAX,
           EQN_TOK_FUNC,
           EQN_TOK_EOF
 };  };
   
 static  const char *eqn_toks[EQN_TOK__MAX] = {  static  const char *eqn_toks[EQN_TOK__MAX] = {
Line 133  static const char *eqn_toks[EQN_TOK__MAX] = {
Line 131  static const char *eqn_toks[EQN_TOK__MAX] = {
         "tdefine", /* EQN_TOK_TDEFINE */          "tdefine", /* EQN_TOK_TDEFINE */
         "ndefine", /* EQN_TOK_NDEFINE */          "ndefine", /* EQN_TOK_NDEFINE */
         "undef", /* EQN_TOK_UNDEF */          "undef", /* EQN_TOK_UNDEF */
         NULL, /* EQN_TOK_EOF */  
         "above", /* EQN_TOK_ABOVE */          "above", /* EQN_TOK_ABOVE */
 };  };
   
   static  const char *const eqn_func[] = {
           "acos", "acsc", "and",  "arc",  "asec", "asin", "atan",
           "cos",  "cosh", "coth", "csc",  "det",  "exp",  "for",
           "if",   "lim",  "ln",   "log",  "max",  "min",
           "sec",  "sin",  "sinh", "tan",  "tanh", "Im",   "Re",
   };
   
 enum    eqn_symt {  enum    eqn_symt {
         EQNSYM_alpha,          EQNSYM_alpha,
         EQNSYM_beta,          EQNSYM_beta,
Line 197  enum eqn_symt {
Line 201  enum eqn_symt {
         EQNSYM_equiv,          EQNSYM_equiv,
         EQNSYM_lessequal,          EQNSYM_lessequal,
         EQNSYM_moreequal,          EQNSYM_moreequal,
           EQNSYM_minus,
         EQNSYM__MAX          EQNSYM__MAX
 };  };
   
Line 252  static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
Line 257  static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
         { "cdot", "pc" }, /* EQNSYM_cdot */          { "cdot", "pc" }, /* EQNSYM_cdot */
         { "nothing", "&" }, /* EQNSYM_nothing */          { "nothing", "&" }, /* EQNSYM_nothing */
         { "approx", "~~" }, /* EQNSYM_approx */          { "approx", "~~" }, /* EQNSYM_approx */
         { "prime", "aq" }, /* EQNSYM_prime */          { "prime", "fm" }, /* EQNSYM_prime */
         { "half", "12" }, /* EQNSYM_half */          { "half", "12" }, /* EQNSYM_half */
         { "partial", "pd" }, /* EQNSYM_partial */          { "partial", "pd" }, /* EQNSYM_partial */
         { "inf", "if" }, /* EQNSYM_inf */          { "inf", "if" }, /* EQNSYM_inf */
Line 265  static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
Line 270  static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
         { "==", "==" }, /* EQNSYM_equiv */          { "==", "==" }, /* EQNSYM_equiv */
         { "<=", "<=" }, /* EQNSYM_lessequal */          { "<=", "<=" }, /* EQNSYM_lessequal */
         { ">=", ">=" }, /* EQNSYM_moreequal */          { ">=", ">=" }, /* EQNSYM_moreequal */
           { "-", "mi" }, /* EQNSYM_minus */
 };  };
   
   static  struct eqn_box  *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
   static  void             eqn_box_free(struct eqn_box *);
   static  struct eqn_box  *eqn_box_makebinary(struct eqn_node *,
                                   enum eqn_post, struct eqn_box *);
   static  void             eqn_def(struct eqn_node *);
   static  struct eqn_def  *eqn_def_find(struct eqn_node *, const char *, size_t);
   static  void             eqn_delim(struct eqn_node *);
   static  const char      *eqn_next(struct eqn_node *, char, size_t *, int);
   static  const char      *eqn_nextrawtok(struct eqn_node *, size_t *);
   static  const char      *eqn_nexttok(struct eqn_node *, size_t *);
   static  enum rofferr     eqn_parse(struct eqn_node *, struct eqn_box *);
   static  enum eqn_tok     eqn_tok_parse(struct eqn_node *, char **);
   static  void             eqn_undef(struct eqn_node *);
   
   
 enum rofferr  enum rofferr
 eqn_read(struct eqn_node **epp, int ln,  eqn_read(struct eqn_node **epp, int ln,
                 const char *p, int pos, int *offs)                  const char *p, int pos, int *offs)
Line 288  eqn_read(struct eqn_node **epp, int ln,
Line 309  eqn_read(struct eqn_node **epp, int ln,
                 while (' ' == *p || '\t' == *p)                  while (' ' == *p || '\t' == *p)
                         p++;                          p++;
                 if ('\0' == *p)                  if ('\0' == *p)
                         return(er);                          return er;
                 mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse,                  mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse,
                     ln, pos, "EN %s", p);                      ln, pos, "EN %s", p);
                 return(er);                  return er;
         }          }
   
         /*          /*
Line 310  eqn_read(struct eqn_node **epp, int ln,
Line 331  eqn_read(struct eqn_node **epp, int ln,
         ep->sz += sz;          ep->sz += sz;
         strlcat(ep->data, p + pos, ep->sz + 1);          strlcat(ep->data, p + pos, ep->sz + 1);
         strlcat(ep->data, " ", ep->sz + 1);          strlcat(ep->data, " ", ep->sz + 1);
         return(ROFF_IGN);          return ROFF_IGN;
 }  }
   
 struct eqn_node *  struct eqn_node *
 eqn_alloc(const char *name, int pos, int line, struct mparse *parse)  eqn_alloc(int pos, int line, struct mparse *parse)
 {  {
         struct eqn_node *p;          struct eqn_node *p;
         size_t           sz;  
         const char      *end;  
   
         p = mandoc_calloc(1, sizeof(struct eqn_node));          p = mandoc_calloc(1, sizeof(struct eqn_node));
   
         if (name && '\0' != *name) {  
                 sz = strlen(name);  
                 assert(sz);  
                 do {  
                         sz--;  
                         end = name + (int)sz;  
                 } while (' ' == *end || '\t' == *end);  
                 p->eqn.name = mandoc_strndup(name, sz + 1);  
         }  
   
         p->parse = parse;          p->parse = parse;
         p->eqn.ln = line;          p->eqn.ln = line;
         p->eqn.pos = pos;          p->eqn.pos = pos;
         p->gsize = EQN_DEFSIZE;          p->gsize = EQN_DEFSIZE;
   
         return(p);          return p;
 }  }
   
 /*  /*
Line 351  eqn_def_find(struct eqn_node *ep, const char *key, siz
Line 360  eqn_def_find(struct eqn_node *ep, const char *key, siz
         for (i = 0; i < (int)ep->defsz; i++)          for (i = 0; i < (int)ep->defsz; i++)
                 if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,                  if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
                     ep->defs[i].keysz, key, sz))                      ep->defs[i].keysz, key, sz))
                         return(&ep->defs[i]);                          return &ep->defs[i];
   
         return(NULL);          return NULL;
 }  }
   
 /*  /*
Line 364  eqn_def_find(struct eqn_node *ep, const char *key, siz
Line 373  eqn_def_find(struct eqn_node *ep, const char *key, siz
 static const char *  static const char *
 eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)  eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
 {  {
           static size_t    last_len;
           static int       lim;
   
         char            *start, *next;          char            *start, *next;
         int              q, diff, lim;          int              q, diff;
         size_t           ssz, dummy;          size_t           ssz, dummy;
         struct eqn_def  *def;          struct eqn_def  *def;
   
         if (NULL == sz)          if (NULL == sz)
                 sz = &dummy;                  sz = &dummy;
   
         lim = 0;          if (ep->cur >= last_len)
                   lim = 0;
         ep->rew = ep->cur;          ep->rew = ep->cur;
 again:  again:
         /* Prevent self-definitions. */          /* Prevent self-definitions. */
   
         if (lim >= EQN_NEST_MAX) {          if (lim >= EQN_NEST_MAX) {
                 EQN_MSG(MANDOCERR_ROFFLOOP, ep);                  mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
                 return(NULL);                      ep->eqn.ln, ep->eqn.pos, NULL);
                   return NULL;
         }          }
   
         ep->cur = ep->rew;          ep->cur = ep->rew;
Line 387  again:
Line 401  again:
         q = 0;          q = 0;
   
         if ('\0' == *start)          if ('\0' == *start)
                 return(NULL);                  return NULL;
   
         if (quote == *start) {          if (quote == *start) {
                 ep->cur++;                  ep->cur++;
Line 419  again:
Line 433  again:
                         ep->cur++;                          ep->cur++;
         } else {          } else {
                 if (q)                  if (q)
                         EQN_MSG(MANDOCERR_ARG_QUOTE, ep);                          mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
                               ep->eqn.ln, ep->eqn.pos, NULL);
                 next = strchr(start, '\0');                  next = strchr(start, '\0');
                 *sz = (size_t)(next - start);                  *sz = (size_t)(next - start);
                 ep->cur += *sz;                  ep->cur += *sz;
Line 428  again:
Line 443  again:
         /* Quotes aren't expanded for values. */          /* Quotes aren't expanded for values. */
   
         if (q || ! repl)          if (q || ! repl)
                 return(start);                  return start;
   
         if (NULL != (def = eqn_def_find(ep, start, *sz))) {          if (NULL != (def = eqn_def_find(ep, start, *sz))) {
                 diff = def->valsz - *sz;                  diff = def->valsz - *sz;
Line 444  again:
Line 459  again:
                 memmove(start + *sz + diff, start + *sz,                  memmove(start + *sz + diff, start + *sz,
                     (strlen(start) - *sz) + 1);                      (strlen(start) - *sz) + 1);
                 memcpy(start, def->val, def->valsz);                  memcpy(start, def->val, def->valsz);
                   last_len = start - ep->data + def->valsz;
                   lim++;
                 goto again;                  goto again;
         }          }
   
         return(start);          return start;
 }  }
   
 /*  /*
Line 458  static const char *
Line 475  static const char *
 eqn_nexttok(struct eqn_node *ep, size_t *sz)  eqn_nexttok(struct eqn_node *ep, size_t *sz)
 {  {
   
         return(eqn_next(ep, '"', sz, 1));          return eqn_next(ep, '"', sz, 1);
 }  }
   
 /*  /*
Line 468  static const char *
Line 485  static const char *
 eqn_nextrawtok(struct eqn_node *ep, size_t *sz)  eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
 {  {
   
         return(eqn_next(ep, '"', sz, 0));          return eqn_next(ep, '"', sz, 0);
 }  }
   
 /*  /*
Line 486  eqn_tok_parse(struct eqn_node *ep, char **p)
Line 503  eqn_tok_parse(struct eqn_node *ep, char **p)
 {  {
         const char      *start;          const char      *start;
         size_t           i, sz;          size_t           i, sz;
           int              quoted;
   
         if (NULL != p)          if (p != NULL)
                 *p = NULL;                  *p = NULL;
   
         if (NULL == (start = eqn_nexttok(ep, &sz)))          quoted = ep->data[ep->cur] == '"';
                 return(EQN_TOK_EOF);  
   
         for (i = 0; i < EQN_TOK__MAX; i++) {          if ((start = eqn_nexttok(ep, &sz)) == NULL)
                 if (NULL == eqn_toks[i])                  return EQN_TOK_EOF;
                         continue;  
                 if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i])))          if (quoted) {
                         break;                  if (p != NULL)
                           *p = mandoc_strndup(start, sz);
                   return EQN_TOK__MAX;
         }          }
   
         if (i == EQN_TOK__MAX && NULL != p)          for (i = 0; i < EQN_TOK__MAX; i++)
                   if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i])))
                           return i;
   
           if (p != NULL)
                 *p = mandoc_strndup(start, sz);                  *p = mandoc_strndup(start, sz);
   
         return(i);          for (i = 0; i < sizeof(eqn_func)/sizeof(*eqn_func); i++)
                   if (STRNEQ(start, sz, eqn_func[i], strlen(eqn_func[i])))
                           return EQN_TOK_FUNC;
   
           return EQN_TOK__MAX;
 }  }
   
 static void  static void
Line 537  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
Line 564  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
         bp->expectargs = UINT_MAX;          bp->expectargs = UINT_MAX;
         bp->size = ep->gsize;          bp->size = ep->gsize;
   
         assert(NULL != parent);  
   
         if (NULL != parent->first) {          if (NULL != parent->first) {
                 assert(NULL != parent->last);  
                 parent->last->next = bp;                  parent->last->next = bp;
                 bp->prev = parent->last;                  bp->prev = parent->last;
         } else          } else
                 parent->first = bp;                  parent->first = bp;
   
         parent->last = bp;          parent->last = bp;
         return(bp);          return bp;
 }  }
   
 /*  /*
Line 557  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
Line 581  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
  * The new EQN_SUBEXPR will have a two-child limit.   * The new EQN_SUBEXPR will have a two-child limit.
  */   */
 static struct eqn_box *  static struct eqn_box *
 eqn_box_makebinary(struct eqn_node *ep,  eqn_box_makebinary(struct eqn_node *ep,
         enum eqn_post pos, struct eqn_box *parent)          enum eqn_post pos, struct eqn_box *parent)
 {  {
         struct eqn_box  *b, *newb;          struct eqn_box  *b, *newb;
Line 577  eqn_box_makebinary(struct eqn_node *ep, 
Line 601  eqn_box_makebinary(struct eqn_node *ep, 
         newb->first = newb->last = b;          newb->first = newb->last = b;
         newb->first->next = NULL;          newb->first->next = NULL;
         b->parent = newb;          b->parent = newb;
         return(newb);          return newb;
 }  }
   
 /*  /*
    * Parse the "delim" control statement.
    */
   static void
   eqn_delim(struct eqn_node *ep)
   {
           const char      *start;
           size_t           sz;
   
           if ((start = eqn_nextrawtok(ep, &sz)) == NULL)
                   mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                       ep->eqn.ln, ep->eqn.pos, "delim");
           else if (strncmp(start, "off", 3) == 0)
                   ep->delim = 0;
           else if (strncmp(start, "on", 2) == 0) {
                   if (ep->odelim && ep->cdelim)
                           ep->delim = 1;
           } else if (start[1] != '\0') {
                   ep->odelim = start[0];
                   ep->cdelim = start[1];
                   ep->delim = 1;
           }
   }
   
   /*
  * Undefine a previously-defined string.   * Undefine a previously-defined string.
  */   */
 static int  static void
 eqn_undef(struct eqn_node *ep)  eqn_undef(struct eqn_node *ep)
 {  {
         const char      *start;          const char      *start;
         struct eqn_def  *def;          struct eqn_def  *def;
         size_t           sz;          size_t           sz;
   
         if (NULL == (start = eqn_nextrawtok(ep, &sz))) {          if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
                 EQN_MSG(MANDOCERR_EQNEOF, ep);                  mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                 return(0);                      ep->eqn.ln, ep->eqn.pos, "undef");
         } else if (NULL != (def = eqn_def_find(ep, start, sz)))                  return;
                 def->keysz = 0;          }
           if ((def = eqn_def_find(ep, start, sz)) == NULL)
         return(1);                  return;
           free(def->key);
           free(def->val);
           def->key = def->val = NULL;
           def->keysz = def->valsz = 0;
 }  }
   
 static int  static void
 eqn_def(struct eqn_node *ep)  eqn_def(struct eqn_node *ep)
 {  {
         const char      *start;          const char      *start;
Line 607  eqn_def(struct eqn_node *ep)
Line 659  eqn_def(struct eqn_node *ep)
         struct eqn_def  *def;          struct eqn_def  *def;
         int              i;          int              i;
   
         if (NULL == (start = eqn_nextrawtok(ep, &sz))) {          if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
                 EQN_MSG(MANDOCERR_EQNEOF, ep);                  mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                 return(0);                      ep->eqn.ln, ep->eqn.pos, "define");
                   return;
         }          }
   
         /*          /*
Line 629  eqn_def(struct eqn_node *ep)
Line 682  eqn_def(struct eqn_node *ep)
                         ep->defs[i].key = ep->defs[i].val = NULL;                          ep->defs[i].key = ep->defs[i].val = NULL;
                 }                  }
   
                 ep->defs[i].keysz = sz;                  def = ep->defs + i;
                 ep->defs[i].key = mandoc_realloc(                  free(def->key);
                     ep->defs[i].key, sz + 1);                  def->key = mandoc_strndup(start, sz);
                   def->keysz = sz;
                 memcpy(ep->defs[i].key, start, sz);  
                 ep->defs[i].key[(int)sz] = '\0';  
                 def = &ep->defs[i];  
         }          }
   
         start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);          start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
           if (start == NULL) {
         if (NULL == start) {                  mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
                 EQN_MSG(MANDOCERR_EQNEOF, ep);                      ep->eqn.ln, ep->eqn.pos, "define %s", def->key);
                 return(-1);                  free(def->key);
                   free(def->val);
                   def->key = def->val = NULL;
                   def->keysz = def->valsz = 0;
                   return;
         }          }
           free(def->val);
           def->val = mandoc_strndup(start, sz);
         def->valsz = sz;          def->valsz = sz;
         def->val = mandoc_realloc(def->val, sz + 1);  
         memcpy(def->val, start, sz);  
         def->val[(int)sz] = '\0';  
         return(1);  
 }  }
   
 /*  /*
  * Recursively parse an eqn(7) expression.   * Recursively parse an eqn(7) expression.
  */   */
 static int  static enum rofferr
 eqn_parse(struct eqn_node *ep, struct eqn_box *parent)  eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
 {  {
         char            *p;  
         enum eqn_tok     tok;  
         enum eqn_post    pos;  
         struct eqn_box  *cur;  
         int              rc, size;  
         size_t           i, sz;  
         char             sym[64];          char             sym[64];
           struct eqn_box  *cur;
         const char      *start;          const char      *start;
           char            *p;
           size_t           i, sz;
           enum eqn_tok     tok, subtok;
           enum eqn_post    pos;
           int              size;
   
         assert(NULL != parent);          assert(parent != NULL);
 again:  
           /*
         switch ((tok = eqn_tok_parse(ep, &p))) {           * Empty equation.
         case (EQN_TOK_UNDEF):           * Do not add it to the high-level syntax tree.
                 if ((rc = eqn_undef(ep)) <= 0)           */
                         return(rc);  
           if (ep->data == NULL)
                   return ROFF_IGN;
   
   next_tok:
           tok = eqn_tok_parse(ep, &p);
   
   this_tok:
           switch (tok) {
           case EQN_TOK_UNDEF:
                   eqn_undef(ep);
                 break;                  break;
         case (EQN_TOK_NDEFINE):          case EQN_TOK_NDEFINE:
         case (EQN_TOK_DEFINE):          case EQN_TOK_DEFINE:
                 if ((rc = eqn_def(ep)) <= 0)                  eqn_def(ep);
                         return(rc);  
                 break;                  break;
         case (EQN_TOK_TDEFINE):          case EQN_TOK_TDEFINE:
                 if (NULL == eqn_nextrawtok(ep, NULL))                  if (eqn_nextrawtok(ep, NULL) == NULL ||
                         EQN_MSG(MANDOCERR_EQNEOF, ep);                      eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL)
                 else if (NULL == eqn_next(ep,                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                                 ep->data[(int)ep->cur], NULL, 0))                              ep->eqn.ln, ep->eqn.pos, "tdefine");
                         EQN_MSG(MANDOCERR_EQNEOF, ep);  
                 break;                  break;
         case (EQN_TOK_DELIM):          case EQN_TOK_DELIM:
         case (EQN_TOK_GFONT):                  eqn_delim(ep);
                 if (NULL == eqn_nextrawtok(ep, NULL)) {  
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                         return(-1);  
                 }  
                 break;                  break;
         case (EQN_TOK_MARK):          case EQN_TOK_GFONT:
         case (EQN_TOK_LINEUP):                  if (eqn_nextrawtok(ep, NULL) == NULL)
                           mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                               ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                   break;
           case EQN_TOK_MARK:
           case EQN_TOK_LINEUP:
                 /* Ignore these. */                  /* Ignore these. */
                 break;                  break;
         case (EQN_TOK_DYAD):          case EQN_TOK_DYAD:
         case (EQN_TOK_VEC):          case EQN_TOK_VEC:
         case (EQN_TOK_UNDER):          case EQN_TOK_UNDER:
         case (EQN_TOK_BAR):          case EQN_TOK_BAR:
         case (EQN_TOK_TILDE):          case EQN_TOK_TILDE:
         case (EQN_TOK_HAT):          case EQN_TOK_HAT:
         case (EQN_TOK_DOT):          case EQN_TOK_DOT:
         case (EQN_TOK_DOTDOT):          case EQN_TOK_DOTDOT:
                 if (NULL == parent->last) {                  if (parent->last == NULL) {
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                          mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
                         return(-1);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           cur = eqn_box_alloc(ep, parent);
                           cur->type = EQN_TEXT;
                           cur->text = mandoc_strdup("");
                 }                  }
                 parent = eqn_box_makebinary                  parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent);
                         (ep, EQNPOS_NONE, parent);  
                 parent->type = EQN_LISTONE;                  parent->type = EQN_LISTONE;
                 parent->expectargs = 1;                  parent->expectargs = 1;
                 switch (tok) {                  switch (tok) {
                 case (EQN_TOK_DOTDOT):                  case EQN_TOK_DOTDOT:
                         strlcpy(sym, "\\[ad]", sizeof(sym));                          strlcpy(sym, "\\[ad]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_VEC):                  case EQN_TOK_VEC:
                         strlcpy(sym, "\\[->]", sizeof(sym));                          strlcpy(sym, "\\[->]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_DYAD):                  case EQN_TOK_DYAD:
                         strlcpy(sym, "\\[<>]", sizeof(sym));                          strlcpy(sym, "\\[<>]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_TILDE):                  case EQN_TOK_TILDE:
                         strlcpy(sym, "\\[a~]", sizeof(sym));                          strlcpy(sym, "\\[a~]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_UNDER):                  case EQN_TOK_UNDER:
                         strlcpy(sym, "\\[ul]", sizeof(sym));                          strlcpy(sym, "\\[ul]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_BAR):                  case EQN_TOK_BAR:
                         strlcpy(sym, "\\[rl]", sizeof(sym));                          strlcpy(sym, "\\[rl]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_DOT):                  case EQN_TOK_DOT:
                         strlcpy(sym, "\\[a.]", sizeof(sym));                          strlcpy(sym, "\\[a.]", sizeof(sym));
                         break;                          break;
                 case (EQN_TOK_HAT):                  case EQN_TOK_HAT:
                         strlcpy(sym, "\\[ha]", sizeof(sym));                          strlcpy(sym, "\\[ha]", sizeof(sym));
                         break;                          break;
                 default:                  default:
Line 744  again:
Line 806  again:
                 }                  }
   
                 switch (tok) {                  switch (tok) {
                 case (EQN_TOK_DOTDOT):                  case EQN_TOK_DOTDOT:
                 case (EQN_TOK_VEC):                  case EQN_TOK_VEC:
                 case (EQN_TOK_DYAD):                  case EQN_TOK_DYAD:
                 case (EQN_TOK_TILDE):                  case EQN_TOK_TILDE:
                 case (EQN_TOK_BAR):                  case EQN_TOK_BAR:
                 case (EQN_TOK_DOT):                  case EQN_TOK_DOT:
                 case (EQN_TOK_HAT):                  case EQN_TOK_HAT:
                         parent->top = mandoc_strdup(sym);                          parent->top = mandoc_strdup(sym);
                         break;                          break;
                 case (EQN_TOK_UNDER):                  case EQN_TOK_UNDER:
                         parent->bottom = mandoc_strdup(sym);                          parent->bottom = mandoc_strdup(sym);
                         break;                          break;
                 default:                  default:
Line 761  again:
Line 823  again:
                 }                  }
                 parent = parent->parent;                  parent = parent->parent;
                 break;                  break;
         case (EQN_TOK_FWD):          case EQN_TOK_FWD:
         case (EQN_TOK_BACK):          case EQN_TOK_BACK:
         case (EQN_TOK_DOWN):          case EQN_TOK_DOWN:
         case (EQN_TOK_UP):          case EQN_TOK_UP:
                 tok = eqn_tok_parse(ep, NULL);                  subtok = eqn_tok_parse(ep, NULL);
                 if (EQN_TOK__MAX != tok) {                  if (subtok != EQN_TOK__MAX) {
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                         return(-1);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           tok = subtok;
                           goto this_tok;
                 }                  }
                 break;                  break;
         case (EQN_TOK_FAT):          case EQN_TOK_FAT:
         case (EQN_TOK_ROMAN):          case EQN_TOK_ROMAN:
         case (EQN_TOK_ITALIC):          case EQN_TOK_ITALIC:
         case (EQN_TOK_BOLD):          case EQN_TOK_BOLD:
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                 /*                  /*
                  * These values apply to the next word or sequence of                   * These values apply to the next word or sequence of
                  * words; thus, we mark that we'll have a child with                   * words; thus, we mark that we'll have a child with
Line 789  again:
Line 850  again:
                 parent->type = EQN_LISTONE;                  parent->type = EQN_LISTONE;
                 parent->expectargs = 1;                  parent->expectargs = 1;
                 switch (tok) {                  switch (tok) {
                 case (EQN_TOK_FAT):                  case EQN_TOK_FAT:
                         parent->font = EQNFONT_FAT;                          parent->font = EQNFONT_FAT;
                         break;                          break;
                 case (EQN_TOK_ROMAN):                  case EQN_TOK_ROMAN:
                         parent->font = EQNFONT_ROMAN;                          parent->font = EQNFONT_ROMAN;
                         break;                          break;
                 case (EQN_TOK_ITALIC):                  case EQN_TOK_ITALIC:
                         parent->font = EQNFONT_ITALIC;                          parent->font = EQNFONT_ITALIC;
                         break;                          break;
                 case (EQN_TOK_BOLD):                  case EQN_TOK_BOLD:
                         parent->font = EQNFONT_BOLD;                          parent->font = EQNFONT_BOLD;
                         break;                          break;
                 default:                  default:
                         abort();                          abort();
                 }                  }
                 break;                  break;
         case (EQN_TOK_SIZE):          case EQN_TOK_SIZE:
         case (EQN_TOK_GSIZE):          case EQN_TOK_GSIZE:
                 /* Accept two values: integral size and a single. */                  /* Accept two values: integral size and a single. */
                 if (NULL == (start = eqn_nexttok(ep, &sz))) {                  if (NULL == (start = eqn_nexttok(ep, &sz))) {
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                         return(-1);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           break;
                 }                  }
                 size = mandoc_strntoi(start, sz, 10);                  size = mandoc_strntoi(start, sz, 10);
                 if (-1 == size) {                  if (-1 == size) {
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                          mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
                         return(-1);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           break;
                 }                  }
                 if (EQN_TOK_GSIZE == tok) {                  if (EQN_TOK_GSIZE == tok) {
                         ep->gsize = size;                          ep->gsize = size;
Line 826  again:
Line 889  again:
                 parent->expectargs = 1;                  parent->expectargs = 1;
                 parent->size = size;                  parent->size = size;
                 break;                  break;
         case (EQN_TOK_FROM):          case EQN_TOK_FROM:
         case (EQN_TOK_TO):          case EQN_TOK_TO:
         case (EQN_TOK_SUB):          case EQN_TOK_SUB:
         case (EQN_TOK_SUP):          case EQN_TOK_SUP:
                 /*                  /*
                  * We have a left-right-associative expression.                   * We have a left-right-associative expression.
                  * Repivot under a positional node, open a child scope                   * Repivot under a positional node, open a child scope
                  * and keep on reading.                   * and keep on reading.
                  */                   */
                 if (NULL == parent->last) {                  if (parent->last == NULL) {
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                          mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
                         return(-1);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           cur = eqn_box_alloc(ep, parent);
                           cur->type = EQN_TEXT;
                           cur->text = mandoc_strdup("");
                 }                  }
                 /* Handle the "subsup" and "fromto" positions. */                  /* Handle the "subsup" and "fromto" positions. */
                 if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {                  if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {
Line 851  again:
Line 917  again:
                         break;                          break;
                 }                  }
                 switch (tok) {                  switch (tok) {
                 case (EQN_TOK_FROM):                  case EQN_TOK_FROM:
                         pos = EQNPOS_FROM;                          pos = EQNPOS_FROM;
                         break;                          break;
                 case (EQN_TOK_TO):                  case EQN_TOK_TO:
                         pos = EQNPOS_TO;                          pos = EQNPOS_TO;
                         break;                          break;
                 case (EQN_TOK_SUP):                  case EQN_TOK_SUP:
                         pos = EQNPOS_SUP;                          pos = EQNPOS_SUP;
                         break;                          break;
                 case (EQN_TOK_SUB):                  case EQN_TOK_SUB:
                         pos = EQNPOS_SUB;                          pos = EQNPOS_SUB;
                         break;                          break;
                 default:                  default:
Line 868  again:
Line 934  again:
                 }                  }
                 parent = eqn_box_makebinary(ep, pos, parent);                  parent = eqn_box_makebinary(ep, pos, parent);
                 break;                  break;
         case (EQN_TOK_SQRT):          case EQN_TOK_SQRT:
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);                  /*
                                 return(-1);  
                         }  
                 /*  
                  * Accept a left-right-associative set of arguments just                   * Accept a left-right-associative set of arguments just
                  * like sub and sup and friends but without rebalancing                   * like sub and sup and friends but without rebalancing
                  * under a pivot.                   * under a pivot.
Line 884  again:
Line 947  again:
                 parent->pos = EQNPOS_SQRT;                  parent->pos = EQNPOS_SQRT;
                 parent->expectargs = 1;                  parent->expectargs = 1;
                 break;                  break;
         case (EQN_TOK_OVER):          case EQN_TOK_OVER:
                 /*                  /*
                  * We have a right-left-associative fraction.                   * We have a right-left-associative fraction.
                  * Close out anything that's currently open, then                   * Close out anything that's currently open, then
                  * rebalance and continue reading.                   * rebalance and continue reading.
                  */                   */
                 if (NULL == parent->last) {                  if (parent->last == NULL) {
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                          mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
                         return(-1);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           cur = eqn_box_alloc(ep, parent);
                           cur->type = EQN_TEXT;
                           cur->text = mandoc_strdup("");
                 }                  }
                 while (EQN_SUBEXPR == parent->type)                  while (EQN_SUBEXPR == parent->type)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                 parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);                  parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);
                 break;                  break;
         case (EQN_TOK_RIGHT):          case EQN_TOK_RIGHT:
         case (EQN_TOK_BRACE_CLOSE):          case EQN_TOK_BRACE_CLOSE:
                 /*                  /*
                  * Close out the existing brace.                   * Close out the existing brace.
                  * FIXME: this is a shitty sentinel: we should really                   * FIXME: this is a shitty sentinel: we should really
                  * have a native EQN_BRACE type or whatnot.                   * have a native EQN_BRACE type or whatnot.
                  */                   */
                 while (parent->type != EQN_LIST)                  for (cur = parent; cur != NULL; cur = cur->parent)
                         if (NULL == (parent = parent->parent)) {                          if (cur->type == EQN_LIST &&
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);                              (tok == EQN_TOK_BRACE_CLOSE ||
                                 return(-1);                               cur->left != NULL))
                         }                                  break;
                   if (cur == NULL) {
                           mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
                               ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           break;
                   }
                   parent = cur;
                 if (EQN_TOK_RIGHT == tok) {                  if (EQN_TOK_RIGHT == tok) {
                         if (NULL == parent->left) {  
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                         if (NULL == (start = eqn_nexttok(ep, &sz))) {                          if (NULL == (start = eqn_nexttok(ep, &sz))) {
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);                                  mandoc_msg(MANDOCERR_REQ_EMPTY,
                                 return(-1);                                      ep->parse, ep->eqn.ln,
                                       ep->eqn.pos, eqn_toks[tok]);
                                   break;
                         }                          }
                         /* Handling depends on right/left. */                          /* Handling depends on right/left. */
                         if (STRNEQ(start, sz, "ceiling", 7)) {                          if (STRNEQ(start, sz, "ceiling", 7)) {
Line 929  again:
Line 996  again:
                         } else if (STRNEQ(start, sz, "floor", 5)) {                          } else if (STRNEQ(start, sz, "floor", 5)) {
                                 strlcpy(sym, "\\[rf]", sizeof(sym));                                  strlcpy(sym, "\\[rf]", sizeof(sym));
                                 parent->right = mandoc_strdup(sym);                                  parent->right = mandoc_strdup(sym);
                         } else                          } else
                                 parent->right = mandoc_strndup(start, sz);                                  parent->right = mandoc_strndup(start, sz);
                 }                  }
                 if (NULL == (parent = parent->parent)) {                  parent = parent->parent;
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);                  if (tok == EQN_TOK_BRACE_CLOSE &&
                         return(-1);                      (parent->type == EQN_PILE ||
                 }                       parent->type == EQN_MATRIX))
                 if (EQN_TOK_BRACE_CLOSE == tok && parent &&  
                         (parent->type == EQN_PILE ||  
                          parent->type == EQN_MATRIX))  
                         parent = parent->parent;                          parent = parent->parent;
                 /* Close out any "singleton" lists. */                  /* Close out any "singleton" lists. */
                 while (parent->type == EQN_LISTONE &&                  while (parent->type == EQN_LISTONE &&
                         parent->args == parent->expectargs)                      parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                 break;                  break;
         case (EQN_TOK_BRACE_OPEN):          case EQN_TOK_BRACE_OPEN:
         case (EQN_TOK_LEFT):          case EQN_TOK_LEFT:
                 /*                  /*
                  * If we already have something in the stack and we're                   * If we already have something in the stack and we're
                  * in an expression, then rewind til we're not any more                   * in an expression, then rewind til we're not any more
                  * (just like with the text node).                   * (just like with the text node).
                  */                   */
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);                  if (EQN_TOK_LEFT == tok &&
                                 return(-1);                      (start = eqn_nexttok(ep, &sz)) == NULL) {
                         }                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                               ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           break;
                   }
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_LIST;                  parent->type = EQN_LIST;
                 if (EQN_TOK_LEFT == tok) {                  if (EQN_TOK_LEFT == tok) {
                         if (NULL == (start = eqn_nexttok(ep, &sz))) {  
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                         /* Handling depends on right/left. */  
                         if (STRNEQ(start, sz, "ceiling", 7)) {                          if (STRNEQ(start, sz, "ceiling", 7)) {
                                 strlcpy(sym, "\\[lc]", sizeof(sym));                                  strlcpy(sym, "\\[lc]", sizeof(sym));
                                 parent->left = mandoc_strdup(sym);                                  parent->left = mandoc_strdup(sym);
                         } else if (STRNEQ(start, sz, "floor", 5)) {                          } else if (STRNEQ(start, sz, "floor", 5)) {
                                 strlcpy(sym, "\\[lf]", sizeof(sym));                                  strlcpy(sym, "\\[lf]", sizeof(sym));
                                 parent->left = mandoc_strdup(sym);                                  parent->left = mandoc_strdup(sym);
                         } else                          } else
                                 parent->left = mandoc_strndup(start, sz);                                  parent->left = mandoc_strndup(start, sz);
                 }                  }
                 break;                  break;
         case (EQN_TOK_PILE):          case EQN_TOK_PILE:
         case (EQN_TOK_LPILE):          case EQN_TOK_LPILE:
         case (EQN_TOK_RPILE):          case EQN_TOK_RPILE:
         case (EQN_TOK_CPILE):          case EQN_TOK_CPILE:
         case (EQN_TOK_CCOL):          case EQN_TOK_CCOL:
         case (EQN_TOK_LCOL):          case EQN_TOK_LCOL:
         case (EQN_TOK_RCOL):          case EQN_TOK_RCOL:
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                 if (EQN_TOK_BRACE_OPEN != eqn_tok_parse(ep, NULL)) {  
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                         return(-1);  
                 }  
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_PILE;                  parent->type = EQN_PILE;
                 parent = eqn_box_alloc(ep, parent);                  parent->expectargs = 1;
                 parent->type = EQN_LIST;  
                 break;                  break;
         case (EQN_TOK_ABOVE):          case EQN_TOK_ABOVE:
                 while (parent->type != EQN_PILE)                  for (cur = parent; cur != NULL; cur = cur->parent)
                         if (NULL == (parent = parent->parent)) {                          if (cur->type == EQN_PILE)
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);                                  break;
                                 return(-1);                  if (cur == NULL) {
                         }                          mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
                 parent = eqn_box_alloc(ep, parent);                              ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
                           break;
                   }
                   parent = eqn_box_alloc(ep, cur);
                 parent->type = EQN_LIST;                  parent->type = EQN_LIST;
                 break;                  break;
         case (EQN_TOK_MATRIX):          case EQN_TOK_MATRIX:
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                 if (EQN_TOK_BRACE_OPEN != eqn_tok_parse(ep, NULL)) {  
                         EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                         return(-1);  
                 }  
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_MATRIX;                  parent->type = EQN_MATRIX;
                 parent = eqn_box_alloc(ep, parent);                  parent->expectargs = 1;
                 parent->type = EQN_LIST;  
                 break;                  break;
         case (EQN_TOK_EOF):          case EQN_TOK_EOF:
                 /*                  /*
                  * End of file!                   * End of file!
                  * TODO: make sure we're not in an open subexpression.                   * TODO: make sure we're not in an open subexpression.
                  */                   */
                 return(0);                  return ROFF_EQN;
         default:          case EQN_TOK_FUNC:
                 assert(tok == EQN_TOK__MAX);          case EQN_TOK__MAX:
                 assert(NULL != p);                  assert(p != NULL);
                 /*                  /*
                  * If we already have something in the stack and we're                   * If we already have something in the stack and we're
                  * in an expression, then rewind til we're not any more.                   * in an expression, then rewind til we're not any more.
                  */                   */
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);                  if (tok == EQN_TOK_FUNC) {
                                 free(p);                          for (cur = parent; cur != NULL; cur = cur->parent)
                                 return(-1);                                  if (cur->font != EQNFONT_NONE)
                                           break;
                           if (cur == NULL || cur->font != EQNFONT_ROMAN) {
                                   parent = eqn_box_alloc(ep, parent);
                                   parent->type = EQN_LISTONE;
                                   parent->font = EQNFONT_ROMAN;
                                   parent->expectargs = 1;
                         }                          }
                   }
                 cur = eqn_box_alloc(ep, parent);                  cur = eqn_box_alloc(ep, parent);
                 cur->type = EQN_TEXT;                  cur->type = EQN_TEXT;
                 for (i = 0; i < EQNSYM__MAX; i++)                  for (i = 0; i < EQNSYM__MAX; i++)
                         if (0 == strcmp(eqnsyms[i].str, p)) {                          if (0 == strcmp(eqnsyms[i].str, p)) {
                                 (void)snprintf(sym, sizeof(sym),                                  (void)snprintf(sym, sizeof(sym),
                                         "\\[%s]", eqnsyms[i].sym);                                          "\\[%s]", eqnsyms[i].sym);
                                 cur->text = mandoc_strdup(sym);                                  cur->text = mandoc_strdup(sym);
                                 free(p);                                  free(p);
Line 1058  again:
Line 1111  again:
                 /*                  /*
                  * Post-process list status.                   * Post-process list status.
                  */                   */
                 while (parent->type == EQN_LISTONE &&                  while (parent->type == EQN_LISTONE &&
                         parent->args == parent->expectargs)                      parent->args == parent->expectargs)
                         if (NULL == (parent = parent->parent)) {                          parent = parent->parent;
                                 EQN_MSG(MANDOCERR_EQNSYNT, ep);  
                                 return(-1);  
                         }  
                 break;                  break;
           default:
                   abort();
         }          }
         goto again;          goto next_tok;
 }  }
   
 enum rofferr  enum rofferr
 eqn_end(struct eqn_node **epp)  eqn_end(struct eqn_node **epp)
 {  {
         struct eqn_node *ep;          struct eqn_node *ep;
   
Line 1079  eqn_end(struct eqn_node **epp) 
Line 1131  eqn_end(struct eqn_node **epp) 
   
         ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));          ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
         ep->eqn.root->expectargs = UINT_MAX;          ep->eqn.root->expectargs = UINT_MAX;
         return(0 == eqn_parse(ep, ep->eqn.root) ? ROFF_EQN : ROFF_IGN);          return eqn_parse(ep, ep->eqn.root);
 }  }
   
 void  void
Line 1094  eqn_free(struct eqn_node *p)
Line 1146  eqn_free(struct eqn_node *p)
                 free(p->defs[i].val);                  free(p->defs[i].val);
         }          }
   
         free(p->eqn.name);  
         free(p->data);          free(p->data);
         free(p->defs);          free(p->defs);
         free(p);          free(p);

Legend:
Removed from v.1.49  
changed lines
  Added in v.1.64

CVSweb