1*cec8643bSMichal Nowak /* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 3260e9a87SYuri Pankov * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4*cec8643bSMichal Nowak * Copyright (c) 2014, 2015, 2017, 2018 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> 23c66b8046SYuri Pankov #include <ctype.h> 2495c635efSGarrett D'Amore #include <limits.h> 2595c635efSGarrett D'Amore #include <stdio.h> 2695c635efSGarrett D'Amore #include <stdlib.h> 2795c635efSGarrett D'Amore #include <string.h> 2895c635efSGarrett D'Amore #include <time.h> 2995c635efSGarrett D'Amore 30260e9a87SYuri Pankov #include "mandoc_aux.h" 31c66b8046SYuri Pankov #include "mandoc.h" 32c66b8046SYuri Pankov #include "roff.h" 33*cec8643bSMichal Nowak #include "eqn.h" 3495c635efSGarrett D'Amore #include "libmandoc.h" 35*cec8643bSMichal Nowak #include "eqn_parse.h" 3695c635efSGarrett D'Amore 3795c635efSGarrett D'Amore #define EQN_NEST_MAX 128 /* maximum nesting of defines */ 38260e9a87SYuri Pankov #define STRNEQ(p1, sz1, p2, sz2) \ 39260e9a87SYuri Pankov ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) 4095c635efSGarrett D'Amore 41260e9a87SYuri Pankov enum eqn_tok { 42260e9a87SYuri Pankov EQN_TOK_DYAD = 0, 43260e9a87SYuri Pankov EQN_TOK_VEC, 44260e9a87SYuri Pankov EQN_TOK_UNDER, 45260e9a87SYuri Pankov EQN_TOK_BAR, 46260e9a87SYuri Pankov EQN_TOK_TILDE, 47260e9a87SYuri Pankov EQN_TOK_HAT, 48260e9a87SYuri Pankov EQN_TOK_DOT, 49260e9a87SYuri Pankov EQN_TOK_DOTDOT, 50260e9a87SYuri Pankov EQN_TOK_FWD, 51260e9a87SYuri Pankov EQN_TOK_BACK, 52260e9a87SYuri Pankov EQN_TOK_DOWN, 53260e9a87SYuri Pankov EQN_TOK_UP, 54260e9a87SYuri Pankov EQN_TOK_FAT, 55260e9a87SYuri Pankov EQN_TOK_ROMAN, 56260e9a87SYuri Pankov EQN_TOK_ITALIC, 57260e9a87SYuri Pankov EQN_TOK_BOLD, 58260e9a87SYuri Pankov EQN_TOK_SIZE, 59260e9a87SYuri Pankov EQN_TOK_SUB, 60260e9a87SYuri Pankov EQN_TOK_SUP, 61260e9a87SYuri Pankov EQN_TOK_SQRT, 62260e9a87SYuri Pankov EQN_TOK_OVER, 63260e9a87SYuri Pankov EQN_TOK_FROM, 64260e9a87SYuri Pankov EQN_TOK_TO, 65260e9a87SYuri Pankov EQN_TOK_BRACE_OPEN, 66260e9a87SYuri Pankov EQN_TOK_BRACE_CLOSE, 67260e9a87SYuri Pankov EQN_TOK_GSIZE, 68260e9a87SYuri Pankov EQN_TOK_GFONT, 69260e9a87SYuri Pankov EQN_TOK_MARK, 70260e9a87SYuri Pankov EQN_TOK_LINEUP, 71260e9a87SYuri Pankov EQN_TOK_LEFT, 72260e9a87SYuri Pankov EQN_TOK_RIGHT, 73260e9a87SYuri Pankov EQN_TOK_PILE, 74260e9a87SYuri Pankov EQN_TOK_LPILE, 75260e9a87SYuri Pankov EQN_TOK_RPILE, 76260e9a87SYuri Pankov EQN_TOK_CPILE, 77260e9a87SYuri Pankov EQN_TOK_MATRIX, 78260e9a87SYuri Pankov EQN_TOK_CCOL, 79260e9a87SYuri Pankov EQN_TOK_LCOL, 80260e9a87SYuri Pankov EQN_TOK_RCOL, 81260e9a87SYuri Pankov EQN_TOK_DELIM, 82260e9a87SYuri Pankov EQN_TOK_DEFINE, 83260e9a87SYuri Pankov EQN_TOK_TDEFINE, 84260e9a87SYuri Pankov EQN_TOK_NDEFINE, 85260e9a87SYuri Pankov EQN_TOK_UNDEF, 86260e9a87SYuri Pankov EQN_TOK_ABOVE, 87c66b8046SYuri Pankov EQN_TOK__MAX, 88c66b8046SYuri Pankov EQN_TOK_FUNC, 89c66b8046SYuri Pankov EQN_TOK_QUOTED, 90c66b8046SYuri Pankov EQN_TOK_SYM, 91c66b8046SYuri Pankov EQN_TOK_EOF 92260e9a87SYuri Pankov }; 93260e9a87SYuri Pankov 94260e9a87SYuri Pankov static const char *eqn_toks[EQN_TOK__MAX] = { 95260e9a87SYuri Pankov "dyad", /* EQN_TOK_DYAD */ 96260e9a87SYuri Pankov "vec", /* EQN_TOK_VEC */ 97260e9a87SYuri Pankov "under", /* EQN_TOK_UNDER */ 98260e9a87SYuri Pankov "bar", /* EQN_TOK_BAR */ 99260e9a87SYuri Pankov "tilde", /* EQN_TOK_TILDE */ 100260e9a87SYuri Pankov "hat", /* EQN_TOK_HAT */ 101260e9a87SYuri Pankov "dot", /* EQN_TOK_DOT */ 102260e9a87SYuri Pankov "dotdot", /* EQN_TOK_DOTDOT */ 103260e9a87SYuri Pankov "fwd", /* EQN_TOK_FWD * */ 104260e9a87SYuri Pankov "back", /* EQN_TOK_BACK */ 105260e9a87SYuri Pankov "down", /* EQN_TOK_DOWN */ 106260e9a87SYuri Pankov "up", /* EQN_TOK_UP */ 107260e9a87SYuri Pankov "fat", /* EQN_TOK_FAT */ 108260e9a87SYuri Pankov "roman", /* EQN_TOK_ROMAN */ 109260e9a87SYuri Pankov "italic", /* EQN_TOK_ITALIC */ 110260e9a87SYuri Pankov "bold", /* EQN_TOK_BOLD */ 111260e9a87SYuri Pankov "size", /* EQN_TOK_SIZE */ 112260e9a87SYuri Pankov "sub", /* EQN_TOK_SUB */ 113260e9a87SYuri Pankov "sup", /* EQN_TOK_SUP */ 114260e9a87SYuri Pankov "sqrt", /* EQN_TOK_SQRT */ 115260e9a87SYuri Pankov "over", /* EQN_TOK_OVER */ 116260e9a87SYuri Pankov "from", /* EQN_TOK_FROM */ 117260e9a87SYuri Pankov "to", /* EQN_TOK_TO */ 118260e9a87SYuri Pankov "{", /* EQN_TOK_BRACE_OPEN */ 119260e9a87SYuri Pankov "}", /* EQN_TOK_BRACE_CLOSE */ 120260e9a87SYuri Pankov "gsize", /* EQN_TOK_GSIZE */ 121260e9a87SYuri Pankov "gfont", /* EQN_TOK_GFONT */ 122260e9a87SYuri Pankov "mark", /* EQN_TOK_MARK */ 123260e9a87SYuri Pankov "lineup", /* EQN_TOK_LINEUP */ 124260e9a87SYuri Pankov "left", /* EQN_TOK_LEFT */ 125260e9a87SYuri Pankov "right", /* EQN_TOK_RIGHT */ 126260e9a87SYuri Pankov "pile", /* EQN_TOK_PILE */ 127260e9a87SYuri Pankov "lpile", /* EQN_TOK_LPILE */ 128260e9a87SYuri Pankov "rpile", /* EQN_TOK_RPILE */ 129260e9a87SYuri Pankov "cpile", /* EQN_TOK_CPILE */ 130260e9a87SYuri Pankov "matrix", /* EQN_TOK_MATRIX */ 131260e9a87SYuri Pankov "ccol", /* EQN_TOK_CCOL */ 132260e9a87SYuri Pankov "lcol", /* EQN_TOK_LCOL */ 133260e9a87SYuri Pankov "rcol", /* EQN_TOK_RCOL */ 134260e9a87SYuri Pankov "delim", /* EQN_TOK_DELIM */ 135260e9a87SYuri Pankov "define", /* EQN_TOK_DEFINE */ 136260e9a87SYuri Pankov "tdefine", /* EQN_TOK_TDEFINE */ 137260e9a87SYuri Pankov "ndefine", /* EQN_TOK_NDEFINE */ 138260e9a87SYuri Pankov "undef", /* EQN_TOK_UNDEF */ 139260e9a87SYuri Pankov "above", /* EQN_TOK_ABOVE */ 14095c635efSGarrett D'Amore }; 14195c635efSGarrett D'Amore 142c66b8046SYuri Pankov static const char *const eqn_func[] = { 143c66b8046SYuri Pankov "acos", "acsc", "and", "arc", "asec", "asin", "atan", 144c66b8046SYuri Pankov "cos", "cosh", "coth", "csc", "det", "exp", "for", 145c66b8046SYuri Pankov "if", "lim", "ln", "log", "max", "min", 146c66b8046SYuri Pankov "sec", "sin", "sinh", "tan", "tanh", "Im", "Re", 147c66b8046SYuri Pankov }; 148c66b8046SYuri Pankov 14995c635efSGarrett D'Amore enum eqn_symt { 150c66b8046SYuri Pankov EQNSYM_alpha = 0, 15195c635efSGarrett D'Amore EQNSYM_beta, 15295c635efSGarrett D'Amore EQNSYM_chi, 15395c635efSGarrett D'Amore EQNSYM_delta, 15495c635efSGarrett D'Amore EQNSYM_epsilon, 15595c635efSGarrett D'Amore EQNSYM_eta, 15695c635efSGarrett D'Amore EQNSYM_gamma, 15795c635efSGarrett D'Amore EQNSYM_iota, 15895c635efSGarrett D'Amore EQNSYM_kappa, 15995c635efSGarrett D'Amore EQNSYM_lambda, 16095c635efSGarrett D'Amore EQNSYM_mu, 16195c635efSGarrett D'Amore EQNSYM_nu, 16295c635efSGarrett D'Amore EQNSYM_omega, 16395c635efSGarrett D'Amore EQNSYM_omicron, 16495c635efSGarrett D'Amore EQNSYM_phi, 16595c635efSGarrett D'Amore EQNSYM_pi, 16695c635efSGarrett D'Amore EQNSYM_ps, 16795c635efSGarrett D'Amore EQNSYM_rho, 16895c635efSGarrett D'Amore EQNSYM_sigma, 16995c635efSGarrett D'Amore EQNSYM_tau, 17095c635efSGarrett D'Amore EQNSYM_theta, 17195c635efSGarrett D'Amore EQNSYM_upsilon, 17295c635efSGarrett D'Amore EQNSYM_xi, 17395c635efSGarrett D'Amore EQNSYM_zeta, 17495c635efSGarrett D'Amore EQNSYM_DELTA, 17595c635efSGarrett D'Amore EQNSYM_GAMMA, 17695c635efSGarrett D'Amore EQNSYM_LAMBDA, 17795c635efSGarrett D'Amore EQNSYM_OMEGA, 17895c635efSGarrett D'Amore EQNSYM_PHI, 17995c635efSGarrett D'Amore EQNSYM_PI, 18095c635efSGarrett D'Amore EQNSYM_PSI, 18195c635efSGarrett D'Amore EQNSYM_SIGMA, 18295c635efSGarrett D'Amore EQNSYM_THETA, 18395c635efSGarrett D'Amore EQNSYM_UPSILON, 18495c635efSGarrett D'Amore EQNSYM_XI, 18595c635efSGarrett D'Amore EQNSYM_inter, 18695c635efSGarrett D'Amore EQNSYM_union, 18795c635efSGarrett D'Amore EQNSYM_prod, 18895c635efSGarrett D'Amore EQNSYM_int, 18995c635efSGarrett D'Amore EQNSYM_sum, 19095c635efSGarrett D'Amore EQNSYM_grad, 19195c635efSGarrett D'Amore EQNSYM_del, 19295c635efSGarrett D'Amore EQNSYM_times, 19395c635efSGarrett D'Amore EQNSYM_cdot, 19495c635efSGarrett D'Amore EQNSYM_nothing, 19595c635efSGarrett D'Amore EQNSYM_approx, 19695c635efSGarrett D'Amore EQNSYM_prime, 19795c635efSGarrett D'Amore EQNSYM_half, 19895c635efSGarrett D'Amore EQNSYM_partial, 19995c635efSGarrett D'Amore EQNSYM_inf, 20095c635efSGarrett D'Amore EQNSYM_muchgreat, 20195c635efSGarrett D'Amore EQNSYM_muchless, 20295c635efSGarrett D'Amore EQNSYM_larrow, 20395c635efSGarrett D'Amore EQNSYM_rarrow, 20495c635efSGarrett D'Amore EQNSYM_pm, 20595c635efSGarrett D'Amore EQNSYM_nequal, 20695c635efSGarrett D'Amore EQNSYM_equiv, 20795c635efSGarrett D'Amore EQNSYM_lessequal, 20895c635efSGarrett D'Amore EQNSYM_moreequal, 209260e9a87SYuri Pankov EQNSYM_minus, 21095c635efSGarrett D'Amore EQNSYM__MAX 21195c635efSGarrett D'Amore }; 21295c635efSGarrett D'Amore 21395c635efSGarrett D'Amore struct eqnsym { 214260e9a87SYuri Pankov const char *str; 21595c635efSGarrett D'Amore const char *sym; 21695c635efSGarrett D'Amore }; 21795c635efSGarrett D'Amore 21895c635efSGarrett D'Amore static const struct eqnsym eqnsyms[EQNSYM__MAX] = { 219260e9a87SYuri Pankov { "alpha", "*a" }, /* EQNSYM_alpha */ 220260e9a87SYuri Pankov { "beta", "*b" }, /* EQNSYM_beta */ 221260e9a87SYuri Pankov { "chi", "*x" }, /* EQNSYM_chi */ 222260e9a87SYuri Pankov { "delta", "*d" }, /* EQNSYM_delta */ 223260e9a87SYuri Pankov { "epsilon", "*e" }, /* EQNSYM_epsilon */ 224260e9a87SYuri Pankov { "eta", "*y" }, /* EQNSYM_eta */ 225260e9a87SYuri Pankov { "gamma", "*g" }, /* EQNSYM_gamma */ 226260e9a87SYuri Pankov { "iota", "*i" }, /* EQNSYM_iota */ 227260e9a87SYuri Pankov { "kappa", "*k" }, /* EQNSYM_kappa */ 228260e9a87SYuri Pankov { "lambda", "*l" }, /* EQNSYM_lambda */ 229260e9a87SYuri Pankov { "mu", "*m" }, /* EQNSYM_mu */ 230260e9a87SYuri Pankov { "nu", "*n" }, /* EQNSYM_nu */ 231260e9a87SYuri Pankov { "omega", "*w" }, /* EQNSYM_omega */ 232260e9a87SYuri Pankov { "omicron", "*o" }, /* EQNSYM_omicron */ 233260e9a87SYuri Pankov { "phi", "*f" }, /* EQNSYM_phi */ 234260e9a87SYuri Pankov { "pi", "*p" }, /* EQNSYM_pi */ 235260e9a87SYuri Pankov { "psi", "*q" }, /* EQNSYM_psi */ 236260e9a87SYuri Pankov { "rho", "*r" }, /* EQNSYM_rho */ 237260e9a87SYuri Pankov { "sigma", "*s" }, /* EQNSYM_sigma */ 238260e9a87SYuri Pankov { "tau", "*t" }, /* EQNSYM_tau */ 239260e9a87SYuri Pankov { "theta", "*h" }, /* EQNSYM_theta */ 240260e9a87SYuri Pankov { "upsilon", "*u" }, /* EQNSYM_upsilon */ 241260e9a87SYuri Pankov { "xi", "*c" }, /* EQNSYM_xi */ 242260e9a87SYuri Pankov { "zeta", "*z" }, /* EQNSYM_zeta */ 243260e9a87SYuri Pankov { "DELTA", "*D" }, /* EQNSYM_DELTA */ 244260e9a87SYuri Pankov { "GAMMA", "*G" }, /* EQNSYM_GAMMA */ 245260e9a87SYuri Pankov { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */ 246260e9a87SYuri Pankov { "OMEGA", "*W" }, /* EQNSYM_OMEGA */ 247260e9a87SYuri Pankov { "PHI", "*F" }, /* EQNSYM_PHI */ 248260e9a87SYuri Pankov { "PI", "*P" }, /* EQNSYM_PI */ 249260e9a87SYuri Pankov { "PSI", "*Q" }, /* EQNSYM_PSI */ 250260e9a87SYuri Pankov { "SIGMA", "*S" }, /* EQNSYM_SIGMA */ 251260e9a87SYuri Pankov { "THETA", "*H" }, /* EQNSYM_THETA */ 252260e9a87SYuri Pankov { "UPSILON", "*U" }, /* EQNSYM_UPSILON */ 253260e9a87SYuri Pankov { "XI", "*C" }, /* EQNSYM_XI */ 254260e9a87SYuri Pankov { "inter", "ca" }, /* EQNSYM_inter */ 255260e9a87SYuri Pankov { "union", "cu" }, /* EQNSYM_union */ 256260e9a87SYuri Pankov { "prod", "product" }, /* EQNSYM_prod */ 257260e9a87SYuri Pankov { "int", "integral" }, /* EQNSYM_int */ 258260e9a87SYuri Pankov { "sum", "sum" }, /* EQNSYM_sum */ 259260e9a87SYuri Pankov { "grad", "gr" }, /* EQNSYM_grad */ 260260e9a87SYuri Pankov { "del", "gr" }, /* EQNSYM_del */ 261260e9a87SYuri Pankov { "times", "mu" }, /* EQNSYM_times */ 262260e9a87SYuri Pankov { "cdot", "pc" }, /* EQNSYM_cdot */ 263260e9a87SYuri Pankov { "nothing", "&" }, /* EQNSYM_nothing */ 264260e9a87SYuri Pankov { "approx", "~~" }, /* EQNSYM_approx */ 265260e9a87SYuri Pankov { "prime", "fm" }, /* EQNSYM_prime */ 266260e9a87SYuri Pankov { "half", "12" }, /* EQNSYM_half */ 267260e9a87SYuri Pankov { "partial", "pd" }, /* EQNSYM_partial */ 268260e9a87SYuri Pankov { "inf", "if" }, /* EQNSYM_inf */ 269260e9a87SYuri Pankov { ">>", ">>" }, /* EQNSYM_muchgreat */ 270260e9a87SYuri Pankov { "<<", "<<" }, /* EQNSYM_muchless */ 271260e9a87SYuri Pankov { "<-", "<-" }, /* EQNSYM_larrow */ 272260e9a87SYuri Pankov { "->", "->" }, /* EQNSYM_rarrow */ 273260e9a87SYuri Pankov { "+-", "+-" }, /* EQNSYM_pm */ 274260e9a87SYuri Pankov { "!=", "!=" }, /* EQNSYM_nequal */ 275260e9a87SYuri Pankov { "==", "==" }, /* EQNSYM_equiv */ 276260e9a87SYuri Pankov { "<=", "<=" }, /* EQNSYM_lessequal */ 277260e9a87SYuri Pankov { ">=", ">=" }, /* EQNSYM_moreequal */ 278260e9a87SYuri Pankov { "-", "mi" }, /* EQNSYM_minus */ 27995c635efSGarrett D'Amore }; 28095c635efSGarrett D'Amore 281c66b8046SYuri Pankov enum parse_mode { 282c66b8046SYuri Pankov MODE_QUOTED, 283c66b8046SYuri Pankov MODE_NOSUB, 284c66b8046SYuri Pankov MODE_SUB, 285c66b8046SYuri Pankov MODE_TOK 286c66b8046SYuri Pankov }; 287c66b8046SYuri Pankov 288*cec8643bSMichal Nowak struct eqn_def { 289*cec8643bSMichal Nowak char *key; 290*cec8643bSMichal Nowak size_t keysz; 291*cec8643bSMichal Nowak char *val; 292*cec8643bSMichal Nowak size_t valsz; 293*cec8643bSMichal Nowak }; 294*cec8643bSMichal Nowak 295260e9a87SYuri Pankov static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); 296260e9a87SYuri Pankov static struct eqn_box *eqn_box_makebinary(struct eqn_node *, 297c66b8046SYuri Pankov struct eqn_box *); 298260e9a87SYuri Pankov static void eqn_def(struct eqn_node *); 299c66b8046SYuri Pankov static struct eqn_def *eqn_def_find(struct eqn_node *); 300260e9a87SYuri Pankov static void eqn_delim(struct eqn_node *); 301c66b8046SYuri Pankov static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); 302260e9a87SYuri Pankov static void eqn_undef(struct eqn_node *); 303260e9a87SYuri Pankov 304260e9a87SYuri Pankov 30595c635efSGarrett D'Amore struct eqn_node * 306*cec8643bSMichal Nowak eqn_alloc(void) 30795c635efSGarrett D'Amore { 308c66b8046SYuri Pankov struct eqn_node *ep; 30995c635efSGarrett D'Amore 310c66b8046SYuri Pankov ep = mandoc_calloc(1, sizeof(*ep)); 311c66b8046SYuri Pankov ep->gsize = EQN_DEFSIZE; 312c66b8046SYuri Pankov return ep; 313c66b8046SYuri Pankov } 31495c635efSGarrett D'Amore 315c66b8046SYuri Pankov void 316c66b8046SYuri Pankov eqn_reset(struct eqn_node *ep) 317c66b8046SYuri Pankov { 318c66b8046SYuri Pankov free(ep->data); 319c66b8046SYuri Pankov ep->data = ep->start = ep->end = NULL; 320c66b8046SYuri Pankov ep->sz = ep->toksz = 0; 321c66b8046SYuri Pankov } 32295c635efSGarrett D'Amore 323c66b8046SYuri Pankov void 324c66b8046SYuri Pankov eqn_read(struct eqn_node *ep, const char *p) 325c66b8046SYuri Pankov { 326c66b8046SYuri Pankov char *cp; 327c66b8046SYuri Pankov 328c66b8046SYuri Pankov if (ep->data == NULL) { 329c66b8046SYuri Pankov ep->sz = strlen(p); 330c66b8046SYuri Pankov ep->data = mandoc_strdup(p); 331c66b8046SYuri Pankov } else { 332c66b8046SYuri Pankov ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p); 333c66b8046SYuri Pankov free(ep->data); 334c66b8046SYuri Pankov ep->data = cp; 335c66b8046SYuri Pankov } 336c66b8046SYuri Pankov ep->sz += 1; 33795c635efSGarrett D'Amore } 33895c635efSGarrett D'Amore 339260e9a87SYuri Pankov /* 340260e9a87SYuri Pankov * Find the key "key" of the give size within our eqn-defined values. 341260e9a87SYuri Pankov */ 342260e9a87SYuri Pankov static struct eqn_def * 343c66b8046SYuri Pankov eqn_def_find(struct eqn_node *ep) 34495c635efSGarrett D'Amore { 34595c635efSGarrett D'Amore int i; 34695c635efSGarrett D'Amore 347260e9a87SYuri Pankov for (i = 0; i < (int)ep->defsz; i++) 348260e9a87SYuri Pankov if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, 349c66b8046SYuri Pankov ep->defs[i].keysz, ep->start, ep->toksz)) 350371584c2SYuri Pankov return &ep->defs[i]; 35195c635efSGarrett D'Amore 352371584c2SYuri Pankov return NULL; 35395c635efSGarrett D'Amore } 35495c635efSGarrett D'Amore 355260e9a87SYuri Pankov /* 356c66b8046SYuri Pankov * Parse a token from the input text. The modes are: 357c66b8046SYuri Pankov * MODE_QUOTED: Use *ep->start as the delimiter; the token ends 358c66b8046SYuri Pankov * before its next occurence. Do not interpret the token in any 359c66b8046SYuri Pankov * way and return EQN_TOK_QUOTED. All other modes behave like 360c66b8046SYuri Pankov * MODE_QUOTED when *ep->start is '"'. 361c66b8046SYuri Pankov * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it; 362c66b8046SYuri Pankov * otherwise, it ends before the next whitespace or brace. 363c66b8046SYuri Pankov * Do not interpret the token and return EQN_TOK__MAX. 364c66b8046SYuri Pankov * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an 365c66b8046SYuri Pankov * alias created with define. If it is an alias, replace it with 366c66b8046SYuri Pankov * its string value and reparse. 367c66b8046SYuri Pankov * MODE_TOK: Like MODE_SUB, but also check the token against the list 368c66b8046SYuri Pankov * of tokens, and if there is a match, return that token. Otherwise, 369c66b8046SYuri Pankov * if the token matches a symbol, return EQN_TOK_SYM; if it matches 370c66b8046SYuri Pankov * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for 371c66b8046SYuri Pankov * a token match, *ep->start is set to an allocated string that the 372c66b8046SYuri Pankov * caller is expected to free. 373c66b8046SYuri Pankov * All modes skip whitespace following the end of the token. 374260e9a87SYuri Pankov */ 375260e9a87SYuri Pankov static enum eqn_tok 376c66b8046SYuri Pankov eqn_next(struct eqn_node *ep, enum parse_mode mode) 377260e9a87SYuri Pankov { 378c66b8046SYuri Pankov static int last_len, lim; 379260e9a87SYuri Pankov 380c66b8046SYuri Pankov struct eqn_def *def; 381c66b8046SYuri Pankov size_t start; 382c66b8046SYuri Pankov int diff, i, quoted; 383c66b8046SYuri Pankov enum eqn_tok tok; 384260e9a87SYuri Pankov 385c66b8046SYuri Pankov /* 386c66b8046SYuri Pankov * Reset the recursion counter after advancing 387c66b8046SYuri Pankov * beyond the end of the previous substitution. 388c66b8046SYuri Pankov */ 389c66b8046SYuri Pankov if (ep->end - ep->data >= last_len) 390c66b8046SYuri Pankov lim = 0; 391260e9a87SYuri Pankov 392c66b8046SYuri Pankov ep->start = ep->end; 393c66b8046SYuri Pankov quoted = mode == MODE_QUOTED; 394c66b8046SYuri Pankov for (;;) { 395c66b8046SYuri Pankov switch (*ep->start) { 396c66b8046SYuri Pankov case '\0': 397c66b8046SYuri Pankov ep->toksz = 0; 398371584c2SYuri Pankov return EQN_TOK_EOF; 399c66b8046SYuri Pankov case '"': 400c66b8046SYuri Pankov quoted = 1; 401c66b8046SYuri Pankov break; 402c66b8046SYuri Pankov default: 403c66b8046SYuri Pankov break; 404c66b8046SYuri Pankov } 405260e9a87SYuri Pankov if (quoted) { 406c66b8046SYuri Pankov ep->end = strchr(ep->start + 1, *ep->start); 407c66b8046SYuri Pankov ep->start++; /* Skip opening quote. */ 408c66b8046SYuri Pankov if (ep->end == NULL) { 409*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_QUOTE, 410c66b8046SYuri Pankov ep->node->line, ep->node->pos, NULL); 411c66b8046SYuri Pankov ep->end = strchr(ep->start, '\0'); 412c66b8046SYuri Pankov } 413c66b8046SYuri Pankov } else { 414c66b8046SYuri Pankov ep->end = ep->start + 1; 415c66b8046SYuri Pankov if (*ep->start != '{' && *ep->start != '}') 416c66b8046SYuri Pankov ep->end += strcspn(ep->end, " ^~\"{}\t"); 417c66b8046SYuri Pankov } 418c66b8046SYuri Pankov ep->toksz = ep->end - ep->start; 419c66b8046SYuri Pankov if (quoted && *ep->end != '\0') 420c66b8046SYuri Pankov ep->end++; /* Skip closing quote. */ 421c66b8046SYuri Pankov while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL) 422c66b8046SYuri Pankov ep->end++; 423c66b8046SYuri Pankov if (quoted) /* Cannot return, may have to strndup. */ 424c66b8046SYuri Pankov break; 425c66b8046SYuri Pankov if (mode == MODE_NOSUB) 426c66b8046SYuri Pankov return EQN_TOK__MAX; 427c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) 428c66b8046SYuri Pankov break; 429c66b8046SYuri Pankov if (++lim > EQN_NEST_MAX) { 430*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ROFFLOOP, 431c66b8046SYuri Pankov ep->node->line, ep->node->pos, NULL); 432c66b8046SYuri Pankov return EQN_TOK_EOF; 433c66b8046SYuri Pankov } 434c66b8046SYuri Pankov 435c66b8046SYuri Pankov /* Replace a defined name with its string value. */ 436c66b8046SYuri Pankov if ((diff = def->valsz - ep->toksz) > 0) { 437c66b8046SYuri Pankov start = ep->start - ep->data; 438c66b8046SYuri Pankov ep->sz += diff; 439c66b8046SYuri Pankov ep->data = mandoc_realloc(ep->data, ep->sz + 1); 440c66b8046SYuri Pankov ep->start = ep->data + start; 441c66b8046SYuri Pankov } 442c66b8046SYuri Pankov if (diff) 443c66b8046SYuri Pankov memmove(ep->start + def->valsz, ep->start + ep->toksz, 444c66b8046SYuri Pankov strlen(ep->start + ep->toksz) + 1); 445c66b8046SYuri Pankov memcpy(ep->start, def->val, def->valsz); 446c66b8046SYuri Pankov last_len = ep->start - ep->data + def->valsz; 447c66b8046SYuri Pankov } 448c66b8046SYuri Pankov if (mode != MODE_TOK) 449c66b8046SYuri Pankov return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX; 450c66b8046SYuri Pankov if (quoted) { 451c66b8046SYuri Pankov ep->start = mandoc_strndup(ep->start, ep->toksz); 452c66b8046SYuri Pankov return EQN_TOK_QUOTED; 453c66b8046SYuri Pankov } 454c66b8046SYuri Pankov for (tok = 0; tok < EQN_TOK__MAX; tok++) 455c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, 456c66b8046SYuri Pankov eqn_toks[tok], strlen(eqn_toks[tok]))) 457c66b8046SYuri Pankov return tok; 458c66b8046SYuri Pankov 459c66b8046SYuri Pankov for (i = 0; i < EQNSYM__MAX; i++) { 460c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, 461c66b8046SYuri Pankov eqnsyms[i].str, strlen(eqnsyms[i].str))) { 462c66b8046SYuri Pankov mandoc_asprintf(&ep->start, 463c66b8046SYuri Pankov "\\[%s]", eqnsyms[i].sym); 464c66b8046SYuri Pankov return EQN_TOK_SYM; 465c66b8046SYuri Pankov } 466c66b8046SYuri Pankov } 467c66b8046SYuri Pankov ep->start = mandoc_strndup(ep->start, ep->toksz); 468c66b8046SYuri Pankov for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++) 469c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, 470c66b8046SYuri Pankov eqn_func[i], strlen(eqn_func[i]))) 471c66b8046SYuri Pankov return EQN_TOK_FUNC; 472371584c2SYuri Pankov return EQN_TOK__MAX; 473260e9a87SYuri Pankov } 474260e9a87SYuri Pankov 475c66b8046SYuri Pankov void 476260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp) 47795c635efSGarrett D'Amore { 478*cec8643bSMichal Nowak if (bp == NULL) 479*cec8643bSMichal Nowak return; 48095c635efSGarrett D'Amore 481260e9a87SYuri Pankov if (bp->first) 482260e9a87SYuri Pankov eqn_box_free(bp->first); 483260e9a87SYuri Pankov if (bp->next) 484260e9a87SYuri Pankov eqn_box_free(bp->next); 48595c635efSGarrett D'Amore 486260e9a87SYuri Pankov free(bp->text); 487260e9a87SYuri Pankov free(bp->left); 488260e9a87SYuri Pankov free(bp->right); 489260e9a87SYuri Pankov free(bp->top); 490260e9a87SYuri Pankov free(bp->bottom); 491260e9a87SYuri Pankov free(bp); 49295c635efSGarrett D'Amore } 49395c635efSGarrett D'Amore 494*cec8643bSMichal Nowak struct eqn_box * 495*cec8643bSMichal Nowak eqn_box_new(void) 496*cec8643bSMichal Nowak { 497*cec8643bSMichal Nowak struct eqn_box *bp; 498*cec8643bSMichal Nowak 499*cec8643bSMichal Nowak bp = mandoc_calloc(1, sizeof(*bp)); 500*cec8643bSMichal Nowak bp->expectargs = UINT_MAX; 501*cec8643bSMichal Nowak return bp; 502*cec8643bSMichal Nowak } 503*cec8643bSMichal Nowak 504260e9a87SYuri Pankov /* 505260e9a87SYuri Pankov * Allocate a box as the last child of the parent node. 506260e9a87SYuri Pankov */ 507260e9a87SYuri Pankov static struct eqn_box * 508260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) 509260e9a87SYuri Pankov { 510260e9a87SYuri Pankov struct eqn_box *bp; 511260e9a87SYuri Pankov 512*cec8643bSMichal Nowak bp = eqn_box_new(); 513260e9a87SYuri Pankov bp->parent = parent; 514260e9a87SYuri Pankov bp->parent->args++; 515c66b8046SYuri Pankov bp->font = bp->parent->font; 516260e9a87SYuri Pankov bp->size = ep->gsize; 517260e9a87SYuri Pankov 518260e9a87SYuri Pankov if (NULL != parent->first) { 519260e9a87SYuri Pankov parent->last->next = bp; 520260e9a87SYuri Pankov bp->prev = parent->last; 521260e9a87SYuri Pankov } else 522260e9a87SYuri Pankov parent->first = bp; 523260e9a87SYuri Pankov 524260e9a87SYuri Pankov parent->last = bp; 525371584c2SYuri Pankov return bp; 526260e9a87SYuri Pankov } 527260e9a87SYuri Pankov 528260e9a87SYuri Pankov /* 529260e9a87SYuri Pankov * Reparent the current last node (of the current parent) under a new 530260e9a87SYuri Pankov * EQN_SUBEXPR as the first element. 531260e9a87SYuri Pankov * Then return the new parent. 532260e9a87SYuri Pankov * The new EQN_SUBEXPR will have a two-child limit. 533260e9a87SYuri Pankov */ 534260e9a87SYuri Pankov static struct eqn_box * 535c66b8046SYuri Pankov eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent) 536260e9a87SYuri Pankov { 537260e9a87SYuri Pankov struct eqn_box *b, *newb; 538260e9a87SYuri Pankov 539260e9a87SYuri Pankov assert(NULL != parent->last); 540260e9a87SYuri Pankov b = parent->last; 541260e9a87SYuri Pankov if (parent->last == parent->first) 542260e9a87SYuri Pankov parent->first = NULL; 543260e9a87SYuri Pankov parent->args--; 544260e9a87SYuri Pankov parent->last = b->prev; 545260e9a87SYuri Pankov b->prev = NULL; 546260e9a87SYuri Pankov newb = eqn_box_alloc(ep, parent); 547260e9a87SYuri Pankov newb->type = EQN_SUBEXPR; 548260e9a87SYuri Pankov newb->expectargs = 2; 549260e9a87SYuri Pankov newb->args = 1; 550260e9a87SYuri Pankov newb->first = newb->last = b; 551260e9a87SYuri Pankov newb->first->next = NULL; 552260e9a87SYuri Pankov b->parent = newb; 553371584c2SYuri Pankov return newb; 554260e9a87SYuri Pankov } 555260e9a87SYuri Pankov 556260e9a87SYuri Pankov /* 557260e9a87SYuri Pankov * Parse the "delim" control statement. 558260e9a87SYuri Pankov */ 559260e9a87SYuri Pankov static void 560260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep) 561260e9a87SYuri Pankov { 562c66b8046SYuri Pankov if (ep->end[0] == '\0' || ep->end[1] == '\0') { 563*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, 564c66b8046SYuri Pankov ep->node->line, ep->node->pos, "delim"); 565c66b8046SYuri Pankov if (ep->end[0] != '\0') 566c66b8046SYuri Pankov ep->end++; 567c66b8046SYuri Pankov } else if (strncmp(ep->end, "off", 3) == 0) { 568260e9a87SYuri Pankov ep->delim = 0; 569c66b8046SYuri Pankov ep->end += 3; 570c66b8046SYuri Pankov } else if (strncmp(ep->end, "on", 2) == 0) { 571260e9a87SYuri Pankov if (ep->odelim && ep->cdelim) 572260e9a87SYuri Pankov ep->delim = 1; 573c66b8046SYuri Pankov ep->end += 2; 574c66b8046SYuri Pankov } else { 575c66b8046SYuri Pankov ep->odelim = *ep->end++; 576c66b8046SYuri Pankov ep->cdelim = *ep->end++; 577260e9a87SYuri Pankov ep->delim = 1; 578260e9a87SYuri Pankov } 579260e9a87SYuri Pankov } 580260e9a87SYuri Pankov 581260e9a87SYuri Pankov /* 582260e9a87SYuri Pankov * Undefine a previously-defined string. 583260e9a87SYuri Pankov */ 584260e9a87SYuri Pankov static void 585260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep) 586260e9a87SYuri Pankov { 587260e9a87SYuri Pankov struct eqn_def *def; 588260e9a87SYuri Pankov 589c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { 590*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, 591c66b8046SYuri Pankov ep->node->line, ep->node->pos, "undef"); 592260e9a87SYuri Pankov return; 593260e9a87SYuri Pankov } 594c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) 595260e9a87SYuri Pankov return; 596260e9a87SYuri Pankov free(def->key); 597260e9a87SYuri Pankov free(def->val); 598260e9a87SYuri Pankov def->key = def->val = NULL; 599260e9a87SYuri Pankov def->keysz = def->valsz = 0; 600260e9a87SYuri Pankov } 601260e9a87SYuri Pankov 602260e9a87SYuri Pankov static void 603260e9a87SYuri Pankov eqn_def(struct eqn_node *ep) 60495c635efSGarrett D'Amore { 60595c635efSGarrett D'Amore struct eqn_def *def; 60695c635efSGarrett D'Amore int i; 60795c635efSGarrett D'Amore 608c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { 609*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, 610c66b8046SYuri Pankov ep->node->line, ep->node->pos, "define"); 611260e9a87SYuri Pankov return; 61295c635efSGarrett D'Amore } 61395c635efSGarrett D'Amore 61495c635efSGarrett D'Amore /* 61595c635efSGarrett D'Amore * Search for a key that already exists. 61695c635efSGarrett D'Amore * Create a new key if none is found. 61795c635efSGarrett D'Amore */ 618c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) { 61995c635efSGarrett D'Amore /* Find holes in string array. */ 62095c635efSGarrett D'Amore for (i = 0; i < (int)ep->defsz; i++) 62195c635efSGarrett D'Amore if (0 == ep->defs[i].keysz) 62295c635efSGarrett D'Amore break; 62395c635efSGarrett D'Amore 62495c635efSGarrett D'Amore if (i == (int)ep->defsz) { 62595c635efSGarrett D'Amore ep->defsz++; 626260e9a87SYuri Pankov ep->defs = mandoc_reallocarray(ep->defs, 627260e9a87SYuri Pankov ep->defsz, sizeof(struct eqn_def)); 62895c635efSGarrett D'Amore ep->defs[i].key = ep->defs[i].val = NULL; 62995c635efSGarrett D'Amore } 63095c635efSGarrett D'Amore 631260e9a87SYuri Pankov def = ep->defs + i; 632260e9a87SYuri Pankov free(def->key); 633c66b8046SYuri Pankov def->key = mandoc_strndup(ep->start, ep->toksz); 634c66b8046SYuri Pankov def->keysz = ep->toksz; 63595c635efSGarrett D'Amore } 63695c635efSGarrett D'Amore 637c66b8046SYuri Pankov if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { 638*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, 639c66b8046SYuri Pankov ep->node->line, ep->node->pos, "define %s", def->key); 640260e9a87SYuri Pankov free(def->key); 641260e9a87SYuri Pankov free(def->val); 642260e9a87SYuri Pankov def->key = def->val = NULL; 643260e9a87SYuri Pankov def->keysz = def->valsz = 0; 644260e9a87SYuri Pankov return; 64595c635efSGarrett D'Amore } 646260e9a87SYuri Pankov free(def->val); 647c66b8046SYuri Pankov def->val = mandoc_strndup(ep->start, ep->toksz); 648c66b8046SYuri Pankov def->valsz = ep->toksz; 64995c635efSGarrett D'Amore } 65095c635efSGarrett D'Amore 651c66b8046SYuri Pankov void 652c66b8046SYuri Pankov eqn_parse(struct eqn_node *ep) 65395c635efSGarrett D'Amore { 654c66b8046SYuri Pankov struct eqn_box *cur, *nbox, *parent, *split; 655c66b8046SYuri Pankov const char *cp, *cpn; 656260e9a87SYuri Pankov char *p; 657c66b8046SYuri Pankov enum eqn_tok tok; 658c66b8046SYuri Pankov enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; 659260e9a87SYuri Pankov int size; 66095c635efSGarrett D'Amore 661c66b8046SYuri Pankov parent = ep->node->eqn; 662260e9a87SYuri Pankov assert(parent != NULL); 663260e9a87SYuri Pankov 664260e9a87SYuri Pankov /* 665260e9a87SYuri Pankov * Empty equation. 666260e9a87SYuri Pankov * Do not add it to the high-level syntax tree. 667260e9a87SYuri Pankov */ 668260e9a87SYuri Pankov 669260e9a87SYuri Pankov if (ep->data == NULL) 670c66b8046SYuri Pankov return; 671c66b8046SYuri Pankov 672c66b8046SYuri Pankov ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); 673260e9a87SYuri Pankov 674260e9a87SYuri Pankov next_tok: 675c66b8046SYuri Pankov tok = eqn_next(ep, MODE_TOK); 676260e9a87SYuri Pankov switch (tok) { 677c66b8046SYuri Pankov case EQN_TOK_UNDEF: 678260e9a87SYuri Pankov eqn_undef(ep); 679260e9a87SYuri Pankov break; 680c66b8046SYuri Pankov case EQN_TOK_NDEFINE: 681c66b8046SYuri Pankov case EQN_TOK_DEFINE: 682260e9a87SYuri Pankov eqn_def(ep); 683260e9a87SYuri Pankov break; 684c66b8046SYuri Pankov case EQN_TOK_TDEFINE: 685c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || 686c66b8046SYuri Pankov eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) 687*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, 688c66b8046SYuri Pankov ep->node->line, ep->node->pos, "tdefine"); 689260e9a87SYuri Pankov break; 690c66b8046SYuri Pankov case EQN_TOK_DELIM: 691260e9a87SYuri Pankov eqn_delim(ep); 692260e9a87SYuri Pankov break; 693c66b8046SYuri Pankov case EQN_TOK_GFONT: 694c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) 695*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 696*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 697260e9a87SYuri Pankov break; 698c66b8046SYuri Pankov case EQN_TOK_MARK: 699c66b8046SYuri Pankov case EQN_TOK_LINEUP: 700260e9a87SYuri Pankov /* Ignore these. */ 701260e9a87SYuri Pankov break; 702c66b8046SYuri Pankov case EQN_TOK_DYAD: 703c66b8046SYuri Pankov case EQN_TOK_VEC: 704c66b8046SYuri Pankov case EQN_TOK_UNDER: 705c66b8046SYuri Pankov case EQN_TOK_BAR: 706c66b8046SYuri Pankov case EQN_TOK_TILDE: 707c66b8046SYuri Pankov case EQN_TOK_HAT: 708c66b8046SYuri Pankov case EQN_TOK_DOT: 709c66b8046SYuri Pankov case EQN_TOK_DOTDOT: 710260e9a87SYuri Pankov if (parent->last == NULL) { 711*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, 712*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 713260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 714260e9a87SYuri Pankov cur->type = EQN_TEXT; 715260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 71695c635efSGarrett D'Amore } 717c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent); 718c66b8046SYuri Pankov parent->type = EQN_LIST; 719260e9a87SYuri Pankov parent->expectargs = 1; 720c66b8046SYuri Pankov parent->font = EQNFONT_ROMAN; 721260e9a87SYuri Pankov switch (tok) { 722c66b8046SYuri Pankov case EQN_TOK_DOTDOT: 723c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[ad]"); 724260e9a87SYuri Pankov break; 725c66b8046SYuri Pankov case EQN_TOK_VEC: 726c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[->]"); 727260e9a87SYuri Pankov break; 728c66b8046SYuri Pankov case EQN_TOK_DYAD: 729c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[<>]"); 730260e9a87SYuri Pankov break; 731c66b8046SYuri Pankov case EQN_TOK_TILDE: 732c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[a~]"); 733260e9a87SYuri Pankov break; 734c66b8046SYuri Pankov case EQN_TOK_UNDER: 735c66b8046SYuri Pankov parent->bottom = mandoc_strdup("\\[ul]"); 736260e9a87SYuri Pankov break; 737c66b8046SYuri Pankov case EQN_TOK_BAR: 738c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[rn]"); 739260e9a87SYuri Pankov break; 740c66b8046SYuri Pankov case EQN_TOK_DOT: 741c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[a.]"); 742260e9a87SYuri Pankov break; 743c66b8046SYuri Pankov case EQN_TOK_HAT: 744c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[ha]"); 745260e9a87SYuri Pankov break; 746260e9a87SYuri Pankov default: 747260e9a87SYuri Pankov abort(); 748260e9a87SYuri Pankov } 749260e9a87SYuri Pankov parent = parent->parent; 750260e9a87SYuri Pankov break; 751c66b8046SYuri Pankov case EQN_TOK_FWD: 752c66b8046SYuri Pankov case EQN_TOK_BACK: 753c66b8046SYuri Pankov case EQN_TOK_DOWN: 754c66b8046SYuri Pankov case EQN_TOK_UP: 755c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) 756*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 757*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 758260e9a87SYuri Pankov break; 759c66b8046SYuri Pankov case EQN_TOK_FAT: 760c66b8046SYuri Pankov case EQN_TOK_ROMAN: 761c66b8046SYuri Pankov case EQN_TOK_ITALIC: 762c66b8046SYuri Pankov case EQN_TOK_BOLD: 763260e9a87SYuri Pankov while (parent->args == parent->expectargs) 764260e9a87SYuri Pankov parent = parent->parent; 765260e9a87SYuri Pankov /* 766260e9a87SYuri Pankov * These values apply to the next word or sequence of 767260e9a87SYuri Pankov * words; thus, we mark that we'll have a child with 768260e9a87SYuri Pankov * exactly one of those. 769260e9a87SYuri Pankov */ 770260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 771c66b8046SYuri Pankov parent->type = EQN_LIST; 772260e9a87SYuri Pankov parent->expectargs = 1; 773260e9a87SYuri Pankov switch (tok) { 774c66b8046SYuri Pankov case EQN_TOK_FAT: 775260e9a87SYuri Pankov parent->font = EQNFONT_FAT; 776260e9a87SYuri Pankov break; 777c66b8046SYuri Pankov case EQN_TOK_ROMAN: 778260e9a87SYuri Pankov parent->font = EQNFONT_ROMAN; 779260e9a87SYuri Pankov break; 780c66b8046SYuri Pankov case EQN_TOK_ITALIC: 781260e9a87SYuri Pankov parent->font = EQNFONT_ITALIC; 782260e9a87SYuri Pankov break; 783c66b8046SYuri Pankov case EQN_TOK_BOLD: 784260e9a87SYuri Pankov parent->font = EQNFONT_BOLD; 785260e9a87SYuri Pankov break; 786260e9a87SYuri Pankov default: 787260e9a87SYuri Pankov abort(); 788260e9a87SYuri Pankov } 789260e9a87SYuri Pankov break; 790c66b8046SYuri Pankov case EQN_TOK_SIZE: 791c66b8046SYuri Pankov case EQN_TOK_GSIZE: 792260e9a87SYuri Pankov /* Accept two values: integral size and a single. */ 793c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 794*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 795*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 796260e9a87SYuri Pankov break; 797260e9a87SYuri Pankov } 798c66b8046SYuri Pankov size = mandoc_strntoi(ep->start, ep->toksz, 10); 799260e9a87SYuri Pankov if (-1 == size) { 800*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line, 801*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 802260e9a87SYuri Pankov break; 803260e9a87SYuri Pankov } 804260e9a87SYuri Pankov if (EQN_TOK_GSIZE == tok) { 805260e9a87SYuri Pankov ep->gsize = size; 806260e9a87SYuri Pankov break; 807260e9a87SYuri Pankov } 808c66b8046SYuri Pankov while (parent->args == parent->expectargs) 809c66b8046SYuri Pankov parent = parent->parent; 810260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 811c66b8046SYuri Pankov parent->type = EQN_LIST; 812260e9a87SYuri Pankov parent->expectargs = 1; 813260e9a87SYuri Pankov parent->size = size; 814260e9a87SYuri Pankov break; 815c66b8046SYuri Pankov case EQN_TOK_FROM: 816c66b8046SYuri Pankov case EQN_TOK_TO: 817c66b8046SYuri Pankov case EQN_TOK_SUB: 818c66b8046SYuri Pankov case EQN_TOK_SUP: 819260e9a87SYuri Pankov /* 820260e9a87SYuri Pankov * We have a left-right-associative expression. 821260e9a87SYuri Pankov * Repivot under a positional node, open a child scope 822260e9a87SYuri Pankov * and keep on reading. 823260e9a87SYuri Pankov */ 824260e9a87SYuri Pankov if (parent->last == NULL) { 825*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, 826*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 827260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 828260e9a87SYuri Pankov cur->type = EQN_TEXT; 829260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 830260e9a87SYuri Pankov } 831c66b8046SYuri Pankov while (parent->expectargs == 1 && parent->args == 1) 832c66b8046SYuri Pankov parent = parent->parent; 833c66b8046SYuri Pankov if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) { 834c66b8046SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 835c66b8046SYuri Pankov if (cur->pos == EQNPOS_SUB || 836c66b8046SYuri Pankov cur->pos == EQNPOS_SUP || 837c66b8046SYuri Pankov cur->pos == EQNPOS_SUBSUP || 838c66b8046SYuri Pankov cur->pos == EQNPOS_SQRT || 839c66b8046SYuri Pankov cur->pos == EQNPOS_OVER) 840c66b8046SYuri Pankov break; 841c66b8046SYuri Pankov if (cur != NULL) 842c66b8046SYuri Pankov parent = cur->parent; 843c66b8046SYuri Pankov } 844c66b8046SYuri Pankov if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) { 845260e9a87SYuri Pankov parent->expectargs = 3; 846260e9a87SYuri Pankov parent->pos = EQNPOS_SUBSUP; 847260e9a87SYuri Pankov break; 848260e9a87SYuri Pankov } 849c66b8046SYuri Pankov if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) { 850260e9a87SYuri Pankov parent->expectargs = 3; 851260e9a87SYuri Pankov parent->pos = EQNPOS_FROMTO; 852260e9a87SYuri Pankov break; 853260e9a87SYuri Pankov } 854c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent); 855260e9a87SYuri Pankov switch (tok) { 856c66b8046SYuri Pankov case EQN_TOK_FROM: 857c66b8046SYuri Pankov parent->pos = EQNPOS_FROM; 858260e9a87SYuri Pankov break; 859c66b8046SYuri Pankov case EQN_TOK_TO: 860c66b8046SYuri Pankov parent->pos = EQNPOS_TO; 861260e9a87SYuri Pankov break; 862c66b8046SYuri Pankov case EQN_TOK_SUP: 863c66b8046SYuri Pankov parent->pos = EQNPOS_SUP; 864260e9a87SYuri Pankov break; 865c66b8046SYuri Pankov case EQN_TOK_SUB: 866c66b8046SYuri Pankov parent->pos = EQNPOS_SUB; 867260e9a87SYuri Pankov break; 868260e9a87SYuri Pankov default: 869260e9a87SYuri Pankov abort(); 870260e9a87SYuri Pankov } 871260e9a87SYuri Pankov break; 872c66b8046SYuri Pankov case EQN_TOK_SQRT: 873260e9a87SYuri Pankov while (parent->args == parent->expectargs) 874260e9a87SYuri Pankov parent = parent->parent; 875260e9a87SYuri Pankov /* 876260e9a87SYuri Pankov * Accept a left-right-associative set of arguments just 877260e9a87SYuri Pankov * like sub and sup and friends but without rebalancing 878260e9a87SYuri Pankov * under a pivot. 879260e9a87SYuri Pankov */ 880260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 881260e9a87SYuri Pankov parent->type = EQN_SUBEXPR; 882260e9a87SYuri Pankov parent->pos = EQNPOS_SQRT; 883260e9a87SYuri Pankov parent->expectargs = 1; 884260e9a87SYuri Pankov break; 885c66b8046SYuri Pankov case EQN_TOK_OVER: 886260e9a87SYuri Pankov /* 887260e9a87SYuri Pankov * We have a right-left-associative fraction. 888260e9a87SYuri Pankov * Close out anything that's currently open, then 889260e9a87SYuri Pankov * rebalance and continue reading. 890260e9a87SYuri Pankov */ 891260e9a87SYuri Pankov if (parent->last == NULL) { 892*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, 893*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 894260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 895260e9a87SYuri Pankov cur->type = EQN_TEXT; 896260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 897260e9a87SYuri Pankov } 898c66b8046SYuri Pankov while (parent->args == parent->expectargs) 899c66b8046SYuri Pankov parent = parent->parent; 900260e9a87SYuri Pankov while (EQN_SUBEXPR == parent->type) 901260e9a87SYuri Pankov parent = parent->parent; 902c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent); 903c66b8046SYuri Pankov parent->pos = EQNPOS_OVER; 904260e9a87SYuri Pankov break; 905c66b8046SYuri Pankov case EQN_TOK_RIGHT: 906c66b8046SYuri Pankov case EQN_TOK_BRACE_CLOSE: 907260e9a87SYuri Pankov /* 908260e9a87SYuri Pankov * Close out the existing brace. 909260e9a87SYuri Pankov * FIXME: this is a shitty sentinel: we should really 910260e9a87SYuri Pankov * have a native EQN_BRACE type or whatnot. 911260e9a87SYuri Pankov */ 912260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 913260e9a87SYuri Pankov if (cur->type == EQN_LIST && 914c66b8046SYuri Pankov cur->expectargs > 1 && 915260e9a87SYuri Pankov (tok == EQN_TOK_BRACE_CLOSE || 916260e9a87SYuri Pankov cur->left != NULL)) 917260e9a87SYuri Pankov break; 918260e9a87SYuri Pankov if (cur == NULL) { 919*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line, 920*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 921260e9a87SYuri Pankov break; 922260e9a87SYuri Pankov } 923260e9a87SYuri Pankov parent = cur; 924260e9a87SYuri Pankov if (EQN_TOK_RIGHT == tok) { 925c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 926260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, 927*cec8643bSMichal Nowak ep->node->line, ep->node->pos, 928*cec8643bSMichal Nowak "%s", eqn_toks[tok]); 929260e9a87SYuri Pankov break; 930260e9a87SYuri Pankov } 931260e9a87SYuri Pankov /* Handling depends on right/left. */ 932c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) 933c66b8046SYuri Pankov parent->right = mandoc_strdup("\\[rc]"); 934c66b8046SYuri Pankov else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) 935c66b8046SYuri Pankov parent->right = mandoc_strdup("\\[rf]"); 936c66b8046SYuri Pankov else 937c66b8046SYuri Pankov parent->right = 938c66b8046SYuri Pankov mandoc_strndup(ep->start, ep->toksz); 939260e9a87SYuri Pankov } 940260e9a87SYuri Pankov parent = parent->parent; 941371584c2SYuri Pankov if (tok == EQN_TOK_BRACE_CLOSE && 942260e9a87SYuri Pankov (parent->type == EQN_PILE || 943260e9a87SYuri Pankov parent->type == EQN_MATRIX)) 944260e9a87SYuri Pankov parent = parent->parent; 945260e9a87SYuri Pankov /* Close out any "singleton" lists. */ 946c66b8046SYuri Pankov while (parent->type == EQN_LIST && 947c66b8046SYuri Pankov parent->expectargs == 1 && 948c66b8046SYuri Pankov parent->args == 1) 949260e9a87SYuri Pankov parent = parent->parent; 950260e9a87SYuri Pankov break; 951c66b8046SYuri Pankov case EQN_TOK_BRACE_OPEN: 952c66b8046SYuri Pankov case EQN_TOK_LEFT: 953260e9a87SYuri Pankov /* 954260e9a87SYuri Pankov * If we already have something in the stack and we're 955260e9a87SYuri Pankov * in an expression, then rewind til we're not any more 956260e9a87SYuri Pankov * (just like with the text node). 957260e9a87SYuri Pankov */ 958260e9a87SYuri Pankov while (parent->args == parent->expectargs) 959260e9a87SYuri Pankov parent = parent->parent; 960260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok && 961c66b8046SYuri Pankov eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 962*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 963*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 964260e9a87SYuri Pankov break; 965260e9a87SYuri Pankov } 966260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 967260e9a87SYuri Pankov parent->type = EQN_LIST; 968260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok) { 969c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) 970c66b8046SYuri Pankov parent->left = mandoc_strdup("\\[lc]"); 971c66b8046SYuri Pankov else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) 972c66b8046SYuri Pankov parent->left = mandoc_strdup("\\[lf]"); 973c66b8046SYuri Pankov else 974c66b8046SYuri Pankov parent->left = 975c66b8046SYuri Pankov mandoc_strndup(ep->start, ep->toksz); 976260e9a87SYuri Pankov } 977260e9a87SYuri Pankov break; 978c66b8046SYuri Pankov case EQN_TOK_PILE: 979c66b8046SYuri Pankov case EQN_TOK_LPILE: 980c66b8046SYuri Pankov case EQN_TOK_RPILE: 981c66b8046SYuri Pankov case EQN_TOK_CPILE: 982c66b8046SYuri Pankov case EQN_TOK_CCOL: 983c66b8046SYuri Pankov case EQN_TOK_LCOL: 984c66b8046SYuri Pankov case EQN_TOK_RCOL: 985260e9a87SYuri Pankov while (parent->args == parent->expectargs) 986260e9a87SYuri Pankov parent = parent->parent; 987260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 988260e9a87SYuri Pankov parent->type = EQN_PILE; 989260e9a87SYuri Pankov parent->expectargs = 1; 990260e9a87SYuri Pankov break; 991c66b8046SYuri Pankov case EQN_TOK_ABOVE: 992260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 993260e9a87SYuri Pankov if (cur->type == EQN_PILE) 994260e9a87SYuri Pankov break; 995260e9a87SYuri Pankov if (cur == NULL) { 996*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line, 997*cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]); 998260e9a87SYuri Pankov break; 999260e9a87SYuri Pankov } 1000260e9a87SYuri Pankov parent = eqn_box_alloc(ep, cur); 1001260e9a87SYuri Pankov parent->type = EQN_LIST; 1002260e9a87SYuri Pankov break; 1003c66b8046SYuri Pankov case EQN_TOK_MATRIX: 1004260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1005260e9a87SYuri Pankov parent = parent->parent; 1006260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 1007260e9a87SYuri Pankov parent->type = EQN_MATRIX; 1008260e9a87SYuri Pankov parent->expectargs = 1; 1009260e9a87SYuri Pankov break; 1010c66b8046SYuri Pankov case EQN_TOK_EOF: 1011c66b8046SYuri Pankov return; 1012c66b8046SYuri Pankov case EQN_TOK__MAX: 1013c66b8046SYuri Pankov case EQN_TOK_FUNC: 1014c66b8046SYuri Pankov case EQN_TOK_QUOTED: 1015c66b8046SYuri Pankov case EQN_TOK_SYM: 1016c66b8046SYuri Pankov p = ep->start; 1017c66b8046SYuri Pankov assert(p != NULL); 1018260e9a87SYuri Pankov /* 1019260e9a87SYuri Pankov * If we already have something in the stack and we're 1020260e9a87SYuri Pankov * in an expression, then rewind til we're not any more. 1021260e9a87SYuri Pankov */ 1022260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1023260e9a87SYuri Pankov parent = parent->parent; 1024260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 1025260e9a87SYuri Pankov cur->type = EQN_TEXT; 1026c66b8046SYuri Pankov cur->text = p; 1027c66b8046SYuri Pankov switch (tok) { 1028c66b8046SYuri Pankov case EQN_TOK_FUNC: 1029c66b8046SYuri Pankov cur->font = EQNFONT_ROMAN; 1030c66b8046SYuri Pankov break; 1031c66b8046SYuri Pankov case EQN_TOK_QUOTED: 1032c66b8046SYuri Pankov if (cur->font == EQNFONT_NONE) 1033c66b8046SYuri Pankov cur->font = EQNFONT_ITALIC; 1034c66b8046SYuri Pankov break; 1035c66b8046SYuri Pankov case EQN_TOK_SYM: 1036c66b8046SYuri Pankov break; 1037c66b8046SYuri Pankov default: 1038c66b8046SYuri Pankov if (cur->font != EQNFONT_NONE || *p == '\0') 1039c66b8046SYuri Pankov break; 1040c66b8046SYuri Pankov cpn = p - 1; 1041c66b8046SYuri Pankov ccln = CCL_LET; 1042c66b8046SYuri Pankov split = NULL; 1043c66b8046SYuri Pankov for (;;) { 1044c66b8046SYuri Pankov /* Advance to next character. */ 1045c66b8046SYuri Pankov cp = cpn++; 1046c66b8046SYuri Pankov ccl = ccln; 1047c66b8046SYuri Pankov ccln = isalpha((unsigned char)*cpn) ? CCL_LET : 1048c66b8046SYuri Pankov isdigit((unsigned char)*cpn) || 1049c66b8046SYuri Pankov (*cpn == '.' && (ccl == CCL_DIG || 1050c66b8046SYuri Pankov isdigit((unsigned char)cpn[1]))) ? 1051c66b8046SYuri Pankov CCL_DIG : CCL_PUN; 1052c66b8046SYuri Pankov /* No boundary before first character. */ 1053c66b8046SYuri Pankov if (cp < p) 1054c66b8046SYuri Pankov continue; 1055c66b8046SYuri Pankov cur->font = ccl == CCL_LET ? 1056c66b8046SYuri Pankov EQNFONT_ITALIC : EQNFONT_ROMAN; 1057c66b8046SYuri Pankov if (*cp == '\\') 1058c66b8046SYuri Pankov mandoc_escape(&cpn, NULL, NULL); 1059c66b8046SYuri Pankov /* No boundary after last character. */ 1060c66b8046SYuri Pankov if (*cpn == '\0') 1061c66b8046SYuri Pankov break; 1062c66b8046SYuri Pankov if (ccln == ccl && *cp != ',' && *cpn != ',') 1063c66b8046SYuri Pankov continue; 1064c66b8046SYuri Pankov /* Boundary found, split the text. */ 1065c66b8046SYuri Pankov if (parent->args == parent->expectargs) { 1066c66b8046SYuri Pankov /* Remove the text from the tree. */ 1067c66b8046SYuri Pankov if (cur->prev == NULL) 1068c66b8046SYuri Pankov parent->first = cur->next; 1069c66b8046SYuri Pankov else 1070c66b8046SYuri Pankov cur->prev->next = NULL; 1071c66b8046SYuri Pankov parent->last = cur->prev; 1072c66b8046SYuri Pankov parent->args--; 1073c66b8046SYuri Pankov /* Set up a list instead. */ 1074c66b8046SYuri Pankov split = eqn_box_alloc(ep, parent); 1075c66b8046SYuri Pankov split->type = EQN_LIST; 1076c66b8046SYuri Pankov /* Insert the word into the list. */ 1077c66b8046SYuri Pankov split->first = split->last = cur; 1078c66b8046SYuri Pankov cur->parent = split; 1079c66b8046SYuri Pankov cur->prev = NULL; 1080c66b8046SYuri Pankov parent = split; 1081c66b8046SYuri Pankov } 1082c66b8046SYuri Pankov /* Append a new text box. */ 1083c66b8046SYuri Pankov nbox = eqn_box_alloc(ep, parent); 1084c66b8046SYuri Pankov nbox->type = EQN_TEXT; 1085c66b8046SYuri Pankov nbox->text = mandoc_strdup(cpn); 1086c66b8046SYuri Pankov /* Truncate the old box. */ 1087c66b8046SYuri Pankov p = mandoc_strndup(cur->text, 1088c66b8046SYuri Pankov cpn - cur->text); 1089c66b8046SYuri Pankov free(cur->text); 1090c66b8046SYuri Pankov cur->text = p; 1091c66b8046SYuri Pankov /* Setup to process the new box. */ 1092c66b8046SYuri Pankov cur = nbox; 1093c66b8046SYuri Pankov p = nbox->text; 1094c66b8046SYuri Pankov cpn = p - 1; 1095c66b8046SYuri Pankov ccln = CCL_LET; 1096c66b8046SYuri Pankov } 1097c66b8046SYuri Pankov if (split != NULL) 1098c66b8046SYuri Pankov parent = split->parent; 1099260e9a87SYuri Pankov break; 1100260e9a87SYuri Pankov } 1101260e9a87SYuri Pankov break; 1102c66b8046SYuri Pankov default: 1103c66b8046SYuri Pankov abort(); 1104260e9a87SYuri Pankov } 1105260e9a87SYuri Pankov goto next_tok; 1106260e9a87SYuri Pankov } 1107260e9a87SYuri Pankov 1108260e9a87SYuri Pankov void 1109260e9a87SYuri Pankov eqn_free(struct eqn_node *p) 111095c635efSGarrett D'Amore { 111195c635efSGarrett D'Amore int i; 111295c635efSGarrett D'Amore 1113*cec8643bSMichal Nowak if (p == NULL) 1114*cec8643bSMichal Nowak return; 1115*cec8643bSMichal Nowak 1116260e9a87SYuri Pankov for (i = 0; i < (int)p->defsz; i++) { 1117260e9a87SYuri Pankov free(p->defs[i].key); 1118260e9a87SYuri Pankov free(p->defs[i].val); 1119260e9a87SYuri Pankov } 1120260e9a87SYuri Pankov 1121260e9a87SYuri Pankov free(p->data); 1122260e9a87SYuri Pankov free(p->defs); 1123260e9a87SYuri Pankov free(p); 112495c635efSGarrett D'Amore } 1125