1*371584c2SYuri Pankov /* $Id: eqn.c,v 1.61 2016/01/08 00:50:45 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 3260e9a87SYuri Pankov * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4260e9a87SYuri Pankov * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> 595c635efSGarrett D'Amore * 695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 995c635efSGarrett D'Amore * 1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1795c635efSGarrett D'Amore */ 1895c635efSGarrett D'Amore #include "config.h" 19260e9a87SYuri Pankov 20260e9a87SYuri Pankov #include <sys/types.h> 2195c635efSGarrett D'Amore 2295c635efSGarrett D'Amore #include <assert.h> 2395c635efSGarrett D'Amore #include <limits.h> 2495c635efSGarrett D'Amore #include <stdio.h> 2595c635efSGarrett D'Amore #include <stdlib.h> 2695c635efSGarrett D'Amore #include <string.h> 2795c635efSGarrett D'Amore #include <time.h> 2895c635efSGarrett D'Amore 2995c635efSGarrett D'Amore #include "mandoc.h" 30260e9a87SYuri Pankov #include "mandoc_aux.h" 3195c635efSGarrett D'Amore #include "libmandoc.h" 3295c635efSGarrett D'Amore #include "libroff.h" 3395c635efSGarrett D'Amore 3495c635efSGarrett D'Amore #define EQN_NEST_MAX 128 /* maximum nesting of defines */ 35260e9a87SYuri Pankov #define STRNEQ(p1, sz1, p2, sz2) \ 36260e9a87SYuri Pankov ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) 3795c635efSGarrett D'Amore 38260e9a87SYuri Pankov enum eqn_tok { 39260e9a87SYuri Pankov EQN_TOK_DYAD = 0, 40260e9a87SYuri Pankov EQN_TOK_VEC, 41260e9a87SYuri Pankov EQN_TOK_UNDER, 42260e9a87SYuri Pankov EQN_TOK_BAR, 43260e9a87SYuri Pankov EQN_TOK_TILDE, 44260e9a87SYuri Pankov EQN_TOK_HAT, 45260e9a87SYuri Pankov EQN_TOK_DOT, 46260e9a87SYuri Pankov EQN_TOK_DOTDOT, 47260e9a87SYuri Pankov EQN_TOK_FWD, 48260e9a87SYuri Pankov EQN_TOK_BACK, 49260e9a87SYuri Pankov EQN_TOK_DOWN, 50260e9a87SYuri Pankov EQN_TOK_UP, 51260e9a87SYuri Pankov EQN_TOK_FAT, 52260e9a87SYuri Pankov EQN_TOK_ROMAN, 53260e9a87SYuri Pankov EQN_TOK_ITALIC, 54260e9a87SYuri Pankov EQN_TOK_BOLD, 55260e9a87SYuri Pankov EQN_TOK_SIZE, 56260e9a87SYuri Pankov EQN_TOK_SUB, 57260e9a87SYuri Pankov EQN_TOK_SUP, 58260e9a87SYuri Pankov EQN_TOK_SQRT, 59260e9a87SYuri Pankov EQN_TOK_OVER, 60260e9a87SYuri Pankov EQN_TOK_FROM, 61260e9a87SYuri Pankov EQN_TOK_TO, 62260e9a87SYuri Pankov EQN_TOK_BRACE_OPEN, 63260e9a87SYuri Pankov EQN_TOK_BRACE_CLOSE, 64260e9a87SYuri Pankov EQN_TOK_GSIZE, 65260e9a87SYuri Pankov EQN_TOK_GFONT, 66260e9a87SYuri Pankov EQN_TOK_MARK, 67260e9a87SYuri Pankov EQN_TOK_LINEUP, 68260e9a87SYuri Pankov EQN_TOK_LEFT, 69260e9a87SYuri Pankov EQN_TOK_RIGHT, 70260e9a87SYuri Pankov EQN_TOK_PILE, 71260e9a87SYuri Pankov EQN_TOK_LPILE, 72260e9a87SYuri Pankov EQN_TOK_RPILE, 73260e9a87SYuri Pankov EQN_TOK_CPILE, 74260e9a87SYuri Pankov EQN_TOK_MATRIX, 75260e9a87SYuri Pankov EQN_TOK_CCOL, 76260e9a87SYuri Pankov EQN_TOK_LCOL, 77260e9a87SYuri Pankov EQN_TOK_RCOL, 78260e9a87SYuri Pankov EQN_TOK_DELIM, 79260e9a87SYuri Pankov EQN_TOK_DEFINE, 80260e9a87SYuri Pankov EQN_TOK_TDEFINE, 81260e9a87SYuri Pankov EQN_TOK_NDEFINE, 82260e9a87SYuri Pankov EQN_TOK_UNDEF, 83260e9a87SYuri Pankov EQN_TOK_EOF, 84260e9a87SYuri Pankov EQN_TOK_ABOVE, 85260e9a87SYuri Pankov EQN_TOK__MAX 86260e9a87SYuri Pankov }; 87260e9a87SYuri Pankov 88260e9a87SYuri Pankov static const char *eqn_toks[EQN_TOK__MAX] = { 89260e9a87SYuri Pankov "dyad", /* EQN_TOK_DYAD */ 90260e9a87SYuri Pankov "vec", /* EQN_TOK_VEC */ 91260e9a87SYuri Pankov "under", /* EQN_TOK_UNDER */ 92260e9a87SYuri Pankov "bar", /* EQN_TOK_BAR */ 93260e9a87SYuri Pankov "tilde", /* EQN_TOK_TILDE */ 94260e9a87SYuri Pankov "hat", /* EQN_TOK_HAT */ 95260e9a87SYuri Pankov "dot", /* EQN_TOK_DOT */ 96260e9a87SYuri Pankov "dotdot", /* EQN_TOK_DOTDOT */ 97260e9a87SYuri Pankov "fwd", /* EQN_TOK_FWD * */ 98260e9a87SYuri Pankov "back", /* EQN_TOK_BACK */ 99260e9a87SYuri Pankov "down", /* EQN_TOK_DOWN */ 100260e9a87SYuri Pankov "up", /* EQN_TOK_UP */ 101260e9a87SYuri Pankov "fat", /* EQN_TOK_FAT */ 102260e9a87SYuri Pankov "roman", /* EQN_TOK_ROMAN */ 103260e9a87SYuri Pankov "italic", /* EQN_TOK_ITALIC */ 104260e9a87SYuri Pankov "bold", /* EQN_TOK_BOLD */ 105260e9a87SYuri Pankov "size", /* EQN_TOK_SIZE */ 106260e9a87SYuri Pankov "sub", /* EQN_TOK_SUB */ 107260e9a87SYuri Pankov "sup", /* EQN_TOK_SUP */ 108260e9a87SYuri Pankov "sqrt", /* EQN_TOK_SQRT */ 109260e9a87SYuri Pankov "over", /* EQN_TOK_OVER */ 110260e9a87SYuri Pankov "from", /* EQN_TOK_FROM */ 111260e9a87SYuri Pankov "to", /* EQN_TOK_TO */ 112260e9a87SYuri Pankov "{", /* EQN_TOK_BRACE_OPEN */ 113260e9a87SYuri Pankov "}", /* EQN_TOK_BRACE_CLOSE */ 114260e9a87SYuri Pankov "gsize", /* EQN_TOK_GSIZE */ 115260e9a87SYuri Pankov "gfont", /* EQN_TOK_GFONT */ 116260e9a87SYuri Pankov "mark", /* EQN_TOK_MARK */ 117260e9a87SYuri Pankov "lineup", /* EQN_TOK_LINEUP */ 118260e9a87SYuri Pankov "left", /* EQN_TOK_LEFT */ 119260e9a87SYuri Pankov "right", /* EQN_TOK_RIGHT */ 120260e9a87SYuri Pankov "pile", /* EQN_TOK_PILE */ 121260e9a87SYuri Pankov "lpile", /* EQN_TOK_LPILE */ 122260e9a87SYuri Pankov "rpile", /* EQN_TOK_RPILE */ 123260e9a87SYuri Pankov "cpile", /* EQN_TOK_CPILE */ 124260e9a87SYuri Pankov "matrix", /* EQN_TOK_MATRIX */ 125260e9a87SYuri Pankov "ccol", /* EQN_TOK_CCOL */ 126260e9a87SYuri Pankov "lcol", /* EQN_TOK_LCOL */ 127260e9a87SYuri Pankov "rcol", /* EQN_TOK_RCOL */ 128260e9a87SYuri Pankov "delim", /* EQN_TOK_DELIM */ 129260e9a87SYuri Pankov "define", /* EQN_TOK_DEFINE */ 130260e9a87SYuri Pankov "tdefine", /* EQN_TOK_TDEFINE */ 131260e9a87SYuri Pankov "ndefine", /* EQN_TOK_NDEFINE */ 132260e9a87SYuri Pankov "undef", /* EQN_TOK_UNDEF */ 133260e9a87SYuri Pankov NULL, /* EQN_TOK_EOF */ 134260e9a87SYuri Pankov "above", /* EQN_TOK_ABOVE */ 13595c635efSGarrett D'Amore }; 13695c635efSGarrett D'Amore 13795c635efSGarrett D'Amore enum eqn_symt { 13895c635efSGarrett D'Amore EQNSYM_alpha, 13995c635efSGarrett D'Amore EQNSYM_beta, 14095c635efSGarrett D'Amore EQNSYM_chi, 14195c635efSGarrett D'Amore EQNSYM_delta, 14295c635efSGarrett D'Amore EQNSYM_epsilon, 14395c635efSGarrett D'Amore EQNSYM_eta, 14495c635efSGarrett D'Amore EQNSYM_gamma, 14595c635efSGarrett D'Amore EQNSYM_iota, 14695c635efSGarrett D'Amore EQNSYM_kappa, 14795c635efSGarrett D'Amore EQNSYM_lambda, 14895c635efSGarrett D'Amore EQNSYM_mu, 14995c635efSGarrett D'Amore EQNSYM_nu, 15095c635efSGarrett D'Amore EQNSYM_omega, 15195c635efSGarrett D'Amore EQNSYM_omicron, 15295c635efSGarrett D'Amore EQNSYM_phi, 15395c635efSGarrett D'Amore EQNSYM_pi, 15495c635efSGarrett D'Amore EQNSYM_ps, 15595c635efSGarrett D'Amore EQNSYM_rho, 15695c635efSGarrett D'Amore EQNSYM_sigma, 15795c635efSGarrett D'Amore EQNSYM_tau, 15895c635efSGarrett D'Amore EQNSYM_theta, 15995c635efSGarrett D'Amore EQNSYM_upsilon, 16095c635efSGarrett D'Amore EQNSYM_xi, 16195c635efSGarrett D'Amore EQNSYM_zeta, 16295c635efSGarrett D'Amore EQNSYM_DELTA, 16395c635efSGarrett D'Amore EQNSYM_GAMMA, 16495c635efSGarrett D'Amore EQNSYM_LAMBDA, 16595c635efSGarrett D'Amore EQNSYM_OMEGA, 16695c635efSGarrett D'Amore EQNSYM_PHI, 16795c635efSGarrett D'Amore EQNSYM_PI, 16895c635efSGarrett D'Amore EQNSYM_PSI, 16995c635efSGarrett D'Amore EQNSYM_SIGMA, 17095c635efSGarrett D'Amore EQNSYM_THETA, 17195c635efSGarrett D'Amore EQNSYM_UPSILON, 17295c635efSGarrett D'Amore EQNSYM_XI, 17395c635efSGarrett D'Amore EQNSYM_inter, 17495c635efSGarrett D'Amore EQNSYM_union, 17595c635efSGarrett D'Amore EQNSYM_prod, 17695c635efSGarrett D'Amore EQNSYM_int, 17795c635efSGarrett D'Amore EQNSYM_sum, 17895c635efSGarrett D'Amore EQNSYM_grad, 17995c635efSGarrett D'Amore EQNSYM_del, 18095c635efSGarrett D'Amore EQNSYM_times, 18195c635efSGarrett D'Amore EQNSYM_cdot, 18295c635efSGarrett D'Amore EQNSYM_nothing, 18395c635efSGarrett D'Amore EQNSYM_approx, 18495c635efSGarrett D'Amore EQNSYM_prime, 18595c635efSGarrett D'Amore EQNSYM_half, 18695c635efSGarrett D'Amore EQNSYM_partial, 18795c635efSGarrett D'Amore EQNSYM_inf, 18895c635efSGarrett D'Amore EQNSYM_muchgreat, 18995c635efSGarrett D'Amore EQNSYM_muchless, 19095c635efSGarrett D'Amore EQNSYM_larrow, 19195c635efSGarrett D'Amore EQNSYM_rarrow, 19295c635efSGarrett D'Amore EQNSYM_pm, 19395c635efSGarrett D'Amore EQNSYM_nequal, 19495c635efSGarrett D'Amore EQNSYM_equiv, 19595c635efSGarrett D'Amore EQNSYM_lessequal, 19695c635efSGarrett D'Amore EQNSYM_moreequal, 197260e9a87SYuri Pankov EQNSYM_minus, 19895c635efSGarrett D'Amore EQNSYM__MAX 19995c635efSGarrett D'Amore }; 20095c635efSGarrett D'Amore 20195c635efSGarrett D'Amore struct eqnsym { 202260e9a87SYuri Pankov const char *str; 20395c635efSGarrett D'Amore const char *sym; 20495c635efSGarrett D'Amore }; 20595c635efSGarrett D'Amore 20695c635efSGarrett D'Amore static const struct eqnsym eqnsyms[EQNSYM__MAX] = { 207260e9a87SYuri Pankov { "alpha", "*a" }, /* EQNSYM_alpha */ 208260e9a87SYuri Pankov { "beta", "*b" }, /* EQNSYM_beta */ 209260e9a87SYuri Pankov { "chi", "*x" }, /* EQNSYM_chi */ 210260e9a87SYuri Pankov { "delta", "*d" }, /* EQNSYM_delta */ 211260e9a87SYuri Pankov { "epsilon", "*e" }, /* EQNSYM_epsilon */ 212260e9a87SYuri Pankov { "eta", "*y" }, /* EQNSYM_eta */ 213260e9a87SYuri Pankov { "gamma", "*g" }, /* EQNSYM_gamma */ 214260e9a87SYuri Pankov { "iota", "*i" }, /* EQNSYM_iota */ 215260e9a87SYuri Pankov { "kappa", "*k" }, /* EQNSYM_kappa */ 216260e9a87SYuri Pankov { "lambda", "*l" }, /* EQNSYM_lambda */ 217260e9a87SYuri Pankov { "mu", "*m" }, /* EQNSYM_mu */ 218260e9a87SYuri Pankov { "nu", "*n" }, /* EQNSYM_nu */ 219260e9a87SYuri Pankov { "omega", "*w" }, /* EQNSYM_omega */ 220260e9a87SYuri Pankov { "omicron", "*o" }, /* EQNSYM_omicron */ 221260e9a87SYuri Pankov { "phi", "*f" }, /* EQNSYM_phi */ 222260e9a87SYuri Pankov { "pi", "*p" }, /* EQNSYM_pi */ 223260e9a87SYuri Pankov { "psi", "*q" }, /* EQNSYM_psi */ 224260e9a87SYuri Pankov { "rho", "*r" }, /* EQNSYM_rho */ 225260e9a87SYuri Pankov { "sigma", "*s" }, /* EQNSYM_sigma */ 226260e9a87SYuri Pankov { "tau", "*t" }, /* EQNSYM_tau */ 227260e9a87SYuri Pankov { "theta", "*h" }, /* EQNSYM_theta */ 228260e9a87SYuri Pankov { "upsilon", "*u" }, /* EQNSYM_upsilon */ 229260e9a87SYuri Pankov { "xi", "*c" }, /* EQNSYM_xi */ 230260e9a87SYuri Pankov { "zeta", "*z" }, /* EQNSYM_zeta */ 231260e9a87SYuri Pankov { "DELTA", "*D" }, /* EQNSYM_DELTA */ 232260e9a87SYuri Pankov { "GAMMA", "*G" }, /* EQNSYM_GAMMA */ 233260e9a87SYuri Pankov { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */ 234260e9a87SYuri Pankov { "OMEGA", "*W" }, /* EQNSYM_OMEGA */ 235260e9a87SYuri Pankov { "PHI", "*F" }, /* EQNSYM_PHI */ 236260e9a87SYuri Pankov { "PI", "*P" }, /* EQNSYM_PI */ 237260e9a87SYuri Pankov { "PSI", "*Q" }, /* EQNSYM_PSI */ 238260e9a87SYuri Pankov { "SIGMA", "*S" }, /* EQNSYM_SIGMA */ 239260e9a87SYuri Pankov { "THETA", "*H" }, /* EQNSYM_THETA */ 240260e9a87SYuri Pankov { "UPSILON", "*U" }, /* EQNSYM_UPSILON */ 241260e9a87SYuri Pankov { "XI", "*C" }, /* EQNSYM_XI */ 242260e9a87SYuri Pankov { "inter", "ca" }, /* EQNSYM_inter */ 243260e9a87SYuri Pankov { "union", "cu" }, /* EQNSYM_union */ 244260e9a87SYuri Pankov { "prod", "product" }, /* EQNSYM_prod */ 245260e9a87SYuri Pankov { "int", "integral" }, /* EQNSYM_int */ 246260e9a87SYuri Pankov { "sum", "sum" }, /* EQNSYM_sum */ 247260e9a87SYuri Pankov { "grad", "gr" }, /* EQNSYM_grad */ 248260e9a87SYuri Pankov { "del", "gr" }, /* EQNSYM_del */ 249260e9a87SYuri Pankov { "times", "mu" }, /* EQNSYM_times */ 250260e9a87SYuri Pankov { "cdot", "pc" }, /* EQNSYM_cdot */ 251260e9a87SYuri Pankov { "nothing", "&" }, /* EQNSYM_nothing */ 252260e9a87SYuri Pankov { "approx", "~~" }, /* EQNSYM_approx */ 253260e9a87SYuri Pankov { "prime", "fm" }, /* EQNSYM_prime */ 254260e9a87SYuri Pankov { "half", "12" }, /* EQNSYM_half */ 255260e9a87SYuri Pankov { "partial", "pd" }, /* EQNSYM_partial */ 256260e9a87SYuri Pankov { "inf", "if" }, /* EQNSYM_inf */ 257260e9a87SYuri Pankov { ">>", ">>" }, /* EQNSYM_muchgreat */ 258260e9a87SYuri Pankov { "<<", "<<" }, /* EQNSYM_muchless */ 259260e9a87SYuri Pankov { "<-", "<-" }, /* EQNSYM_larrow */ 260260e9a87SYuri Pankov { "->", "->" }, /* EQNSYM_rarrow */ 261260e9a87SYuri Pankov { "+-", "+-" }, /* EQNSYM_pm */ 262260e9a87SYuri Pankov { "!=", "!=" }, /* EQNSYM_nequal */ 263260e9a87SYuri Pankov { "==", "==" }, /* EQNSYM_equiv */ 264260e9a87SYuri Pankov { "<=", "<=" }, /* EQNSYM_lessequal */ 265260e9a87SYuri Pankov { ">=", ">=" }, /* EQNSYM_moreequal */ 266260e9a87SYuri Pankov { "-", "mi" }, /* EQNSYM_minus */ 26795c635efSGarrett D'Amore }; 26895c635efSGarrett D'Amore 269260e9a87SYuri Pankov static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); 270260e9a87SYuri Pankov static void eqn_box_free(struct eqn_box *); 271260e9a87SYuri Pankov static struct eqn_box *eqn_box_makebinary(struct eqn_node *, 272260e9a87SYuri Pankov enum eqn_post, struct eqn_box *); 273260e9a87SYuri Pankov static void eqn_def(struct eqn_node *); 274260e9a87SYuri Pankov static struct eqn_def *eqn_def_find(struct eqn_node *, const char *, size_t); 275260e9a87SYuri Pankov static void eqn_delim(struct eqn_node *); 276260e9a87SYuri Pankov static const char *eqn_next(struct eqn_node *, char, size_t *, int); 277260e9a87SYuri Pankov static const char *eqn_nextrawtok(struct eqn_node *, size_t *); 278260e9a87SYuri Pankov static const char *eqn_nexttok(struct eqn_node *, size_t *); 279260e9a87SYuri Pankov static enum rofferr eqn_parse(struct eqn_node *, struct eqn_box *); 280260e9a87SYuri Pankov static enum eqn_tok eqn_tok_parse(struct eqn_node *, char **); 281260e9a87SYuri Pankov static void eqn_undef(struct eqn_node *); 282260e9a87SYuri Pankov 283260e9a87SYuri Pankov 28495c635efSGarrett D'Amore enum rofferr 28595c635efSGarrett D'Amore eqn_read(struct eqn_node **epp, int ln, 28695c635efSGarrett D'Amore const char *p, int pos, int *offs) 28795c635efSGarrett D'Amore { 28895c635efSGarrett D'Amore size_t sz; 28995c635efSGarrett D'Amore struct eqn_node *ep; 29095c635efSGarrett D'Amore enum rofferr er; 29195c635efSGarrett D'Amore 29295c635efSGarrett D'Amore ep = *epp; 29395c635efSGarrett D'Amore 29495c635efSGarrett D'Amore /* 29595c635efSGarrett D'Amore * If we're the terminating mark, unset our equation status and 29695c635efSGarrett D'Amore * validate the full equation. 29795c635efSGarrett D'Amore */ 29895c635efSGarrett D'Amore 29995c635efSGarrett D'Amore if (0 == strncmp(p, ".EN", 3)) { 30095c635efSGarrett D'Amore er = eqn_end(epp); 30195c635efSGarrett D'Amore p += 3; 30295c635efSGarrett D'Amore while (' ' == *p || '\t' == *p) 30395c635efSGarrett D'Amore p++; 30495c635efSGarrett D'Amore if ('\0' == *p) 305*371584c2SYuri Pankov return er; 306260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse, 307260e9a87SYuri Pankov ln, pos, "EN %s", p); 308*371584c2SYuri Pankov return er; 30995c635efSGarrett D'Amore } 31095c635efSGarrett D'Amore 31195c635efSGarrett D'Amore /* 31295c635efSGarrett D'Amore * Build up the full string, replacing all newlines with regular 31395c635efSGarrett D'Amore * whitespace. 31495c635efSGarrett D'Amore */ 31595c635efSGarrett D'Amore 31695c635efSGarrett D'Amore sz = strlen(p + pos) + 1; 31795c635efSGarrett D'Amore ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1); 31895c635efSGarrett D'Amore 31995c635efSGarrett D'Amore /* First invocation: nil terminate the string. */ 32095c635efSGarrett D'Amore 32195c635efSGarrett D'Amore if (0 == ep->sz) 32295c635efSGarrett D'Amore *ep->data = '\0'; 32395c635efSGarrett D'Amore 32495c635efSGarrett D'Amore ep->sz += sz; 32595c635efSGarrett D'Amore strlcat(ep->data, p + pos, ep->sz + 1); 32695c635efSGarrett D'Amore strlcat(ep->data, " ", ep->sz + 1); 327*371584c2SYuri Pankov return ROFF_IGN; 32895c635efSGarrett D'Amore } 32995c635efSGarrett D'Amore 33095c635efSGarrett D'Amore struct eqn_node * 331260e9a87SYuri Pankov eqn_alloc(int pos, int line, struct mparse *parse) 33295c635efSGarrett D'Amore { 33395c635efSGarrett D'Amore struct eqn_node *p; 33495c635efSGarrett D'Amore 33595c635efSGarrett D'Amore p = mandoc_calloc(1, sizeof(struct eqn_node)); 33695c635efSGarrett D'Amore 33795c635efSGarrett D'Amore p->parse = parse; 33895c635efSGarrett D'Amore p->eqn.ln = line; 33995c635efSGarrett D'Amore p->eqn.pos = pos; 34095c635efSGarrett D'Amore p->gsize = EQN_DEFSIZE; 34195c635efSGarrett D'Amore 342*371584c2SYuri Pankov return p; 34395c635efSGarrett D'Amore } 34495c635efSGarrett D'Amore 345260e9a87SYuri Pankov /* 346260e9a87SYuri Pankov * Find the key "key" of the give size within our eqn-defined values. 347260e9a87SYuri Pankov */ 348260e9a87SYuri Pankov static struct eqn_def * 349260e9a87SYuri Pankov eqn_def_find(struct eqn_node *ep, const char *key, size_t sz) 35095c635efSGarrett D'Amore { 35195c635efSGarrett D'Amore int i; 35295c635efSGarrett D'Amore 353260e9a87SYuri Pankov for (i = 0; i < (int)ep->defsz; i++) 354260e9a87SYuri Pankov if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, 355260e9a87SYuri Pankov ep->defs[i].keysz, key, sz)) 356*371584c2SYuri Pankov return &ep->defs[i]; 35795c635efSGarrett D'Amore 358*371584c2SYuri Pankov return NULL; 35995c635efSGarrett D'Amore } 36095c635efSGarrett D'Amore 361260e9a87SYuri Pankov /* 362260e9a87SYuri Pankov * Get the next token from the input stream using the given quote 363260e9a87SYuri Pankov * character. 364260e9a87SYuri Pankov * Optionally make any replacements. 365260e9a87SYuri Pankov */ 36695c635efSGarrett D'Amore static const char * 36795c635efSGarrett D'Amore eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl) 36895c635efSGarrett D'Amore { 36995c635efSGarrett D'Amore char *start, *next; 37095c635efSGarrett D'Amore int q, diff, lim; 37195c635efSGarrett D'Amore size_t ssz, dummy; 37295c635efSGarrett D'Amore struct eqn_def *def; 37395c635efSGarrett D'Amore 37495c635efSGarrett D'Amore if (NULL == sz) 37595c635efSGarrett D'Amore sz = &dummy; 37695c635efSGarrett D'Amore 37795c635efSGarrett D'Amore lim = 0; 37895c635efSGarrett D'Amore ep->rew = ep->cur; 37995c635efSGarrett D'Amore again: 38095c635efSGarrett D'Amore /* Prevent self-definitions. */ 38195c635efSGarrett D'Amore 38295c635efSGarrett D'Amore if (lim >= EQN_NEST_MAX) { 383260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, 384260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, NULL); 385*371584c2SYuri Pankov return NULL; 38695c635efSGarrett D'Amore } 38795c635efSGarrett D'Amore 38895c635efSGarrett D'Amore ep->cur = ep->rew; 38995c635efSGarrett D'Amore start = &ep->data[(int)ep->cur]; 39095c635efSGarrett D'Amore q = 0; 39195c635efSGarrett D'Amore 39295c635efSGarrett D'Amore if ('\0' == *start) 393*371584c2SYuri Pankov return NULL; 39495c635efSGarrett D'Amore 39595c635efSGarrett D'Amore if (quote == *start) { 39695c635efSGarrett D'Amore ep->cur++; 39795c635efSGarrett D'Amore q = 1; 39895c635efSGarrett D'Amore } 39995c635efSGarrett D'Amore 40095c635efSGarrett D'Amore start = &ep->data[(int)ep->cur]; 40195c635efSGarrett D'Amore 40295c635efSGarrett D'Amore if ( ! q) { 40395c635efSGarrett D'Amore if ('{' == *start || '}' == *start) 40495c635efSGarrett D'Amore ssz = 1; 40595c635efSGarrett D'Amore else 40695c635efSGarrett D'Amore ssz = strcspn(start + 1, " ^~\"{}\t") + 1; 40795c635efSGarrett D'Amore next = start + (int)ssz; 40895c635efSGarrett D'Amore if ('\0' == *next) 40995c635efSGarrett D'Amore next = NULL; 41095c635efSGarrett D'Amore } else 41195c635efSGarrett D'Amore next = strchr(start, quote); 41295c635efSGarrett D'Amore 41395c635efSGarrett D'Amore if (NULL != next) { 41495c635efSGarrett D'Amore *sz = (size_t)(next - start); 41595c635efSGarrett D'Amore ep->cur += *sz; 41695c635efSGarrett D'Amore if (q) 41795c635efSGarrett D'Amore ep->cur++; 41895c635efSGarrett D'Amore while (' ' == ep->data[(int)ep->cur] || 41995c635efSGarrett D'Amore '\t' == ep->data[(int)ep->cur] || 42095c635efSGarrett D'Amore '^' == ep->data[(int)ep->cur] || 42195c635efSGarrett D'Amore '~' == ep->data[(int)ep->cur]) 42295c635efSGarrett D'Amore ep->cur++; 42395c635efSGarrett D'Amore } else { 42495c635efSGarrett D'Amore if (q) 425260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, 426260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, NULL); 42795c635efSGarrett D'Amore next = strchr(start, '\0'); 42895c635efSGarrett D'Amore *sz = (size_t)(next - start); 42995c635efSGarrett D'Amore ep->cur += *sz; 43095c635efSGarrett D'Amore } 43195c635efSGarrett D'Amore 43295c635efSGarrett D'Amore /* Quotes aren't expanded for values. */ 43395c635efSGarrett D'Amore 43495c635efSGarrett D'Amore if (q || ! repl) 435*371584c2SYuri Pankov return start; 43695c635efSGarrett D'Amore 43795c635efSGarrett D'Amore if (NULL != (def = eqn_def_find(ep, start, *sz))) { 43895c635efSGarrett D'Amore diff = def->valsz - *sz; 43995c635efSGarrett D'Amore 44095c635efSGarrett D'Amore if (def->valsz > *sz) { 44195c635efSGarrett D'Amore ep->sz += diff; 44295c635efSGarrett D'Amore ep->data = mandoc_realloc(ep->data, ep->sz + 1); 44395c635efSGarrett D'Amore ep->data[ep->sz] = '\0'; 44495c635efSGarrett D'Amore start = &ep->data[(int)ep->rew]; 44595c635efSGarrett D'Amore } 44695c635efSGarrett D'Amore 44795c635efSGarrett D'Amore diff = def->valsz - *sz; 44895c635efSGarrett D'Amore memmove(start + *sz + diff, start + *sz, 44995c635efSGarrett D'Amore (strlen(start) - *sz) + 1); 45095c635efSGarrett D'Amore memcpy(start, def->val, def->valsz); 451*371584c2SYuri Pankov lim++; 45295c635efSGarrett D'Amore goto again; 45395c635efSGarrett D'Amore } 45495c635efSGarrett D'Amore 455*371584c2SYuri Pankov return start; 45695c635efSGarrett D'Amore } 45795c635efSGarrett D'Amore 458260e9a87SYuri Pankov /* 459260e9a87SYuri Pankov * Get the next delimited token using the default current quote 460260e9a87SYuri Pankov * character. 461260e9a87SYuri Pankov */ 462260e9a87SYuri Pankov static const char * 463260e9a87SYuri Pankov eqn_nexttok(struct eqn_node *ep, size_t *sz) 46495c635efSGarrett D'Amore { 46595c635efSGarrett D'Amore 466*371584c2SYuri Pankov return eqn_next(ep, '"', sz, 1); 46795c635efSGarrett D'Amore } 46895c635efSGarrett D'Amore 469260e9a87SYuri Pankov /* 470260e9a87SYuri Pankov * Get next token without replacement. 471260e9a87SYuri Pankov */ 472260e9a87SYuri Pankov static const char * 473260e9a87SYuri Pankov eqn_nextrawtok(struct eqn_node *ep, size_t *sz) 47495c635efSGarrett D'Amore { 47595c635efSGarrett D'Amore 476*371584c2SYuri Pankov return eqn_next(ep, '"', sz, 0); 47795c635efSGarrett D'Amore } 47895c635efSGarrett D'Amore 479260e9a87SYuri Pankov /* 480260e9a87SYuri Pankov * Parse a token from the stream of text. 481260e9a87SYuri Pankov * A token consists of one of the recognised eqn(7) strings. 482260e9a87SYuri Pankov * Strings are separated by delimiting marks. 483260e9a87SYuri Pankov * This returns EQN_TOK_EOF when there are no more tokens. 484260e9a87SYuri Pankov * If the token is an unrecognised string literal, then it returns 485260e9a87SYuri Pankov * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated 486260e9a87SYuri Pankov * string. 487260e9a87SYuri Pankov * This must be later freed with free(3). 488260e9a87SYuri Pankov */ 489260e9a87SYuri Pankov static enum eqn_tok 490260e9a87SYuri Pankov eqn_tok_parse(struct eqn_node *ep, char **p) 491260e9a87SYuri Pankov { 492260e9a87SYuri Pankov const char *start; 493260e9a87SYuri Pankov size_t i, sz; 494260e9a87SYuri Pankov int quoted; 495260e9a87SYuri Pankov 496260e9a87SYuri Pankov if (NULL != p) 497260e9a87SYuri Pankov *p = NULL; 498260e9a87SYuri Pankov 499260e9a87SYuri Pankov quoted = ep->data[ep->cur] == '"'; 500260e9a87SYuri Pankov 501260e9a87SYuri Pankov if (NULL == (start = eqn_nexttok(ep, &sz))) 502*371584c2SYuri Pankov return EQN_TOK_EOF; 503260e9a87SYuri Pankov 504260e9a87SYuri Pankov if (quoted) { 505260e9a87SYuri Pankov if (p != NULL) 506260e9a87SYuri Pankov *p = mandoc_strndup(start, sz); 507*371584c2SYuri Pankov return EQN_TOK__MAX; 508260e9a87SYuri Pankov } 509260e9a87SYuri Pankov 510260e9a87SYuri Pankov for (i = 0; i < EQN_TOK__MAX; i++) { 511260e9a87SYuri Pankov if (NULL == eqn_toks[i]) 512260e9a87SYuri Pankov continue; 513260e9a87SYuri Pankov if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i]))) 514260e9a87SYuri Pankov break; 515260e9a87SYuri Pankov } 516260e9a87SYuri Pankov 517260e9a87SYuri Pankov if (i == EQN_TOK__MAX && NULL != p) 518260e9a87SYuri Pankov *p = mandoc_strndup(start, sz); 519260e9a87SYuri Pankov 520*371584c2SYuri Pankov return i; 521260e9a87SYuri Pankov } 522260e9a87SYuri Pankov 523260e9a87SYuri Pankov static void 524260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp) 52595c635efSGarrett D'Amore { 52695c635efSGarrett D'Amore 527260e9a87SYuri Pankov if (bp->first) 528260e9a87SYuri Pankov eqn_box_free(bp->first); 529260e9a87SYuri Pankov if (bp->next) 530260e9a87SYuri Pankov eqn_box_free(bp->next); 53195c635efSGarrett D'Amore 532260e9a87SYuri Pankov free(bp->text); 533260e9a87SYuri Pankov free(bp->left); 534260e9a87SYuri Pankov free(bp->right); 535260e9a87SYuri Pankov free(bp->top); 536260e9a87SYuri Pankov free(bp->bottom); 537260e9a87SYuri Pankov free(bp); 53895c635efSGarrett D'Amore } 53995c635efSGarrett D'Amore 540260e9a87SYuri Pankov /* 541260e9a87SYuri Pankov * Allocate a box as the last child of the parent node. 542260e9a87SYuri Pankov */ 543260e9a87SYuri Pankov static struct eqn_box * 544260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) 545260e9a87SYuri Pankov { 546260e9a87SYuri Pankov struct eqn_box *bp; 547260e9a87SYuri Pankov 548260e9a87SYuri Pankov bp = mandoc_calloc(1, sizeof(struct eqn_box)); 549260e9a87SYuri Pankov bp->parent = parent; 550260e9a87SYuri Pankov bp->parent->args++; 551260e9a87SYuri Pankov bp->expectargs = UINT_MAX; 552260e9a87SYuri Pankov bp->size = ep->gsize; 553260e9a87SYuri Pankov 554260e9a87SYuri Pankov if (NULL != parent->first) { 555260e9a87SYuri Pankov parent->last->next = bp; 556260e9a87SYuri Pankov bp->prev = parent->last; 557260e9a87SYuri Pankov } else 558260e9a87SYuri Pankov parent->first = bp; 559260e9a87SYuri Pankov 560260e9a87SYuri Pankov parent->last = bp; 561*371584c2SYuri Pankov return bp; 562260e9a87SYuri Pankov } 563260e9a87SYuri Pankov 564260e9a87SYuri Pankov /* 565260e9a87SYuri Pankov * Reparent the current last node (of the current parent) under a new 566260e9a87SYuri Pankov * EQN_SUBEXPR as the first element. 567260e9a87SYuri Pankov * Then return the new parent. 568260e9a87SYuri Pankov * The new EQN_SUBEXPR will have a two-child limit. 569260e9a87SYuri Pankov */ 570260e9a87SYuri Pankov static struct eqn_box * 571260e9a87SYuri Pankov eqn_box_makebinary(struct eqn_node *ep, 572260e9a87SYuri Pankov enum eqn_post pos, struct eqn_box *parent) 573260e9a87SYuri Pankov { 574260e9a87SYuri Pankov struct eqn_box *b, *newb; 575260e9a87SYuri Pankov 576260e9a87SYuri Pankov assert(NULL != parent->last); 577260e9a87SYuri Pankov b = parent->last; 578260e9a87SYuri Pankov if (parent->last == parent->first) 579260e9a87SYuri Pankov parent->first = NULL; 580260e9a87SYuri Pankov parent->args--; 581260e9a87SYuri Pankov parent->last = b->prev; 582260e9a87SYuri Pankov b->prev = NULL; 583260e9a87SYuri Pankov newb = eqn_box_alloc(ep, parent); 584260e9a87SYuri Pankov newb->pos = pos; 585260e9a87SYuri Pankov newb->type = EQN_SUBEXPR; 586260e9a87SYuri Pankov newb->expectargs = 2; 587260e9a87SYuri Pankov newb->args = 1; 588260e9a87SYuri Pankov newb->first = newb->last = b; 589260e9a87SYuri Pankov newb->first->next = NULL; 590260e9a87SYuri Pankov b->parent = newb; 591*371584c2SYuri Pankov return newb; 592260e9a87SYuri Pankov } 593260e9a87SYuri Pankov 594260e9a87SYuri Pankov /* 595260e9a87SYuri Pankov * Parse the "delim" control statement. 596260e9a87SYuri Pankov */ 597260e9a87SYuri Pankov static void 598260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep) 599260e9a87SYuri Pankov { 600260e9a87SYuri Pankov const char *start; 601260e9a87SYuri Pankov size_t sz; 602260e9a87SYuri Pankov 603260e9a87SYuri Pankov if ((start = eqn_nextrawtok(ep, &sz)) == NULL) 604260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 605260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, "delim"); 606260e9a87SYuri Pankov else if (strncmp(start, "off", 3) == 0) 607260e9a87SYuri Pankov ep->delim = 0; 608260e9a87SYuri Pankov else if (strncmp(start, "on", 2) == 0) { 609260e9a87SYuri Pankov if (ep->odelim && ep->cdelim) 610260e9a87SYuri Pankov ep->delim = 1; 611260e9a87SYuri Pankov } else if (start[1] != '\0') { 612260e9a87SYuri Pankov ep->odelim = start[0]; 613260e9a87SYuri Pankov ep->cdelim = start[1]; 614260e9a87SYuri Pankov ep->delim = 1; 615260e9a87SYuri Pankov } 616260e9a87SYuri Pankov } 617260e9a87SYuri Pankov 618260e9a87SYuri Pankov /* 619260e9a87SYuri Pankov * Undefine a previously-defined string. 620260e9a87SYuri Pankov */ 621260e9a87SYuri Pankov static void 622260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep) 623260e9a87SYuri Pankov { 624260e9a87SYuri Pankov const char *start; 625260e9a87SYuri Pankov struct eqn_def *def; 626260e9a87SYuri Pankov size_t sz; 627260e9a87SYuri Pankov 628260e9a87SYuri Pankov if ((start = eqn_nextrawtok(ep, &sz)) == NULL) { 629260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 630260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, "undef"); 631260e9a87SYuri Pankov return; 632260e9a87SYuri Pankov } 633260e9a87SYuri Pankov if ((def = eqn_def_find(ep, start, sz)) == NULL) 634260e9a87SYuri Pankov return; 635260e9a87SYuri Pankov free(def->key); 636260e9a87SYuri Pankov free(def->val); 637260e9a87SYuri Pankov def->key = def->val = NULL; 638260e9a87SYuri Pankov def->keysz = def->valsz = 0; 639260e9a87SYuri Pankov } 640260e9a87SYuri Pankov 641260e9a87SYuri Pankov static void 642260e9a87SYuri Pankov eqn_def(struct eqn_node *ep) 64395c635efSGarrett D'Amore { 64495c635efSGarrett D'Amore const char *start; 64595c635efSGarrett D'Amore size_t sz; 64695c635efSGarrett D'Amore struct eqn_def *def; 64795c635efSGarrett D'Amore int i; 64895c635efSGarrett D'Amore 649260e9a87SYuri Pankov if ((start = eqn_nextrawtok(ep, &sz)) == NULL) { 650260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 651260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, "define"); 652260e9a87SYuri Pankov return; 65395c635efSGarrett D'Amore } 65495c635efSGarrett D'Amore 65595c635efSGarrett D'Amore /* 65695c635efSGarrett D'Amore * Search for a key that already exists. 65795c635efSGarrett D'Amore * Create a new key if none is found. 65895c635efSGarrett D'Amore */ 65995c635efSGarrett D'Amore if (NULL == (def = eqn_def_find(ep, start, sz))) { 66095c635efSGarrett D'Amore /* Find holes in string array. */ 66195c635efSGarrett D'Amore for (i = 0; i < (int)ep->defsz; i++) 66295c635efSGarrett D'Amore if (0 == ep->defs[i].keysz) 66395c635efSGarrett D'Amore break; 66495c635efSGarrett D'Amore 66595c635efSGarrett D'Amore if (i == (int)ep->defsz) { 66695c635efSGarrett D'Amore ep->defsz++; 667260e9a87SYuri Pankov ep->defs = mandoc_reallocarray(ep->defs, 668260e9a87SYuri Pankov ep->defsz, sizeof(struct eqn_def)); 66995c635efSGarrett D'Amore ep->defs[i].key = ep->defs[i].val = NULL; 67095c635efSGarrett D'Amore } 67195c635efSGarrett D'Amore 672260e9a87SYuri Pankov def = ep->defs + i; 673260e9a87SYuri Pankov free(def->key); 674260e9a87SYuri Pankov def->key = mandoc_strndup(start, sz); 675260e9a87SYuri Pankov def->keysz = sz; 67695c635efSGarrett D'Amore } 67795c635efSGarrett D'Amore 67895c635efSGarrett D'Amore start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0); 679260e9a87SYuri Pankov if (start == NULL) { 680260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, 681260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, "define %s", def->key); 682260e9a87SYuri Pankov free(def->key); 683260e9a87SYuri Pankov free(def->val); 684260e9a87SYuri Pankov def->key = def->val = NULL; 685260e9a87SYuri Pankov def->keysz = def->valsz = 0; 686260e9a87SYuri Pankov return; 68795c635efSGarrett D'Amore } 688260e9a87SYuri Pankov free(def->val); 689260e9a87SYuri Pankov def->val = mandoc_strndup(start, sz); 69095c635efSGarrett D'Amore def->valsz = sz; 69195c635efSGarrett D'Amore } 69295c635efSGarrett D'Amore 693260e9a87SYuri Pankov /* 694260e9a87SYuri Pankov * Recursively parse an eqn(7) expression. 695260e9a87SYuri Pankov */ 696260e9a87SYuri Pankov static enum rofferr 697260e9a87SYuri Pankov eqn_parse(struct eqn_node *ep, struct eqn_box *parent) 69895c635efSGarrett D'Amore { 699260e9a87SYuri Pankov char sym[64]; 700260e9a87SYuri Pankov struct eqn_box *cur; 70195c635efSGarrett D'Amore const char *start; 702260e9a87SYuri Pankov char *p; 703260e9a87SYuri Pankov size_t i, sz; 704260e9a87SYuri Pankov enum eqn_tok tok, subtok; 705260e9a87SYuri Pankov enum eqn_post pos; 706260e9a87SYuri Pankov int size; 70795c635efSGarrett D'Amore 708260e9a87SYuri Pankov assert(parent != NULL); 709260e9a87SYuri Pankov 710260e9a87SYuri Pankov /* 711260e9a87SYuri Pankov * Empty equation. 712260e9a87SYuri Pankov * Do not add it to the high-level syntax tree. 713260e9a87SYuri Pankov */ 714260e9a87SYuri Pankov 715260e9a87SYuri Pankov if (ep->data == NULL) 716*371584c2SYuri Pankov return ROFF_IGN; 717260e9a87SYuri Pankov 718260e9a87SYuri Pankov next_tok: 719260e9a87SYuri Pankov tok = eqn_tok_parse(ep, &p); 720260e9a87SYuri Pankov 721260e9a87SYuri Pankov this_tok: 722260e9a87SYuri Pankov switch (tok) { 723260e9a87SYuri Pankov case (EQN_TOK_UNDEF): 724260e9a87SYuri Pankov eqn_undef(ep); 725260e9a87SYuri Pankov break; 726260e9a87SYuri Pankov case (EQN_TOK_NDEFINE): 727260e9a87SYuri Pankov case (EQN_TOK_DEFINE): 728260e9a87SYuri Pankov eqn_def(ep); 729260e9a87SYuri Pankov break; 730260e9a87SYuri Pankov case (EQN_TOK_TDEFINE): 731260e9a87SYuri Pankov if (eqn_nextrawtok(ep, NULL) == NULL || 732260e9a87SYuri Pankov eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL) 733260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 734260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, "tdefine"); 735260e9a87SYuri Pankov break; 736260e9a87SYuri Pankov case (EQN_TOK_DELIM): 737260e9a87SYuri Pankov eqn_delim(ep); 738260e9a87SYuri Pankov break; 739260e9a87SYuri Pankov case (EQN_TOK_GFONT): 740260e9a87SYuri Pankov if (eqn_nextrawtok(ep, NULL) == NULL) 741260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 742260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 743260e9a87SYuri Pankov break; 744260e9a87SYuri Pankov case (EQN_TOK_MARK): 745260e9a87SYuri Pankov case (EQN_TOK_LINEUP): 746260e9a87SYuri Pankov /* Ignore these. */ 747260e9a87SYuri Pankov break; 748260e9a87SYuri Pankov case (EQN_TOK_DYAD): 749260e9a87SYuri Pankov case (EQN_TOK_VEC): 750260e9a87SYuri Pankov case (EQN_TOK_UNDER): 751260e9a87SYuri Pankov case (EQN_TOK_BAR): 752260e9a87SYuri Pankov case (EQN_TOK_TILDE): 753260e9a87SYuri Pankov case (EQN_TOK_HAT): 754260e9a87SYuri Pankov case (EQN_TOK_DOT): 755260e9a87SYuri Pankov case (EQN_TOK_DOTDOT): 756260e9a87SYuri Pankov if (parent->last == NULL) { 757260e9a87SYuri Pankov mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, 758260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 759260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 760260e9a87SYuri Pankov cur->type = EQN_TEXT; 761260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 76295c635efSGarrett D'Amore } 763260e9a87SYuri Pankov parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent); 764260e9a87SYuri Pankov parent->type = EQN_LISTONE; 765260e9a87SYuri Pankov parent->expectargs = 1; 766260e9a87SYuri Pankov switch (tok) { 767260e9a87SYuri Pankov case (EQN_TOK_DOTDOT): 768260e9a87SYuri Pankov strlcpy(sym, "\\[ad]", sizeof(sym)); 769260e9a87SYuri Pankov break; 770260e9a87SYuri Pankov case (EQN_TOK_VEC): 771260e9a87SYuri Pankov strlcpy(sym, "\\[->]", sizeof(sym)); 772260e9a87SYuri Pankov break; 773260e9a87SYuri Pankov case (EQN_TOK_DYAD): 774260e9a87SYuri Pankov strlcpy(sym, "\\[<>]", sizeof(sym)); 775260e9a87SYuri Pankov break; 776260e9a87SYuri Pankov case (EQN_TOK_TILDE): 777260e9a87SYuri Pankov strlcpy(sym, "\\[a~]", sizeof(sym)); 778260e9a87SYuri Pankov break; 779260e9a87SYuri Pankov case (EQN_TOK_UNDER): 780260e9a87SYuri Pankov strlcpy(sym, "\\[ul]", sizeof(sym)); 781260e9a87SYuri Pankov break; 782260e9a87SYuri Pankov case (EQN_TOK_BAR): 783260e9a87SYuri Pankov strlcpy(sym, "\\[rl]", sizeof(sym)); 784260e9a87SYuri Pankov break; 785260e9a87SYuri Pankov case (EQN_TOK_DOT): 786260e9a87SYuri Pankov strlcpy(sym, "\\[a.]", sizeof(sym)); 787260e9a87SYuri Pankov break; 788260e9a87SYuri Pankov case (EQN_TOK_HAT): 789260e9a87SYuri Pankov strlcpy(sym, "\\[ha]", sizeof(sym)); 790260e9a87SYuri Pankov break; 791260e9a87SYuri Pankov default: 792260e9a87SYuri Pankov abort(); 79395c635efSGarrett D'Amore } 79495c635efSGarrett D'Amore 795260e9a87SYuri Pankov switch (tok) { 796260e9a87SYuri Pankov case (EQN_TOK_DOTDOT): 797260e9a87SYuri Pankov case (EQN_TOK_VEC): 798260e9a87SYuri Pankov case (EQN_TOK_DYAD): 799260e9a87SYuri Pankov case (EQN_TOK_TILDE): 800260e9a87SYuri Pankov case (EQN_TOK_BAR): 801260e9a87SYuri Pankov case (EQN_TOK_DOT): 802260e9a87SYuri Pankov case (EQN_TOK_HAT): 803260e9a87SYuri Pankov parent->top = mandoc_strdup(sym); 804260e9a87SYuri Pankov break; 805260e9a87SYuri Pankov case (EQN_TOK_UNDER): 806260e9a87SYuri Pankov parent->bottom = mandoc_strdup(sym); 807260e9a87SYuri Pankov break; 808260e9a87SYuri Pankov default: 809260e9a87SYuri Pankov abort(); 810260e9a87SYuri Pankov } 811260e9a87SYuri Pankov parent = parent->parent; 812260e9a87SYuri Pankov break; 813260e9a87SYuri Pankov case (EQN_TOK_FWD): 814260e9a87SYuri Pankov case (EQN_TOK_BACK): 815260e9a87SYuri Pankov case (EQN_TOK_DOWN): 816260e9a87SYuri Pankov case (EQN_TOK_UP): 817260e9a87SYuri Pankov subtok = eqn_tok_parse(ep, NULL); 818260e9a87SYuri Pankov if (subtok != EQN_TOK__MAX) { 819260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 820260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 821260e9a87SYuri Pankov tok = subtok; 822260e9a87SYuri Pankov goto this_tok; 823260e9a87SYuri Pankov } 824260e9a87SYuri Pankov break; 825260e9a87SYuri Pankov case (EQN_TOK_FAT): 826260e9a87SYuri Pankov case (EQN_TOK_ROMAN): 827260e9a87SYuri Pankov case (EQN_TOK_ITALIC): 828260e9a87SYuri Pankov case (EQN_TOK_BOLD): 829260e9a87SYuri Pankov while (parent->args == parent->expectargs) 830260e9a87SYuri Pankov parent = parent->parent; 831260e9a87SYuri Pankov /* 832260e9a87SYuri Pankov * These values apply to the next word or sequence of 833260e9a87SYuri Pankov * words; thus, we mark that we'll have a child with 834260e9a87SYuri Pankov * exactly one of those. 835260e9a87SYuri Pankov */ 836260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 837260e9a87SYuri Pankov parent->type = EQN_LISTONE; 838260e9a87SYuri Pankov parent->expectargs = 1; 839260e9a87SYuri Pankov switch (tok) { 840260e9a87SYuri Pankov case (EQN_TOK_FAT): 841260e9a87SYuri Pankov parent->font = EQNFONT_FAT; 842260e9a87SYuri Pankov break; 843260e9a87SYuri Pankov case (EQN_TOK_ROMAN): 844260e9a87SYuri Pankov parent->font = EQNFONT_ROMAN; 845260e9a87SYuri Pankov break; 846260e9a87SYuri Pankov case (EQN_TOK_ITALIC): 847260e9a87SYuri Pankov parent->font = EQNFONT_ITALIC; 848260e9a87SYuri Pankov break; 849260e9a87SYuri Pankov case (EQN_TOK_BOLD): 850260e9a87SYuri Pankov parent->font = EQNFONT_BOLD; 851260e9a87SYuri Pankov break; 852260e9a87SYuri Pankov default: 853260e9a87SYuri Pankov abort(); 854260e9a87SYuri Pankov } 855260e9a87SYuri Pankov break; 856260e9a87SYuri Pankov case (EQN_TOK_SIZE): 857260e9a87SYuri Pankov case (EQN_TOK_GSIZE): 858260e9a87SYuri Pankov /* Accept two values: integral size and a single. */ 859260e9a87SYuri Pankov if (NULL == (start = eqn_nexttok(ep, &sz))) { 860260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 861260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 862260e9a87SYuri Pankov break; 863260e9a87SYuri Pankov } 864260e9a87SYuri Pankov size = mandoc_strntoi(start, sz, 10); 865260e9a87SYuri Pankov if (-1 == size) { 866260e9a87SYuri Pankov mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, 867260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 868260e9a87SYuri Pankov break; 869260e9a87SYuri Pankov } 870260e9a87SYuri Pankov if (EQN_TOK_GSIZE == tok) { 871260e9a87SYuri Pankov ep->gsize = size; 872260e9a87SYuri Pankov break; 873260e9a87SYuri Pankov } 874260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 875260e9a87SYuri Pankov parent->type = EQN_LISTONE; 876260e9a87SYuri Pankov parent->expectargs = 1; 877260e9a87SYuri Pankov parent->size = size; 878260e9a87SYuri Pankov break; 879260e9a87SYuri Pankov case (EQN_TOK_FROM): 880260e9a87SYuri Pankov case (EQN_TOK_TO): 881260e9a87SYuri Pankov case (EQN_TOK_SUB): 882260e9a87SYuri Pankov case (EQN_TOK_SUP): 883260e9a87SYuri Pankov /* 884260e9a87SYuri Pankov * We have a left-right-associative expression. 885260e9a87SYuri Pankov * Repivot under a positional node, open a child scope 886260e9a87SYuri Pankov * and keep on reading. 887260e9a87SYuri Pankov */ 888260e9a87SYuri Pankov if (parent->last == NULL) { 889260e9a87SYuri Pankov mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, 890260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 891260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 892260e9a87SYuri Pankov cur->type = EQN_TEXT; 893260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 894260e9a87SYuri Pankov } 895260e9a87SYuri Pankov /* Handle the "subsup" and "fromto" positions. */ 896260e9a87SYuri Pankov if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) { 897260e9a87SYuri Pankov parent->expectargs = 3; 898260e9a87SYuri Pankov parent->pos = EQNPOS_SUBSUP; 899260e9a87SYuri Pankov break; 900260e9a87SYuri Pankov } 901260e9a87SYuri Pankov if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) { 902260e9a87SYuri Pankov parent->expectargs = 3; 903260e9a87SYuri Pankov parent->pos = EQNPOS_FROMTO; 904260e9a87SYuri Pankov break; 905260e9a87SYuri Pankov } 906260e9a87SYuri Pankov switch (tok) { 907260e9a87SYuri Pankov case (EQN_TOK_FROM): 908260e9a87SYuri Pankov pos = EQNPOS_FROM; 909260e9a87SYuri Pankov break; 910260e9a87SYuri Pankov case (EQN_TOK_TO): 911260e9a87SYuri Pankov pos = EQNPOS_TO; 912260e9a87SYuri Pankov break; 913260e9a87SYuri Pankov case (EQN_TOK_SUP): 914260e9a87SYuri Pankov pos = EQNPOS_SUP; 915260e9a87SYuri Pankov break; 916260e9a87SYuri Pankov case (EQN_TOK_SUB): 917260e9a87SYuri Pankov pos = EQNPOS_SUB; 918260e9a87SYuri Pankov break; 919260e9a87SYuri Pankov default: 920260e9a87SYuri Pankov abort(); 921260e9a87SYuri Pankov } 922260e9a87SYuri Pankov parent = eqn_box_makebinary(ep, pos, parent); 923260e9a87SYuri Pankov break; 924260e9a87SYuri Pankov case (EQN_TOK_SQRT): 925260e9a87SYuri Pankov while (parent->args == parent->expectargs) 926260e9a87SYuri Pankov parent = parent->parent; 927260e9a87SYuri Pankov /* 928260e9a87SYuri Pankov * Accept a left-right-associative set of arguments just 929260e9a87SYuri Pankov * like sub and sup and friends but without rebalancing 930260e9a87SYuri Pankov * under a pivot. 931260e9a87SYuri Pankov */ 932260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 933260e9a87SYuri Pankov parent->type = EQN_SUBEXPR; 934260e9a87SYuri Pankov parent->pos = EQNPOS_SQRT; 935260e9a87SYuri Pankov parent->expectargs = 1; 936260e9a87SYuri Pankov break; 937260e9a87SYuri Pankov case (EQN_TOK_OVER): 938260e9a87SYuri Pankov /* 939260e9a87SYuri Pankov * We have a right-left-associative fraction. 940260e9a87SYuri Pankov * Close out anything that's currently open, then 941260e9a87SYuri Pankov * rebalance and continue reading. 942260e9a87SYuri Pankov */ 943260e9a87SYuri Pankov if (parent->last == NULL) { 944260e9a87SYuri Pankov mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, 945260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 946260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 947260e9a87SYuri Pankov cur->type = EQN_TEXT; 948260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 949260e9a87SYuri Pankov } 950260e9a87SYuri Pankov while (EQN_SUBEXPR == parent->type) 951260e9a87SYuri Pankov parent = parent->parent; 952260e9a87SYuri Pankov parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent); 953260e9a87SYuri Pankov break; 954260e9a87SYuri Pankov case (EQN_TOK_RIGHT): 955260e9a87SYuri Pankov case (EQN_TOK_BRACE_CLOSE): 956260e9a87SYuri Pankov /* 957260e9a87SYuri Pankov * Close out the existing brace. 958260e9a87SYuri Pankov * FIXME: this is a shitty sentinel: we should really 959260e9a87SYuri Pankov * have a native EQN_BRACE type or whatnot. 960260e9a87SYuri Pankov */ 961260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 962260e9a87SYuri Pankov if (cur->type == EQN_LIST && 963260e9a87SYuri Pankov (tok == EQN_TOK_BRACE_CLOSE || 964260e9a87SYuri Pankov cur->left != NULL)) 965260e9a87SYuri Pankov break; 966260e9a87SYuri Pankov if (cur == NULL) { 967260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, 968260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 969260e9a87SYuri Pankov break; 970260e9a87SYuri Pankov } 971260e9a87SYuri Pankov parent = cur; 972260e9a87SYuri Pankov if (EQN_TOK_RIGHT == tok) { 973260e9a87SYuri Pankov if (NULL == (start = eqn_nexttok(ep, &sz))) { 974260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, 975260e9a87SYuri Pankov ep->parse, ep->eqn.ln, 976260e9a87SYuri Pankov ep->eqn.pos, eqn_toks[tok]); 977260e9a87SYuri Pankov break; 978260e9a87SYuri Pankov } 979260e9a87SYuri Pankov /* Handling depends on right/left. */ 980260e9a87SYuri Pankov if (STRNEQ(start, sz, "ceiling", 7)) { 981260e9a87SYuri Pankov strlcpy(sym, "\\[rc]", sizeof(sym)); 982260e9a87SYuri Pankov parent->right = mandoc_strdup(sym); 983260e9a87SYuri Pankov } else if (STRNEQ(start, sz, "floor", 5)) { 984260e9a87SYuri Pankov strlcpy(sym, "\\[rf]", sizeof(sym)); 985260e9a87SYuri Pankov parent->right = mandoc_strdup(sym); 986260e9a87SYuri Pankov } else 987260e9a87SYuri Pankov parent->right = mandoc_strndup(start, sz); 988260e9a87SYuri Pankov } 989260e9a87SYuri Pankov parent = parent->parent; 990*371584c2SYuri Pankov if (tok == EQN_TOK_BRACE_CLOSE && 991260e9a87SYuri Pankov (parent->type == EQN_PILE || 992260e9a87SYuri Pankov parent->type == EQN_MATRIX)) 993260e9a87SYuri Pankov parent = parent->parent; 994260e9a87SYuri Pankov /* Close out any "singleton" lists. */ 995260e9a87SYuri Pankov while (parent->type == EQN_LISTONE && 996260e9a87SYuri Pankov parent->args == parent->expectargs) 997260e9a87SYuri Pankov parent = parent->parent; 998260e9a87SYuri Pankov break; 999260e9a87SYuri Pankov case (EQN_TOK_BRACE_OPEN): 1000260e9a87SYuri Pankov case (EQN_TOK_LEFT): 1001260e9a87SYuri Pankov /* 1002260e9a87SYuri Pankov * If we already have something in the stack and we're 1003260e9a87SYuri Pankov * in an expression, then rewind til we're not any more 1004260e9a87SYuri Pankov * (just like with the text node). 1005260e9a87SYuri Pankov */ 1006260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1007260e9a87SYuri Pankov parent = parent->parent; 1008260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok && 1009260e9a87SYuri Pankov (start = eqn_nexttok(ep, &sz)) == NULL) { 1010260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 1011260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 1012260e9a87SYuri Pankov break; 1013260e9a87SYuri Pankov } 1014260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 1015260e9a87SYuri Pankov parent->type = EQN_LIST; 1016260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok) { 1017260e9a87SYuri Pankov if (STRNEQ(start, sz, "ceiling", 7)) { 1018260e9a87SYuri Pankov strlcpy(sym, "\\[lc]", sizeof(sym)); 1019260e9a87SYuri Pankov parent->left = mandoc_strdup(sym); 1020260e9a87SYuri Pankov } else if (STRNEQ(start, sz, "floor", 5)) { 1021260e9a87SYuri Pankov strlcpy(sym, "\\[lf]", sizeof(sym)); 1022260e9a87SYuri Pankov parent->left = mandoc_strdup(sym); 1023260e9a87SYuri Pankov } else 1024260e9a87SYuri Pankov parent->left = mandoc_strndup(start, sz); 1025260e9a87SYuri Pankov } 1026260e9a87SYuri Pankov break; 1027260e9a87SYuri Pankov case (EQN_TOK_PILE): 1028260e9a87SYuri Pankov case (EQN_TOK_LPILE): 1029260e9a87SYuri Pankov case (EQN_TOK_RPILE): 1030260e9a87SYuri Pankov case (EQN_TOK_CPILE): 1031260e9a87SYuri Pankov case (EQN_TOK_CCOL): 1032260e9a87SYuri Pankov case (EQN_TOK_LCOL): 1033260e9a87SYuri Pankov case (EQN_TOK_RCOL): 1034260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1035260e9a87SYuri Pankov parent = parent->parent; 1036260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 1037260e9a87SYuri Pankov parent->type = EQN_PILE; 1038260e9a87SYuri Pankov parent->expectargs = 1; 1039260e9a87SYuri Pankov break; 1040260e9a87SYuri Pankov case (EQN_TOK_ABOVE): 1041260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 1042260e9a87SYuri Pankov if (cur->type == EQN_PILE) 1043260e9a87SYuri Pankov break; 1044260e9a87SYuri Pankov if (cur == NULL) { 1045260e9a87SYuri Pankov mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, 1046260e9a87SYuri Pankov ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); 1047260e9a87SYuri Pankov break; 1048260e9a87SYuri Pankov } 1049260e9a87SYuri Pankov parent = eqn_box_alloc(ep, cur); 1050260e9a87SYuri Pankov parent->type = EQN_LIST; 1051260e9a87SYuri Pankov break; 1052260e9a87SYuri Pankov case (EQN_TOK_MATRIX): 1053260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1054260e9a87SYuri Pankov parent = parent->parent; 1055260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 1056260e9a87SYuri Pankov parent->type = EQN_MATRIX; 1057260e9a87SYuri Pankov parent->expectargs = 1; 1058260e9a87SYuri Pankov break; 1059260e9a87SYuri Pankov case (EQN_TOK_EOF): 1060260e9a87SYuri Pankov /* 1061260e9a87SYuri Pankov * End of file! 1062260e9a87SYuri Pankov * TODO: make sure we're not in an open subexpression. 1063260e9a87SYuri Pankov */ 1064*371584c2SYuri Pankov return ROFF_EQN; 1065260e9a87SYuri Pankov default: 1066260e9a87SYuri Pankov assert(tok == EQN_TOK__MAX); 1067260e9a87SYuri Pankov assert(NULL != p); 1068260e9a87SYuri Pankov /* 1069260e9a87SYuri Pankov * If we already have something in the stack and we're 1070260e9a87SYuri Pankov * in an expression, then rewind til we're not any more. 1071260e9a87SYuri Pankov */ 1072260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1073260e9a87SYuri Pankov parent = parent->parent; 1074260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 1075260e9a87SYuri Pankov cur->type = EQN_TEXT; 1076260e9a87SYuri Pankov for (i = 0; i < EQNSYM__MAX; i++) 1077260e9a87SYuri Pankov if (0 == strcmp(eqnsyms[i].str, p)) { 1078260e9a87SYuri Pankov (void)snprintf(sym, sizeof(sym), 1079260e9a87SYuri Pankov "\\[%s]", eqnsyms[i].sym); 1080260e9a87SYuri Pankov cur->text = mandoc_strdup(sym); 1081260e9a87SYuri Pankov free(p); 1082260e9a87SYuri Pankov break; 1083260e9a87SYuri Pankov } 1084260e9a87SYuri Pankov 1085260e9a87SYuri Pankov if (i == EQNSYM__MAX) 1086260e9a87SYuri Pankov cur->text = p; 1087260e9a87SYuri Pankov /* 1088260e9a87SYuri Pankov * Post-process list status. 1089260e9a87SYuri Pankov */ 1090260e9a87SYuri Pankov while (parent->type == EQN_LISTONE && 1091260e9a87SYuri Pankov parent->args == parent->expectargs) 1092260e9a87SYuri Pankov parent = parent->parent; 1093260e9a87SYuri Pankov break; 1094260e9a87SYuri Pankov } 1095260e9a87SYuri Pankov goto next_tok; 1096260e9a87SYuri Pankov } 1097260e9a87SYuri Pankov 1098260e9a87SYuri Pankov enum rofferr 1099260e9a87SYuri Pankov eqn_end(struct eqn_node **epp) 110095c635efSGarrett D'Amore { 1101260e9a87SYuri Pankov struct eqn_node *ep; 110295c635efSGarrett D'Amore 1103260e9a87SYuri Pankov ep = *epp; 1104260e9a87SYuri Pankov *epp = NULL; 110595c635efSGarrett D'Amore 1106260e9a87SYuri Pankov ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); 1107260e9a87SYuri Pankov ep->eqn.root->expectargs = UINT_MAX; 1108*371584c2SYuri Pankov return eqn_parse(ep, ep->eqn.root); 110995c635efSGarrett D'Amore } 111095c635efSGarrett D'Amore 1111260e9a87SYuri Pankov void 1112260e9a87SYuri Pankov eqn_free(struct eqn_node *p) 111395c635efSGarrett D'Amore { 111495c635efSGarrett D'Amore int i; 111595c635efSGarrett D'Amore 1116260e9a87SYuri Pankov eqn_box_free(p->eqn.root); 111795c635efSGarrett D'Amore 1118260e9a87SYuri Pankov for (i = 0; i < (int)p->defsz; i++) { 1119260e9a87SYuri Pankov free(p->defs[i].key); 1120260e9a87SYuri Pankov free(p->defs[i].val); 1121260e9a87SYuri Pankov } 1122260e9a87SYuri Pankov 1123260e9a87SYuri Pankov free(p->data); 1124260e9a87SYuri Pankov free(p->defs); 1125260e9a87SYuri Pankov free(p); 112695c635efSGarrett D'Amore } 1127