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

Diff for /mandoc/eqn.c between version 1.60 and 1.80

version 1.60, 2016/01/07 20:19:01 version 1.80, 2018/12/13 03:40:13
Line 1 
Line 1 
 /*      $Id$ */  /*      $Id$ */
 /*  /*
  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>   * 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 20 
Line 20 
 #include <sys/types.h>  #include <sys/types.h>
   
 #include <assert.h>  #include <assert.h>
   #include <ctype.h>
 #include <limits.h>  #include <limits.h>
 #include <stdio.h>  #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
 #include <time.h>  #include <time.h>
   
 #include "mandoc.h"  
 #include "mandoc_aux.h"  #include "mandoc_aux.h"
   #include "mandoc.h"
   #include "roff.h"
 #include "libmandoc.h"  #include "libmandoc.h"
 #include "libroff.h"  #include "eqn_parse.h"
   
 #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) \
Line 80  enum eqn_tok {
Line 82  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_QUOTED,
           EQN_TOK_SYM,
           EQN_TOK_EOF
 };  };
   
 static  const char *eqn_toks[EQN_TOK__MAX] = {  static  const char *eqn_toks[EQN_TOK__MAX] = {
Line 130  static const char *eqn_toks[EQN_TOK__MAX] = {
Line 135  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 = 0,
         EQNSYM_beta,          EQNSYM_beta,
         EQNSYM_chi,          EQNSYM_chi,
         EQNSYM_delta,          EQNSYM_delta,
Line 266  static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
Line 277  static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
         { "-", "mi" }, /* EQNSYM_minus */          { "-", "mi" }, /* EQNSYM_minus */
 };  };
   
   enum    parse_mode {
           MODE_QUOTED,
           MODE_NOSUB,
           MODE_SUB,
           MODE_TOK
   };
   
   struct  eqn_def {
           char             *key;
           size_t            keysz;
           char             *val;
           size_t            valsz;
   };
   
 static  struct eqn_box  *eqn_box_alloc(struct eqn_node *, struct eqn_box *);  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 *,  static  struct eqn_box  *eqn_box_makebinary(struct eqn_node *,
                                 enum eqn_post, struct eqn_box *);                                  struct eqn_box *);
 static  void             eqn_def(struct eqn_node *);  static  void             eqn_def(struct eqn_node *);
 static  struct eqn_def  *eqn_def_find(struct eqn_node *, const char *, size_t);  static  struct eqn_def  *eqn_def_find(struct eqn_node *);
 static  void             eqn_delim(struct eqn_node *);  static  void             eqn_delim(struct eqn_node *);
 static  const char      *eqn_next(struct eqn_node *, char, size_t *, int);  static  enum eqn_tok     eqn_next(struct eqn_node *, enum parse_mode);
 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 *);  static  void             eqn_undef(struct eqn_node *);
   
   
 enum rofferr  struct eqn_node *
 eqn_read(struct eqn_node **epp, int ln,  eqn_alloc(struct mparse *parse)
                 const char *p, int pos, int *offs)  
 {  {
         size_t           sz;          struct eqn_node *ep;
         struct eqn_node *ep;  
         enum rofferr     er;  
   
         ep = *epp;          ep = mandoc_calloc(1, sizeof(*ep));
           ep->parse = parse;
           ep->gsize = EQN_DEFSIZE;
           return ep;
   }
   
         /*  void
          * If we're the terminating mark, unset our equation status and  eqn_reset(struct eqn_node *ep)
          * validate the full equation.  {
          */          free(ep->data);
           ep->data = ep->start = ep->end = NULL;
         if (0 == strncmp(p, ".EN", 3)) {          ep->sz = ep->toksz = 0;
                 er = eqn_end(epp);  
                 p += 3;  
                 while (' ' == *p || '\t' == *p)  
                         p++;  
                 if ('\0' == *p)  
                         return er;  
                 mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse,  
                     ln, pos, "EN %s", p);  
                 return er;  
         }  
   
         /*  
          * Build up the full string, replacing all newlines with regular  
          * whitespace.  
          */  
   
         sz = strlen(p + pos) + 1;  
         ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);  
   
         /* First invocation: nil terminate the string. */  
   
         if (0 == ep->sz)  
                 *ep->data = '\0';  
   
         ep->sz += sz;  
         strlcat(ep->data, p + pos, ep->sz + 1);  
         strlcat(ep->data, " ", ep->sz + 1);  
         return ROFF_IGN;  
 }  }
   
 struct eqn_node *  void
 eqn_alloc(int pos, int line, struct mparse *parse)  eqn_read(struct eqn_node *ep, const char *p)
 {  {
         struct eqn_node *p;          char            *cp;
   
         p = mandoc_calloc(1, sizeof(struct eqn_node));          if (ep->data == NULL) {
                   ep->sz = strlen(p);
         p->parse = parse;                  ep->data = mandoc_strdup(p);
         p->eqn.ln = line;          } else {
         p->eqn.pos = pos;                  ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
         p->gsize = EQN_DEFSIZE;                  free(ep->data);
                   ep->data = cp;
         return p;          }
           ep->sz += 1;
 }  }
   
 /*  /*
  * Find the key "key" of the give size within our eqn-defined values.   * Find the key "key" of the give size within our eqn-defined values.
  */   */
 static struct eqn_def *  static struct eqn_def *
 eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)  eqn_def_find(struct eqn_node *ep)
 {  {
         int              i;          int              i;
   
         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, ep->start, ep->toksz))
                         return &ep->defs[i];                          return &ep->defs[i];
   
         return NULL;          return NULL;
 }  }
   
 /*  /*
  * Get the next token from the input stream using the given quote   * Parse a token from the input text.  The modes are:
  * character.   * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
  * Optionally make any replacements.   *   before its next occurence.  Do not interpret the token in any
    *   way and return EQN_TOK_QUOTED.  All other modes behave like
    *   MODE_QUOTED when *ep->start is '"'.
    * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
    *   otherwise, it ends before the next whitespace or brace.
    *   Do not interpret the token and return EQN_TOK__MAX.
    * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
    *   alias created with define.  If it is an alias, replace it with
    *   its string value and reparse.
    * MODE_TOK: Like MODE_SUB, but also check the token against the list
    *   of tokens, and if there is a match, return that token.  Otherwise,
    *   if the token matches a symbol, return EQN_TOK_SYM; if it matches
    *   a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX.  Except for
    *   a token match, *ep->start is set to an allocated string that the
    *   caller is expected to free.
    * All modes skip whitespace following the end of the token.
  */   */
 static const char *  static enum eqn_tok
 eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)  eqn_next(struct eqn_node *ep, enum parse_mode mode)
 {  {
         char            *start, *next;          static int       last_len, lim;
         int              q, diff, lim;  
         size_t           ssz, dummy;  
         struct eqn_def  *def;          struct eqn_def  *def;
           size_t           start;
           int              diff, i, quoted;
           enum eqn_tok     tok;
   
         if (NULL == sz)          /*
                 sz = &dummy;           * Reset the recursion counter after advancing
            * beyond the end of the previous substitution.
            */
           if (ep->end - ep->data >= last_len)
                   lim = 0;
   
         lim = 0;          ep->start = ep->end;
         ep->rew = ep->cur;          quoted = mode == MODE_QUOTED;
 again:          for (;;) {
         /* Prevent self-definitions. */                  switch (*ep->start) {
                   case '\0':
                           ep->toksz = 0;
                           return EQN_TOK_EOF;
                   case '"':
                           quoted = 1;
                           break;
                   default:
                           break;
                   }
                   if (quoted) {
                           ep->end = strchr(ep->start + 1, *ep->start);
                           ep->start++;  /* Skip opening quote. */
                           if (ep->end == NULL) {
                                   mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
                                       ep->node->line, ep->node->pos, NULL);
                                   ep->end = strchr(ep->start, '\0');
                           }
                   } else {
                           ep->end = ep->start + 1;
                           if (*ep->start != '{' && *ep->start != '}')
                                   ep->end += strcspn(ep->end, " ^~\"{}\t");
                   }
                   ep->toksz = ep->end - ep->start;
                   if (quoted && *ep->end != '\0')
                           ep->end++;  /* Skip closing quote. */
                   while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
                           ep->end++;
                   if (quoted)  /* Cannot return, may have to strndup. */
                           break;
                   if (mode == MODE_NOSUB)
                           return EQN_TOK__MAX;
                   if ((def = eqn_def_find(ep)) == NULL)
                           break;
                   if (++lim > EQN_NEST_MAX) {
                           mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
                               ep->node->line, ep->node->pos, NULL);
                           return EQN_TOK_EOF;
                   }
   
         if (lim >= EQN_NEST_MAX) {                  /* Replace a defined name with its string value. */
                 mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,                  if ((diff = def->valsz - ep->toksz) > 0) {
                     ep->eqn.ln, ep->eqn.pos, NULL);                          start = ep->start - ep->data;
                 return NULL;  
         }  
   
         ep->cur = ep->rew;  
         start = &ep->data[(int)ep->cur];  
         q = 0;  
   
         if ('\0' == *start)  
                 return NULL;  
   
         if (quote == *start) {  
                 ep->cur++;  
                 q = 1;  
         }  
   
         start = &ep->data[(int)ep->cur];  
   
         if ( ! q) {  
                 if ('{' == *start || '}' == *start)  
                         ssz = 1;  
                 else  
                         ssz = strcspn(start + 1, " ^~\"{}\t") + 1;  
                 next = start + (int)ssz;  
                 if ('\0' == *next)  
                         next = NULL;  
         } else  
                 next = strchr(start, quote);  
   
         if (NULL != next) {  
                 *sz = (size_t)(next - start);  
                 ep->cur += *sz;  
                 if (q)  
                         ep->cur++;  
                 while (' ' == ep->data[(int)ep->cur] ||  
                     '\t' == ep->data[(int)ep->cur] ||  
                     '^' == ep->data[(int)ep->cur] ||  
                     '~' == ep->data[(int)ep->cur])  
                         ep->cur++;  
         } else {  
                 if (q)  
                         mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,  
                             ep->eqn.ln, ep->eqn.pos, NULL);  
                 next = strchr(start, '\0');  
                 *sz = (size_t)(next - start);  
                 ep->cur += *sz;  
         }  
   
         /* Quotes aren't expanded for values. */  
   
         if (q || ! repl)  
                 return start;  
   
         if (NULL != (def = eqn_def_find(ep, start, *sz))) {  
                 diff = def->valsz - *sz;  
   
                 if (def->valsz > *sz) {  
                         ep->sz += diff;                          ep->sz += diff;
                         ep->data = mandoc_realloc(ep->data, ep->sz + 1);                          ep->data = mandoc_realloc(ep->data, ep->sz + 1);
                         ep->data[ep->sz] = '\0';                          ep->start = ep->data + start;
                         start = &ep->data[(int)ep->rew];  
                 }                  }
                   if (diff)
                 diff = def->valsz - *sz;                          memmove(ep->start + def->valsz, ep->start + ep->toksz,
                 memmove(start + *sz + diff, start + *sz,                              strlen(ep->start + ep->toksz) + 1);
                     (strlen(start) - *sz) + 1);                  memcpy(ep->start, def->val, def->valsz);
                 memcpy(start, def->val, def->valsz);                  last_len = ep->start - ep->data + def->valsz;
                 lim++;  
                 goto again;  
         }          }
           if (mode != MODE_TOK)
         return start;                  return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
 }  
   
 /*  
  * Get the next delimited token using the default current quote  
  * character.  
  */  
 static const char *  
 eqn_nexttok(struct eqn_node *ep, size_t *sz)  
 {  
   
         return eqn_next(ep, '"', sz, 1);  
 }  
   
 /*  
  * Get next token without replacement.  
  */  
 static const char *  
 eqn_nextrawtok(struct eqn_node *ep, size_t *sz)  
 {  
   
         return eqn_next(ep, '"', sz, 0);  
 }  
   
 /*  
  * Parse a token from the stream of text.  
  * A token consists of one of the recognised eqn(7) strings.  
  * Strings are separated by delimiting marks.  
  * This returns EQN_TOK_EOF when there are no more tokens.  
  * If the token is an unrecognised string literal, then it returns  
  * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated  
  * string.  
  * This must be later freed with free(3).  
  */  
 static enum eqn_tok  
 eqn_tok_parse(struct eqn_node *ep, char **p)  
 {  
         const char      *start;  
         size_t           i, sz;  
         int              quoted;  
   
         if (NULL != p)  
                 *p = NULL;  
   
         quoted = ep->data[ep->cur] == '"';  
   
         if (NULL == (start = eqn_nexttok(ep, &sz)))  
                 return EQN_TOK_EOF;  
   
         if (quoted) {          if (quoted) {
                 if (p != NULL)                  ep->start = mandoc_strndup(ep->start, ep->toksz);
                         *p = mandoc_strndup(start, sz);                  return EQN_TOK_QUOTED;
                 return EQN_TOK__MAX;  
         }          }
           for (tok = 0; tok < EQN_TOK__MAX; tok++)
                   if (STRNEQ(ep->start, ep->toksz,
                       eqn_toks[tok], strlen(eqn_toks[tok])))
                           return tok;
   
         for (i = 0; i < EQN_TOK__MAX; i++) {          for (i = 0; i < EQNSYM__MAX; i++) {
                 if (NULL == eqn_toks[i])                  if (STRNEQ(ep->start, ep->toksz,
                         continue;                      eqnsyms[i].str, strlen(eqnsyms[i].str))) {
                 if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i])))                          mandoc_asprintf(&ep->start,
                         break;                              "\\[%s]", eqnsyms[i].sym);
                           return EQN_TOK_SYM;
                   }
         }          }
           ep->start = mandoc_strndup(ep->start, ep->toksz);
         if (i == EQN_TOK__MAX && NULL != p)          for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
                 *p = mandoc_strndup(start, sz);                  if (STRNEQ(ep->start, ep->toksz,
                       eqn_func[i], strlen(eqn_func[i])))
         return i;                          return EQN_TOK_FUNC;
           return EQN_TOK__MAX;
 }  }
   
 static void  void
 eqn_box_free(struct eqn_box *bp)  eqn_box_free(struct eqn_box *bp)
 {  {
           if (bp == NULL)
                   return;
   
         if (bp->first)          if (bp->first)
                 eqn_box_free(bp->first);                  eqn_box_free(bp->first);
Line 549  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
Line 503  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
         bp->parent = parent;          bp->parent = parent;
         bp->parent->args++;          bp->parent->args++;
         bp->expectargs = UINT_MAX;          bp->expectargs = UINT_MAX;
           bp->font = bp->parent->font;
         bp->size = ep->gsize;          bp->size = ep->gsize;
   
         if (NULL != parent->first) {          if (NULL != parent->first) {
Line 568  eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par
Line 523  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, struct eqn_box *parent)
         enum eqn_post pos, struct eqn_box *parent)  
 {  {
         struct eqn_box  *b, *newb;          struct eqn_box  *b, *newb;
   
Line 581  eqn_box_makebinary(struct eqn_node *ep,
Line 535  eqn_box_makebinary(struct eqn_node *ep,
         parent->last = b->prev;          parent->last = b->prev;
         b->prev = NULL;          b->prev = NULL;
         newb = eqn_box_alloc(ep, parent);          newb = eqn_box_alloc(ep, parent);
         newb->pos = pos;  
         newb->type = EQN_SUBEXPR;          newb->type = EQN_SUBEXPR;
         newb->expectargs = 2;          newb->expectargs = 2;
         newb->args = 1;          newb->args = 1;
Line 597  eqn_box_makebinary(struct eqn_node *ep,
Line 550  eqn_box_makebinary(struct eqn_node *ep,
 static void  static void
 eqn_delim(struct eqn_node *ep)  eqn_delim(struct eqn_node *ep)
 {  {
         const char      *start;          if (ep->end[0] == '\0' || ep->end[1] == '\0') {
         size_t           sz;  
   
         if ((start = eqn_nextrawtok(ep, &sz)) == NULL)  
                 mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                  mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                     ep->eqn.ln, ep->eqn.pos, "delim");                      ep->node->line, ep->node->pos, "delim");
         else if (strncmp(start, "off", 3) == 0)                  if (ep->end[0] != '\0')
                           ep->end++;
           } else if (strncmp(ep->end, "off", 3) == 0) {
                 ep->delim = 0;                  ep->delim = 0;
         else if (strncmp(start, "on", 2) == 0) {                  ep->end += 3;
           } else if (strncmp(ep->end, "on", 2) == 0) {
                 if (ep->odelim && ep->cdelim)                  if (ep->odelim && ep->cdelim)
                         ep->delim = 1;                          ep->delim = 1;
         } else if (start[1] != '\0') {                  ep->end += 2;
                 ep->odelim = start[0];          } else {
                 ep->cdelim = start[1];                  ep->odelim = *ep->end++;
                   ep->cdelim = *ep->end++;
                 ep->delim = 1;                  ep->delim = 1;
         }          }
 }  }
Line 621  eqn_delim(struct eqn_node *ep)
Line 575  eqn_delim(struct eqn_node *ep)
 static void  static void
 eqn_undef(struct eqn_node *ep)  eqn_undef(struct eqn_node *ep)
 {  {
         const char      *start;  
         struct eqn_def  *def;          struct eqn_def  *def;
         size_t           sz;  
   
         if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {          if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
                 mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                  mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                     ep->eqn.ln, ep->eqn.pos, "undef");                      ep->node->line, ep->node->pos, "undef");
                 return;                  return;
         }          }
         if ((def = eqn_def_find(ep, start, sz)) == NULL)          if ((def = eqn_def_find(ep)) == NULL)
                 return;                  return;
         free(def->key);          free(def->key);
         free(def->val);          free(def->val);
Line 641  eqn_undef(struct eqn_node *ep)
Line 593  eqn_undef(struct eqn_node *ep)
 static void  static void
 eqn_def(struct eqn_node *ep)  eqn_def(struct eqn_node *ep)
 {  {
         const char      *start;  
         size_t           sz;  
         struct eqn_def  *def;          struct eqn_def  *def;
         int              i;          int              i;
   
         if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {          if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
                 mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                  mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                     ep->eqn.ln, ep->eqn.pos, "define");                      ep->node->line, ep->node->pos, "define");
                 return;                  return;
         }          }
   
Line 656  eqn_def(struct eqn_node *ep)
Line 606  eqn_def(struct eqn_node *ep)
          * Search for a key that already exists.           * Search for a key that already exists.
          * Create a new key if none is found.           * Create a new key if none is found.
          */           */
         if (NULL == (def = eqn_def_find(ep, start, sz))) {          if ((def = eqn_def_find(ep)) == NULL) {
                 /* Find holes in string array. */                  /* Find holes in string array. */
                 for (i = 0; i < (int)ep->defsz; i++)                  for (i = 0; i < (int)ep->defsz; i++)
                         if (0 == ep->defs[i].keysz)                          if (0 == ep->defs[i].keysz)
Line 671  eqn_def(struct eqn_node *ep)
Line 621  eqn_def(struct eqn_node *ep)
   
                 def = ep->defs + i;                  def = ep->defs + i;
                 free(def->key);                  free(def->key);
                 def->key = mandoc_strndup(start, sz);                  def->key = mandoc_strndup(ep->start, ep->toksz);
                 def->keysz = sz;                  def->keysz = ep->toksz;
         }          }
   
         start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);          if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
         if (start == NULL) {  
                 mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,                  mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
                     ep->eqn.ln, ep->eqn.pos, "define %s", def->key);                      ep->node->line, ep->node->pos, "define %s", def->key);
                 free(def->key);                  free(def->key);
                 free(def->val);                  free(def->val);
                 def->key = def->val = NULL;                  def->key = def->val = NULL;
Line 686  eqn_def(struct eqn_node *ep)
Line 635  eqn_def(struct eqn_node *ep)
                 return;                  return;
         }          }
         free(def->val);          free(def->val);
         def->val = mandoc_strndup(start, sz);          def->val = mandoc_strndup(ep->start, ep->toksz);
         def->valsz = sz;          def->valsz = ep->toksz;
 }  }
   
 /*  void
  * Recursively parse an eqn(7) expression.  eqn_parse(struct eqn_node *ep)
  */  
 static enum rofferr  
 eqn_parse(struct eqn_node *ep, struct eqn_box *parent)  
 {  {
         char             sym[64];          struct eqn_box  *cur, *nbox, *parent, *split;
         struct eqn_box  *cur;          const char      *cp, *cpn;
         const char      *start;  
         char            *p;          char            *p;
         size_t           i, sz;          enum eqn_tok     tok;
         enum eqn_tok     tok, subtok;          enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
         enum eqn_post    pos;  
         int              size;          int              size;
   
           parent = ep->node->eqn;
         assert(parent != NULL);          assert(parent != NULL);
   
         /*          /*
Line 713  eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
Line 658  eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
          */           */
   
         if (ep->data == NULL)          if (ep->data == NULL)
                 return ROFF_IGN;                  return;
   
 next_tok:          ep->start = ep->end = ep->data + strspn(ep->data, " ^~");
         tok = eqn_tok_parse(ep, &p);  
   
 this_tok:  next_tok:
           tok = eqn_next(ep, MODE_TOK);
         switch (tok) {          switch (tok) {
         case (EQN_TOK_UNDEF):          case EQN_TOK_UNDEF:
                 eqn_undef(ep);                  eqn_undef(ep);
                 break;                  break;
         case (EQN_TOK_NDEFINE):          case EQN_TOK_NDEFINE:
         case (EQN_TOK_DEFINE):          case EQN_TOK_DEFINE:
                 eqn_def(ep);                  eqn_def(ep);
                 break;                  break;
         case (EQN_TOK_TDEFINE):          case EQN_TOK_TDEFINE:
                 if (eqn_nextrawtok(ep, NULL) == NULL ||                  if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
                     eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL)                      eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
                         mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, "tdefine");                              ep->node->line, ep->node->pos, "tdefine");
                 break;                  break;
         case (EQN_TOK_DELIM):          case EQN_TOK_DELIM:
                 eqn_delim(ep);                  eqn_delim(ep);
                 break;                  break;
         case (EQN_TOK_GFONT):          case EQN_TOK_GFONT:
                 if (eqn_nextrawtok(ep, NULL) == NULL)                  if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
                         mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                 break;                  break;
         case (EQN_TOK_MARK):          case EQN_TOK_MARK:
         case (EQN_TOK_LINEUP):          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 (parent->last == NULL) {                  if (parent->last == NULL) {
                         mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,                          mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         cur = eqn_box_alloc(ep, parent);                          cur = eqn_box_alloc(ep, parent);
                         cur->type = EQN_TEXT;                          cur->type = EQN_TEXT;
                         cur->text = mandoc_strdup("");                          cur->text = mandoc_strdup("");
                 }                  }
                 parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent);                  parent = eqn_box_makebinary(ep, parent);
                 parent->type = EQN_LISTONE;                  parent->type = EQN_LIST;
                 parent->expectargs = 1;                  parent->expectargs = 1;
                   parent->font = EQNFONT_ROMAN;
                 switch (tok) {                  switch (tok) {
                 case (EQN_TOK_DOTDOT):                  case EQN_TOK_DOTDOT:
                         strlcpy(sym, "\\[ad]", sizeof(sym));                          parent->top = mandoc_strdup("\\[ad]");
                         break;                          break;
                 case (EQN_TOK_VEC):                  case EQN_TOK_VEC:
                         strlcpy(sym, "\\[->]", sizeof(sym));                          parent->top = mandoc_strdup("\\[->]");
                         break;                          break;
                 case (EQN_TOK_DYAD):                  case EQN_TOK_DYAD:
                         strlcpy(sym, "\\[<>]", sizeof(sym));                          parent->top = mandoc_strdup("\\[<>]");
                         break;                          break;
                 case (EQN_TOK_TILDE):                  case EQN_TOK_TILDE:
                         strlcpy(sym, "\\[a~]", sizeof(sym));                          parent->top = mandoc_strdup("\\[a~]");
                         break;                          break;
                 case (EQN_TOK_UNDER):                  case EQN_TOK_UNDER:
                         strlcpy(sym, "\\[ul]", sizeof(sym));                          parent->bottom = mandoc_strdup("\\[ul]");
                         break;                          break;
                 case (EQN_TOK_BAR):                  case EQN_TOK_BAR:
                         strlcpy(sym, "\\[rl]", sizeof(sym));                          parent->top = mandoc_strdup("\\[rn]");
                         break;                          break;
                 case (EQN_TOK_DOT):                  case EQN_TOK_DOT:
                         strlcpy(sym, "\\[a.]", sizeof(sym));                          parent->top = mandoc_strdup("\\[a.]");
                         break;                          break;
                 case (EQN_TOK_HAT):                  case EQN_TOK_HAT:
                         strlcpy(sym, "\\[ha]", sizeof(sym));                          parent->top = mandoc_strdup("\\[ha]");
                         break;                          break;
                 default:                  default:
                         abort();                          abort();
                 }                  }
   
                 switch (tok) {  
                 case (EQN_TOK_DOTDOT):  
                 case (EQN_TOK_VEC):  
                 case (EQN_TOK_DYAD):  
                 case (EQN_TOK_TILDE):  
                 case (EQN_TOK_BAR):  
                 case (EQN_TOK_DOT):  
                 case (EQN_TOK_HAT):  
                         parent->top = mandoc_strdup(sym);  
                         break;  
                 case (EQN_TOK_UNDER):  
                         parent->bottom = mandoc_strdup(sym);  
                         break;  
                 default:  
                         abort();  
                 }  
                 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:
                 subtok = eqn_tok_parse(ep, NULL);                  if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
                 if (subtok != EQN_TOK__MAX) {  
                         mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->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)
                         parent = parent->parent;                          parent = parent->parent;
                 /*                  /*
Line 834  this_tok:
Line 759  this_tok:
                  * exactly one of those.                   * exactly one of those.
                  */                   */
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_LISTONE;                  parent->type = EQN_LIST;
                 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 (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
                         mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         break;                          break;
                 }                  }
                 size = mandoc_strntoi(start, sz, 10);                  size = mandoc_strntoi(ep->start, ep->toksz, 10);
                 if (-1 == size) {                  if (-1 == size) {
                         mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,                          mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         break;                          break;
                 }                  }
                 if (EQN_TOK_GSIZE == tok) {                  if (EQN_TOK_GSIZE == tok) {
                         ep->gsize = size;                          ep->gsize = size;
                         break;                          break;
                 }                  }
                   while (parent->args == parent->expectargs)
                           parent = parent->parent;
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_LISTONE;                  parent->type = EQN_LIST;
                 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
Line 887  this_tok:
Line 814  this_tok:
                  */                   */
                 if (parent->last == NULL) {                  if (parent->last == NULL) {
                         mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,                          mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         cur = eqn_box_alloc(ep, parent);                          cur = eqn_box_alloc(ep, parent);
                         cur->type = EQN_TEXT;                          cur->type = EQN_TEXT;
                         cur->text = mandoc_strdup("");                          cur->text = mandoc_strdup("");
                 }                  }
                 /* Handle the "subsup" and "fromto" positions. */                  while (parent->expectargs == 1 && parent->args == 1)
                 if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {                          parent = parent->parent;
                   if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO)  {
                           for (cur = parent; cur != NULL; cur = cur->parent)
                                   if (cur->pos == EQNPOS_SUB ||
                                       cur->pos == EQNPOS_SUP ||
                                       cur->pos == EQNPOS_SUBSUP ||
                                       cur->pos == EQNPOS_SQRT ||
                                       cur->pos == EQNPOS_OVER)
                                           break;
                           if (cur != NULL)
                                   parent = cur->parent;
                   }
                   if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
                         parent->expectargs = 3;                          parent->expectargs = 3;
                         parent->pos = EQNPOS_SUBSUP;                          parent->pos = EQNPOS_SUBSUP;
                         break;                          break;
                 }                  }
                 if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) {                  if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
                         parent->expectargs = 3;                          parent->expectargs = 3;
                         parent->pos = EQNPOS_FROMTO;                          parent->pos = EQNPOS_FROMTO;
                         break;                          break;
                 }                  }
                   parent = eqn_box_makebinary(ep, parent);
                 switch (tok) {                  switch (tok) {
                 case (EQN_TOK_FROM):                  case EQN_TOK_FROM:
                         pos = EQNPOS_FROM;                          parent->pos = EQNPOS_FROM;
                         break;                          break;
                 case (EQN_TOK_TO):                  case EQN_TOK_TO:
                         pos = EQNPOS_TO;                          parent->pos = EQNPOS_TO;
                         break;                          break;
                 case (EQN_TOK_SUP):                  case EQN_TOK_SUP:
                         pos = EQNPOS_SUP;                          parent->pos = EQNPOS_SUP;
                         break;                          break;
                 case (EQN_TOK_SUB):                  case EQN_TOK_SUB:
                         pos = EQNPOS_SUB;                          parent->pos = EQNPOS_SUB;
                         break;                          break;
                 default:                  default:
                         abort();                          abort();
                 }                  }
                 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)
                         parent = parent->parent;                          parent = parent->parent;
                 /*                  /*
Line 934  this_tok:
Line 873  this_tok:
                 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
Line 942  this_tok:
Line 881  this_tok:
                  */                   */
                 if (parent->last == NULL) {                  if (parent->last == NULL) {
                         mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,                          mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         cur = eqn_box_alloc(ep, parent);                          cur = eqn_box_alloc(ep, parent);
                         cur->type = EQN_TEXT;                          cur->type = EQN_TEXT;
                         cur->text = mandoc_strdup("");                          cur->text = mandoc_strdup("");
                 }                  }
                   while (parent->args == parent->expectargs)
                           parent = parent->parent;
                 while (EQN_SUBEXPR == parent->type)                  while (EQN_SUBEXPR == parent->type)
                         parent = parent->parent;                          parent = parent->parent;
                 parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);                  parent = eqn_box_makebinary(ep, parent);
                   parent->pos = EQNPOS_OVER;
                 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
Line 960  this_tok:
Line 902  this_tok:
                  */                   */
                 for (cur = parent; cur != NULL; cur = cur->parent)                  for (cur = parent; cur != NULL; cur = cur->parent)
                         if (cur->type == EQN_LIST &&                          if (cur->type == EQN_LIST &&
                               cur->expectargs > 1 &&
                             (tok == EQN_TOK_BRACE_CLOSE ||                              (tok == EQN_TOK_BRACE_CLOSE ||
                              cur->left != NULL))                               cur->left != NULL))
                                 break;                                  break;
                 if (cur == NULL) {                  if (cur == NULL) {
                         mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,                          mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         break;                          break;
                 }                  }
                 parent = cur;                  parent = cur;
                 if (EQN_TOK_RIGHT == tok) {                  if (EQN_TOK_RIGHT == tok) {
                         if (NULL == (start = eqn_nexttok(ep, &sz))) {                          if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
                                 mandoc_msg(MANDOCERR_REQ_EMPTY,                                  mandoc_msg(MANDOCERR_REQ_EMPTY,
                                     ep->parse, ep->eqn.ln,                                      ep->parse, ep->node->line,
                                     ep->eqn.pos, eqn_toks[tok]);                                      ep->node->pos, eqn_toks[tok]);
                                 break;                                  break;
                         }                          }
                         /* Handling depends on right/left. */                          /* Handling depends on right/left. */
                         if (STRNEQ(start, sz, "ceiling", 7)) {                          if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
                                 strlcpy(sym, "\\[rc]", sizeof(sym));                                  parent->right = mandoc_strdup("\\[rc]");
                                 parent->right = mandoc_strdup(sym);                          else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
                         } else if (STRNEQ(start, sz, "floor", 5)) {                                  parent->right = mandoc_strdup("\\[rf]");
                                 strlcpy(sym, "\\[rf]", sizeof(sym));                          else
                                 parent->right = mandoc_strdup(sym);                                  parent->right =
                         } else                                      mandoc_strndup(ep->start, ep->toksz);
                                 parent->right = mandoc_strndup(start, sz);  
                 }                  }
                 parent = parent->parent;                  parent = parent->parent;
                 if (EQN_TOK_BRACE_CLOSE == tok && parent &&                  if (tok == EQN_TOK_BRACE_CLOSE &&
                     (parent->type == EQN_PILE ||                      (parent->type == EQN_PILE ||
                      parent->type == EQN_MATRIX))                       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_LIST &&
                     parent->args == parent->expectargs)                      parent->expectargs == 1 &&
                       parent->args == 1)
                         parent = parent->parent;                          parent = parent->parent;
                 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
Line 1006  this_tok:
Line 949  this_tok:
                 while (parent->args == parent->expectargs)                  while (parent->args == parent->expectargs)
                         parent = parent->parent;                          parent = parent->parent;
                 if (EQN_TOK_LEFT == tok &&                  if (EQN_TOK_LEFT == tok &&
                     (start = eqn_nexttok(ep, &sz)) == NULL) {                      eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
                         mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,                          mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         break;                          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 (STRNEQ(start, sz, "ceiling", 7)) {                          if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
                                 strlcpy(sym, "\\[lc]", sizeof(sym));                                  parent->left = mandoc_strdup("\\[lc]");
                                 parent->left = mandoc_strdup(sym);                          else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
                         } else if (STRNEQ(start, sz, "floor", 5)) {                                  parent->left = mandoc_strdup("\\[lf]");
                                 strlcpy(sym, "\\[lf]", sizeof(sym));                          else
                                 parent->left = mandoc_strdup(sym);                                  parent->left =
                         } else                                      mandoc_strndup(ep->start, ep->toksz);
                                 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)
                         parent = parent->parent;                          parent = parent->parent;
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_PILE;                  parent->type = EQN_PILE;
                 parent->expectargs = 1;                  parent->expectargs = 1;
                 break;                  break;
         case (EQN_TOK_ABOVE):          case EQN_TOK_ABOVE:
                 for (cur = parent; cur != NULL; cur = cur->parent)                  for (cur = parent; cur != NULL; cur = cur->parent)
                         if (cur->type == EQN_PILE)                          if (cur->type == EQN_PILE)
                                 break;                                  break;
                 if (cur == NULL) {                  if (cur == NULL) {
                         mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,                          mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
                             ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);                              ep->node->line, ep->node->pos, eqn_toks[tok]);
                         break;                          break;
                 }                  }
                 parent = eqn_box_alloc(ep, cur);                  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)
                         parent = parent->parent;                          parent = parent->parent;
                 parent = eqn_box_alloc(ep, parent);                  parent = eqn_box_alloc(ep, parent);
                 parent->type = EQN_MATRIX;                  parent->type = EQN_MATRIX;
                 parent->expectargs = 1;                  parent->expectargs = 1;
                 break;                  break;
         case (EQN_TOK_EOF):          case EQN_TOK_EOF:
                   return;
           case EQN_TOK__MAX:
           case EQN_TOK_FUNC:
           case EQN_TOK_QUOTED:
           case EQN_TOK_SYM:
                   p = ep->start;
                   assert(p != NULL);
                 /*                  /*
                  * End of file!  
                  * TODO: make sure we're not in an open subexpression.  
                  */  
                 return ROFF_EQN;  
         default:  
                 assert(tok == EQN_TOK__MAX);  
                 assert(NULL != p);  
                 /*  
                  * 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.
                  */                   */
Line 1073  this_tok:
Line 1014  this_tok:
                         parent = parent->parent;                          parent = parent->parent;
                 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++)                  cur->text = p;
                         if (0 == strcmp(eqnsyms[i].str, p)) {                  switch (tok) {
                                 (void)snprintf(sym, sizeof(sym),                  case EQN_TOK_FUNC:
                                         "\\[%s]", eqnsyms[i].sym);                          cur->font = EQNFONT_ROMAN;
                                 cur->text = mandoc_strdup(sym);                          break;
                                 free(p);                  case EQN_TOK_QUOTED:
                           if (cur->font == EQNFONT_NONE)
                                   cur->font = EQNFONT_ITALIC;
                           break;
                   case EQN_TOK_SYM:
                           break;
                   default:
                           if (cur->font != EQNFONT_NONE || *p == '\0')
                                 break;                                  break;
                           cpn = p - 1;
                           ccln = CCL_LET;
                           split = NULL;
                           for (;;) {
                                   /* Advance to next character. */
                                   cp = cpn++;
                                   ccl = ccln;
                                   ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
                                       isdigit((unsigned char)*cpn) ||
                                       (*cpn == '.' && (ccl == CCL_DIG ||
                                        isdigit((unsigned char)cpn[1]))) ?
                                       CCL_DIG : CCL_PUN;
                                   /* No boundary before first character. */
                                   if (cp < p)
                                           continue;
                                   cur->font = ccl == CCL_LET ?
                                       EQNFONT_ITALIC : EQNFONT_ROMAN;
                                   if (*cp == '\\')
                                           mandoc_escape(&cpn, NULL, NULL);
                                   /* No boundary after last character. */
                                   if (*cpn == '\0')
                                           break;
                                   if (ccln == ccl && *cp != ',' && *cpn != ',')
                                           continue;
                                   /* Boundary found, split the text. */
                                   if (parent->args == parent->expectargs) {
                                           /* Remove the text from the tree. */
                                           if (cur->prev == NULL)
                                                   parent->first = cur->next;
                                           else
                                                   cur->prev->next = NULL;
                                           parent->last = cur->prev;
                                           parent->args--;
                                           /* Set up a list instead. */
                                           split = eqn_box_alloc(ep, parent);
                                           split->type = EQN_LIST;
                                           /* Insert the word into the list. */
                                           split->first = split->last = cur;
                                           cur->parent = split;
                                           cur->prev = NULL;
                                           parent = split;
                                   }
                                   /* Append a new text box. */
                                   nbox = eqn_box_alloc(ep, parent);
                                   nbox->type = EQN_TEXT;
                                   nbox->text = mandoc_strdup(cpn);
                                   /* Truncate the old box. */
                                   p = mandoc_strndup(cur->text,
                                       cpn - cur->text);
                                   free(cur->text);
                                   cur->text = p;
                                   /* Setup to process the new box. */
                                   cur = nbox;
                                   p = nbox->text;
                                   cpn = p - 1;
                                   ccln = CCL_LET;
                         }                          }
                           if (split != NULL)
                 if (i == EQNSYM__MAX)                                  parent = split->parent;
                         cur->text = p;                          break;
                 /*                  }
                  * Post-process list status.  
                  */  
                 while (parent->type == EQN_LISTONE &&  
                     parent->args == parent->expectargs)  
                         parent = parent->parent;  
                 break;                  break;
           default:
                   abort();
         }          }
         goto next_tok;          goto next_tok;
 }  }
   
 enum rofferr  
 eqn_end(struct eqn_node **epp)  
 {  
         struct eqn_node *ep;  
   
         ep = *epp;  
         *epp = NULL;  
   
         ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));  
         ep->eqn.root->expectargs = UINT_MAX;  
         return eqn_parse(ep, ep->eqn.root);  
 }  
   
 void  void
 eqn_free(struct eqn_node *p)  eqn_free(struct eqn_node *p)
 {  {
         int              i;          int              i;
   
         eqn_box_free(p->eqn.root);          if (p == NULL)
                   return;
   
         for (i = 0; i < (int)p->defsz; i++) {          for (i = 0; i < (int)p->defsz; i++) {
                 free(p->defs[i].key);                  free(p->defs[i].key);

Legend:
Removed from v.1.60  
changed lines
  Added in v.1.80

CVSweb