1*c66b8046SYuri Pankov /* $Id: eqn.c,v 1.78 2017/07/15 16:26:17 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 3260e9a87SYuri Pankov * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4*c66b8046SYuri Pankov * Copyright (c) 2014, 2015, 2017 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> 23*c66b8046SYuri 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" 31*c66b8046SYuri Pankov #include "mandoc.h" 32*c66b8046SYuri Pankov #include "roff.h" 3395c635efSGarrett D'Amore #include "libmandoc.h" 3495c635efSGarrett D'Amore #include "libroff.h" 3595c635efSGarrett D'Amore 3695c635efSGarrett D'Amore #define EQN_NEST_MAX 128 /* maximum nesting of defines */ 37260e9a87SYuri Pankov #define STRNEQ(p1, sz1, p2, sz2) \ 38260e9a87SYuri Pankov ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) 3995c635efSGarrett D'Amore 40260e9a87SYuri Pankov enum eqn_tok { 41260e9a87SYuri Pankov EQN_TOK_DYAD = 0, 42260e9a87SYuri Pankov EQN_TOK_VEC, 43260e9a87SYuri Pankov EQN_TOK_UNDER, 44260e9a87SYuri Pankov EQN_TOK_BAR, 45260e9a87SYuri Pankov EQN_TOK_TILDE, 46260e9a87SYuri Pankov EQN_TOK_HAT, 47260e9a87SYuri Pankov EQN_TOK_DOT, 48260e9a87SYuri Pankov EQN_TOK_DOTDOT, 49260e9a87SYuri Pankov EQN_TOK_FWD, 50260e9a87SYuri Pankov EQN_TOK_BACK, 51260e9a87SYuri Pankov EQN_TOK_DOWN, 52260e9a87SYuri Pankov EQN_TOK_UP, 53260e9a87SYuri Pankov EQN_TOK_FAT, 54260e9a87SYuri Pankov EQN_TOK_ROMAN, 55260e9a87SYuri Pankov EQN_TOK_ITALIC, 56260e9a87SYuri Pankov EQN_TOK_BOLD, 57260e9a87SYuri Pankov EQN_TOK_SIZE, 58260e9a87SYuri Pankov EQN_TOK_SUB, 59260e9a87SYuri Pankov EQN_TOK_SUP, 60260e9a87SYuri Pankov EQN_TOK_SQRT, 61260e9a87SYuri Pankov EQN_TOK_OVER, 62260e9a87SYuri Pankov EQN_TOK_FROM, 63260e9a87SYuri Pankov EQN_TOK_TO, 64260e9a87SYuri Pankov EQN_TOK_BRACE_OPEN, 65260e9a87SYuri Pankov EQN_TOK_BRACE_CLOSE, 66260e9a87SYuri Pankov EQN_TOK_GSIZE, 67260e9a87SYuri Pankov EQN_TOK_GFONT, 68260e9a87SYuri Pankov EQN_TOK_MARK, 69260e9a87SYuri Pankov EQN_TOK_LINEUP, 70260e9a87SYuri Pankov EQN_TOK_LEFT, 71260e9a87SYuri Pankov EQN_TOK_RIGHT, 72260e9a87SYuri Pankov EQN_TOK_PILE, 73260e9a87SYuri Pankov EQN_TOK_LPILE, 74260e9a87SYuri Pankov EQN_TOK_RPILE, 75260e9a87SYuri Pankov EQN_TOK_CPILE, 76260e9a87SYuri Pankov EQN_TOK_MATRIX, 77260e9a87SYuri Pankov EQN_TOK_CCOL, 78260e9a87SYuri Pankov EQN_TOK_LCOL, 79260e9a87SYuri Pankov EQN_TOK_RCOL, 80260e9a87SYuri Pankov EQN_TOK_DELIM, 81260e9a87SYuri Pankov EQN_TOK_DEFINE, 82260e9a87SYuri Pankov EQN_TOK_TDEFINE, 83260e9a87SYuri Pankov EQN_TOK_NDEFINE, 84260e9a87SYuri Pankov EQN_TOK_UNDEF, 85260e9a87SYuri Pankov EQN_TOK_ABOVE, 86*c66b8046SYuri Pankov EQN_TOK__MAX, 87*c66b8046SYuri Pankov EQN_TOK_FUNC, 88*c66b8046SYuri Pankov EQN_TOK_QUOTED, 89*c66b8046SYuri Pankov EQN_TOK_SYM, 90*c66b8046SYuri Pankov EQN_TOK_EOF 91260e9a87SYuri Pankov }; 92260e9a87SYuri Pankov 93260e9a87SYuri Pankov static const char *eqn_toks[EQN_TOK__MAX] = { 94260e9a87SYuri Pankov "dyad", /* EQN_TOK_DYAD */ 95260e9a87SYuri Pankov "vec", /* EQN_TOK_VEC */ 96260e9a87SYuri Pankov "under", /* EQN_TOK_UNDER */ 97260e9a87SYuri Pankov "bar", /* EQN_TOK_BAR */ 98260e9a87SYuri Pankov "tilde", /* EQN_TOK_TILDE */ 99260e9a87SYuri Pankov "hat", /* EQN_TOK_HAT */ 100260e9a87SYuri Pankov "dot", /* EQN_TOK_DOT */ 101260e9a87SYuri Pankov "dotdot", /* EQN_TOK_DOTDOT */ 102260e9a87SYuri Pankov "fwd", /* EQN_TOK_FWD * */ 103260e9a87SYuri Pankov "back", /* EQN_TOK_BACK */ 104260e9a87SYuri Pankov "down", /* EQN_TOK_DOWN */ 105260e9a87SYuri Pankov "up", /* EQN_TOK_UP */ 106260e9a87SYuri Pankov "fat", /* EQN_TOK_FAT */ 107260e9a87SYuri Pankov "roman", /* EQN_TOK_ROMAN */ 108260e9a87SYuri Pankov "italic", /* EQN_TOK_ITALIC */ 109260e9a87SYuri Pankov "bold", /* EQN_TOK_BOLD */ 110260e9a87SYuri Pankov "size", /* EQN_TOK_SIZE */ 111260e9a87SYuri Pankov "sub", /* EQN_TOK_SUB */ 112260e9a87SYuri Pankov "sup", /* EQN_TOK_SUP */ 113260e9a87SYuri Pankov "sqrt", /* EQN_TOK_SQRT */ 114260e9a87SYuri Pankov "over", /* EQN_TOK_OVER */ 115260e9a87SYuri Pankov "from", /* EQN_TOK_FROM */ 116260e9a87SYuri Pankov "to", /* EQN_TOK_TO */ 117260e9a87SYuri Pankov "{", /* EQN_TOK_BRACE_OPEN */ 118260e9a87SYuri Pankov "}", /* EQN_TOK_BRACE_CLOSE */ 119260e9a87SYuri Pankov "gsize", /* EQN_TOK_GSIZE */ 120260e9a87SYuri Pankov "gfont", /* EQN_TOK_GFONT */ 121260e9a87SYuri Pankov "mark", /* EQN_TOK_MARK */ 122260e9a87SYuri Pankov "lineup", /* EQN_TOK_LINEUP */ 123260e9a87SYuri Pankov "left", /* EQN_TOK_LEFT */ 124260e9a87SYuri Pankov "right", /* EQN_TOK_RIGHT */ 125260e9a87SYuri Pankov "pile", /* EQN_TOK_PILE */ 126260e9a87SYuri Pankov "lpile", /* EQN_TOK_LPILE */ 127260e9a87SYuri Pankov "rpile", /* EQN_TOK_RPILE */ 128260e9a87SYuri Pankov "cpile", /* EQN_TOK_CPILE */ 129260e9a87SYuri Pankov "matrix", /* EQN_TOK_MATRIX */ 130260e9a87SYuri Pankov "ccol", /* EQN_TOK_CCOL */ 131260e9a87SYuri Pankov "lcol", /* EQN_TOK_LCOL */ 132260e9a87SYuri Pankov "rcol", /* EQN_TOK_RCOL */ 133260e9a87SYuri Pankov "delim", /* EQN_TOK_DELIM */ 134260e9a87SYuri Pankov "define", /* EQN_TOK_DEFINE */ 135260e9a87SYuri Pankov "tdefine", /* EQN_TOK_TDEFINE */ 136260e9a87SYuri Pankov "ndefine", /* EQN_TOK_NDEFINE */ 137260e9a87SYuri Pankov "undef", /* EQN_TOK_UNDEF */ 138260e9a87SYuri Pankov "above", /* EQN_TOK_ABOVE */ 13995c635efSGarrett D'Amore }; 14095c635efSGarrett D'Amore 141*c66b8046SYuri Pankov static const char *const eqn_func[] = { 142*c66b8046SYuri Pankov "acos", "acsc", "and", "arc", "asec", "asin", "atan", 143*c66b8046SYuri Pankov "cos", "cosh", "coth", "csc", "det", "exp", "for", 144*c66b8046SYuri Pankov "if", "lim", "ln", "log", "max", "min", 145*c66b8046SYuri Pankov "sec", "sin", "sinh", "tan", "tanh", "Im", "Re", 146*c66b8046SYuri Pankov }; 147*c66b8046SYuri Pankov 14895c635efSGarrett D'Amore enum eqn_symt { 149*c66b8046SYuri Pankov EQNSYM_alpha = 0, 15095c635efSGarrett D'Amore EQNSYM_beta, 15195c635efSGarrett D'Amore EQNSYM_chi, 15295c635efSGarrett D'Amore EQNSYM_delta, 15395c635efSGarrett D'Amore EQNSYM_epsilon, 15495c635efSGarrett D'Amore EQNSYM_eta, 15595c635efSGarrett D'Amore EQNSYM_gamma, 15695c635efSGarrett D'Amore EQNSYM_iota, 15795c635efSGarrett D'Amore EQNSYM_kappa, 15895c635efSGarrett D'Amore EQNSYM_lambda, 15995c635efSGarrett D'Amore EQNSYM_mu, 16095c635efSGarrett D'Amore EQNSYM_nu, 16195c635efSGarrett D'Amore EQNSYM_omega, 16295c635efSGarrett D'Amore EQNSYM_omicron, 16395c635efSGarrett D'Amore EQNSYM_phi, 16495c635efSGarrett D'Amore EQNSYM_pi, 16595c635efSGarrett D'Amore EQNSYM_ps, 16695c635efSGarrett D'Amore EQNSYM_rho, 16795c635efSGarrett D'Amore EQNSYM_sigma, 16895c635efSGarrett D'Amore EQNSYM_tau, 16995c635efSGarrett D'Amore EQNSYM_theta, 17095c635efSGarrett D'Amore EQNSYM_upsilon, 17195c635efSGarrett D'Amore EQNSYM_xi, 17295c635efSGarrett D'Amore EQNSYM_zeta, 17395c635efSGarrett D'Amore EQNSYM_DELTA, 17495c635efSGarrett D'Amore EQNSYM_GAMMA, 17595c635efSGarrett D'Amore EQNSYM_LAMBDA, 17695c635efSGarrett D'Amore EQNSYM_OMEGA, 17795c635efSGarrett D'Amore EQNSYM_PHI, 17895c635efSGarrett D'Amore EQNSYM_PI, 17995c635efSGarrett D'Amore EQNSYM_PSI, 18095c635efSGarrett D'Amore EQNSYM_SIGMA, 18195c635efSGarrett D'Amore EQNSYM_THETA, 18295c635efSGarrett D'Amore EQNSYM_UPSILON, 18395c635efSGarrett D'Amore EQNSYM_XI, 18495c635efSGarrett D'Amore EQNSYM_inter, 18595c635efSGarrett D'Amore EQNSYM_union, 18695c635efSGarrett D'Amore EQNSYM_prod, 18795c635efSGarrett D'Amore EQNSYM_int, 18895c635efSGarrett D'Amore EQNSYM_sum, 18995c635efSGarrett D'Amore EQNSYM_grad, 19095c635efSGarrett D'Amore EQNSYM_del, 19195c635efSGarrett D'Amore EQNSYM_times, 19295c635efSGarrett D'Amore EQNSYM_cdot, 19395c635efSGarrett D'Amore EQNSYM_nothing, 19495c635efSGarrett D'Amore EQNSYM_approx, 19595c635efSGarrett D'Amore EQNSYM_prime, 19695c635efSGarrett D'Amore EQNSYM_half, 19795c635efSGarrett D'Amore EQNSYM_partial, 19895c635efSGarrett D'Amore EQNSYM_inf, 19995c635efSGarrett D'Amore EQNSYM_muchgreat, 20095c635efSGarrett D'Amore EQNSYM_muchless, 20195c635efSGarrett D'Amore EQNSYM_larrow, 20295c635efSGarrett D'Amore EQNSYM_rarrow, 20395c635efSGarrett D'Amore EQNSYM_pm, 20495c635efSGarrett D'Amore EQNSYM_nequal, 20595c635efSGarrett D'Amore EQNSYM_equiv, 20695c635efSGarrett D'Amore EQNSYM_lessequal, 20795c635efSGarrett D'Amore EQNSYM_moreequal, 208260e9a87SYuri Pankov EQNSYM_minus, 20995c635efSGarrett D'Amore EQNSYM__MAX 21095c635efSGarrett D'Amore }; 21195c635efSGarrett D'Amore 21295c635efSGarrett D'Amore struct eqnsym { 213260e9a87SYuri Pankov const char *str; 21495c635efSGarrett D'Amore const char *sym; 21595c635efSGarrett D'Amore }; 21695c635efSGarrett D'Amore 21795c635efSGarrett D'Amore static const struct eqnsym eqnsyms[EQNSYM__MAX] = { 218260e9a87SYuri Pankov { "alpha", "*a" }, /* EQNSYM_alpha */ 219260e9a87SYuri Pankov { "beta", "*b" }, /* EQNSYM_beta */ 220260e9a87SYuri Pankov { "chi", "*x" }, /* EQNSYM_chi */ 221260e9a87SYuri Pankov { "delta", "*d" }, /* EQNSYM_delta */ 222260e9a87SYuri Pankov { "epsilon", "*e" }, /* EQNSYM_epsilon */ 223260e9a87SYuri Pankov { "eta", "*y" }, /* EQNSYM_eta */ 224260e9a87SYuri Pankov { "gamma", "*g" }, /* EQNSYM_gamma */ 225260e9a87SYuri Pankov { "iota", "*i" }, /* EQNSYM_iota */ 226260e9a87SYuri Pankov { "kappa", "*k" }, /* EQNSYM_kappa */ 227260e9a87SYuri Pankov { "lambda", "*l" }, /* EQNSYM_lambda */ 228260e9a87SYuri Pankov { "mu", "*m" }, /* EQNSYM_mu */ 229260e9a87SYuri Pankov { "nu", "*n" }, /* EQNSYM_nu */ 230260e9a87SYuri Pankov { "omega", "*w" }, /* EQNSYM_omega */ 231260e9a87SYuri Pankov { "omicron", "*o" }, /* EQNSYM_omicron */ 232260e9a87SYuri Pankov { "phi", "*f" }, /* EQNSYM_phi */ 233260e9a87SYuri Pankov { "pi", "*p" }, /* EQNSYM_pi */ 234260e9a87SYuri Pankov { "psi", "*q" }, /* EQNSYM_psi */ 235260e9a87SYuri Pankov { "rho", "*r" }, /* EQNSYM_rho */ 236260e9a87SYuri Pankov { "sigma", "*s" }, /* EQNSYM_sigma */ 237260e9a87SYuri Pankov { "tau", "*t" }, /* EQNSYM_tau */ 238260e9a87SYuri Pankov { "theta", "*h" }, /* EQNSYM_theta */ 239260e9a87SYuri Pankov { "upsilon", "*u" }, /* EQNSYM_upsilon */ 240260e9a87SYuri Pankov { "xi", "*c" }, /* EQNSYM_xi */ 241260e9a87SYuri Pankov { "zeta", "*z" }, /* EQNSYM_zeta */ 242260e9a87SYuri Pankov { "DELTA", "*D" }, /* EQNSYM_DELTA */ 243260e9a87SYuri Pankov { "GAMMA", "*G" }, /* EQNSYM_GAMMA */ 244260e9a87SYuri Pankov { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */ 245260e9a87SYuri Pankov { "OMEGA", "*W" }, /* EQNSYM_OMEGA */ 246260e9a87SYuri Pankov { "PHI", "*F" }, /* EQNSYM_PHI */ 247260e9a87SYuri Pankov { "PI", "*P" }, /* EQNSYM_PI */ 248260e9a87SYuri Pankov { "PSI", "*Q" }, /* EQNSYM_PSI */ 249260e9a87SYuri Pankov { "SIGMA", "*S" }, /* EQNSYM_SIGMA */ 250260e9a87SYuri Pankov { "THETA", "*H" }, /* EQNSYM_THETA */ 251260e9a87SYuri Pankov { "UPSILON", "*U" }, /* EQNSYM_UPSILON */ 252260e9a87SYuri Pankov { "XI", "*C" }, /* EQNSYM_XI */ 253260e9a87SYuri Pankov { "inter", "ca" }, /* EQNSYM_inter */ 254260e9a87SYuri Pankov { "union", "cu" }, /* EQNSYM_union */ 255260e9a87SYuri Pankov { "prod", "product" }, /* EQNSYM_prod */ 256260e9a87SYuri Pankov { "int", "integral" }, /* EQNSYM_int */ 257260e9a87SYuri Pankov { "sum", "sum" }, /* EQNSYM_sum */ 258260e9a87SYuri Pankov { "grad", "gr" }, /* EQNSYM_grad */ 259260e9a87SYuri Pankov { "del", "gr" }, /* EQNSYM_del */ 260260e9a87SYuri Pankov { "times", "mu" }, /* EQNSYM_times */ 261260e9a87SYuri Pankov { "cdot", "pc" }, /* EQNSYM_cdot */ 262260e9a87SYuri Pankov { "nothing", "&" }, /* EQNSYM_nothing */ 263260e9a87SYuri Pankov { "approx", "~~" }, /* EQNSYM_approx */ 264260e9a87SYuri Pankov { "prime", "fm" }, /* EQNSYM_prime */ 265260e9a87SYuri Pankov { "half", "12" }, /* EQNSYM_half */ 266260e9a87SYuri Pankov { "partial", "pd" }, /* EQNSYM_partial */ 267260e9a87SYuri Pankov { "inf", "if" }, /* EQNSYM_inf */ 268260e9a87SYuri Pankov { ">>", ">>" }, /* EQNSYM_muchgreat */ 269260e9a87SYuri Pankov { "<<", "<<" }, /* EQNSYM_muchless */ 270260e9a87SYuri Pankov { "<-", "<-" }, /* EQNSYM_larrow */ 271260e9a87SYuri Pankov { "->", "->" }, /* EQNSYM_rarrow */ 272260e9a87SYuri Pankov { "+-", "+-" }, /* EQNSYM_pm */ 273260e9a87SYuri Pankov { "!=", "!=" }, /* EQNSYM_nequal */ 274260e9a87SYuri Pankov { "==", "==" }, /* EQNSYM_equiv */ 275260e9a87SYuri Pankov { "<=", "<=" }, /* EQNSYM_lessequal */ 276260e9a87SYuri Pankov { ">=", ">=" }, /* EQNSYM_moreequal */ 277260e9a87SYuri Pankov { "-", "mi" }, /* EQNSYM_minus */ 27895c635efSGarrett D'Amore }; 27995c635efSGarrett D'Amore 280*c66b8046SYuri Pankov enum parse_mode { 281*c66b8046SYuri Pankov MODE_QUOTED, 282*c66b8046SYuri Pankov MODE_NOSUB, 283*c66b8046SYuri Pankov MODE_SUB, 284*c66b8046SYuri Pankov MODE_TOK 285*c66b8046SYuri Pankov }; 286*c66b8046SYuri Pankov 287260e9a87SYuri Pankov static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); 288260e9a87SYuri Pankov static struct eqn_box *eqn_box_makebinary(struct eqn_node *, 289*c66b8046SYuri Pankov struct eqn_box *); 290260e9a87SYuri Pankov static void eqn_def(struct eqn_node *); 291*c66b8046SYuri Pankov static struct eqn_def *eqn_def_find(struct eqn_node *); 292260e9a87SYuri Pankov static void eqn_delim(struct eqn_node *); 293*c66b8046SYuri Pankov static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); 294260e9a87SYuri Pankov static void eqn_undef(struct eqn_node *); 295260e9a87SYuri Pankov 296260e9a87SYuri Pankov 29795c635efSGarrett D'Amore struct eqn_node * 298*c66b8046SYuri Pankov eqn_alloc(struct mparse *parse) 29995c635efSGarrett D'Amore { 300*c66b8046SYuri Pankov struct eqn_node *ep; 30195c635efSGarrett D'Amore 302*c66b8046SYuri Pankov ep = mandoc_calloc(1, sizeof(*ep)); 303*c66b8046SYuri Pankov ep->parse = parse; 304*c66b8046SYuri Pankov ep->gsize = EQN_DEFSIZE; 305*c66b8046SYuri Pankov return ep; 306*c66b8046SYuri Pankov } 30795c635efSGarrett D'Amore 308*c66b8046SYuri Pankov void 309*c66b8046SYuri Pankov eqn_reset(struct eqn_node *ep) 310*c66b8046SYuri Pankov { 311*c66b8046SYuri Pankov free(ep->data); 312*c66b8046SYuri Pankov ep->data = ep->start = ep->end = NULL; 313*c66b8046SYuri Pankov ep->sz = ep->toksz = 0; 314*c66b8046SYuri Pankov } 31595c635efSGarrett D'Amore 316*c66b8046SYuri Pankov void 317*c66b8046SYuri Pankov eqn_read(struct eqn_node *ep, const char *p) 318*c66b8046SYuri Pankov { 319*c66b8046SYuri Pankov char *cp; 320*c66b8046SYuri Pankov 321*c66b8046SYuri Pankov if (ep->data == NULL) { 322*c66b8046SYuri Pankov ep->sz = strlen(p); 323*c66b8046SYuri Pankov ep->data = mandoc_strdup(p); 324*c66b8046SYuri Pankov } else { 325*c66b8046SYuri Pankov ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p); 326*c66b8046SYuri Pankov free(ep->data); 327*c66b8046SYuri Pankov ep->data = cp; 328*c66b8046SYuri Pankov } 329*c66b8046SYuri Pankov ep->sz += 1; 33095c635efSGarrett D'Amore } 33195c635efSGarrett D'Amore 332260e9a87SYuri Pankov /* 333260e9a87SYuri Pankov * Find the key "key" of the give size within our eqn-defined values. 334260e9a87SYuri Pankov */ 335260e9a87SYuri Pankov static struct eqn_def * 336*c66b8046SYuri Pankov eqn_def_find(struct eqn_node *ep) 33795c635efSGarrett D'Amore { 33895c635efSGarrett D'Amore int i; 33995c635efSGarrett D'Amore 340260e9a87SYuri Pankov for (i = 0; i < (int)ep->defsz; i++) 341260e9a87SYuri Pankov if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, 342*c66b8046SYuri Pankov ep->defs[i].keysz, ep->start, ep->toksz)) 343371584c2SYuri Pankov return &ep->defs[i]; 34495c635efSGarrett D'Amore 345371584c2SYuri Pankov return NULL; 34695c635efSGarrett D'Amore } 34795c635efSGarrett D'Amore 348260e9a87SYuri Pankov /* 349*c66b8046SYuri Pankov * Parse a token from the input text. The modes are: 350*c66b8046SYuri Pankov * MODE_QUOTED: Use *ep->start as the delimiter; the token ends 351*c66b8046SYuri Pankov * before its next occurence. Do not interpret the token in any 352*c66b8046SYuri Pankov * way and return EQN_TOK_QUOTED. All other modes behave like 353*c66b8046SYuri Pankov * MODE_QUOTED when *ep->start is '"'. 354*c66b8046SYuri Pankov * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it; 355*c66b8046SYuri Pankov * otherwise, it ends before the next whitespace or brace. 356*c66b8046SYuri Pankov * Do not interpret the token and return EQN_TOK__MAX. 357*c66b8046SYuri Pankov * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an 358*c66b8046SYuri Pankov * alias created with define. If it is an alias, replace it with 359*c66b8046SYuri Pankov * its string value and reparse. 360*c66b8046SYuri Pankov * MODE_TOK: Like MODE_SUB, but also check the token against the list 361*c66b8046SYuri Pankov * of tokens, and if there is a match, return that token. Otherwise, 362*c66b8046SYuri Pankov * if the token matches a symbol, return EQN_TOK_SYM; if it matches 363*c66b8046SYuri Pankov * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for 364*c66b8046SYuri Pankov * a token match, *ep->start is set to an allocated string that the 365*c66b8046SYuri Pankov * caller is expected to free. 366*c66b8046SYuri Pankov * All modes skip whitespace following the end of the token. 367260e9a87SYuri Pankov */ 368260e9a87SYuri Pankov static enum eqn_tok 369*c66b8046SYuri Pankov eqn_next(struct eqn_node *ep, enum parse_mode mode) 370260e9a87SYuri Pankov { 371*c66b8046SYuri Pankov static int last_len, lim; 372260e9a87SYuri Pankov 373*c66b8046SYuri Pankov struct eqn_def *def; 374*c66b8046SYuri Pankov size_t start; 375*c66b8046SYuri Pankov int diff, i, quoted; 376*c66b8046SYuri Pankov enum eqn_tok tok; 377260e9a87SYuri Pankov 378*c66b8046SYuri Pankov /* 379*c66b8046SYuri Pankov * Reset the recursion counter after advancing 380*c66b8046SYuri Pankov * beyond the end of the previous substitution. 381*c66b8046SYuri Pankov */ 382*c66b8046SYuri Pankov if (ep->end - ep->data >= last_len) 383*c66b8046SYuri Pankov lim = 0; 384260e9a87SYuri Pankov 385*c66b8046SYuri Pankov ep->start = ep->end; 386*c66b8046SYuri Pankov quoted = mode == MODE_QUOTED; 387*c66b8046SYuri Pankov for (;;) { 388*c66b8046SYuri Pankov switch (*ep->start) { 389*c66b8046SYuri Pankov case '\0': 390*c66b8046SYuri Pankov ep->toksz = 0; 391371584c2SYuri Pankov return EQN_TOK_EOF; 392*c66b8046SYuri Pankov case '"': 393*c66b8046SYuri Pankov quoted = 1; 394*c66b8046SYuri Pankov break; 395*c66b8046SYuri Pankov default: 396*c66b8046SYuri Pankov break; 397*c66b8046SYuri Pankov } 398260e9a87SYuri Pankov if (quoted) { 399*c66b8046SYuri Pankov ep->end = strchr(ep->start + 1, *ep->start); 400*c66b8046SYuri Pankov ep->start++; /* Skip opening quote. */ 401*c66b8046SYuri Pankov if (ep->end == NULL) { 402*c66b8046SYuri Pankov mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, 403*c66b8046SYuri Pankov ep->node->line, ep->node->pos, NULL); 404*c66b8046SYuri Pankov ep->end = strchr(ep->start, '\0'); 405*c66b8046SYuri Pankov } 406*c66b8046SYuri Pankov } else { 407*c66b8046SYuri Pankov ep->end = ep->start + 1; 408*c66b8046SYuri Pankov if (*ep->start != '{' && *ep->start != '}') 409*c66b8046SYuri Pankov ep->end += strcspn(ep->end, " ^~\"{}\t"); 410*c66b8046SYuri Pankov } 411*c66b8046SYuri Pankov ep->toksz = ep->end - ep->start; 412*c66b8046SYuri Pankov if (quoted && *ep->end != '\0') 413*c66b8046SYuri Pankov ep->end++; /* Skip closing quote. */ 414*c66b8046SYuri Pankov while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL) 415*c66b8046SYuri Pankov ep->end++; 416*c66b8046SYuri Pankov if (quoted) /* Cannot return, may have to strndup. */ 417*c66b8046SYuri Pankov break; 418*c66b8046SYuri Pankov if (mode == MODE_NOSUB) 419*c66b8046SYuri Pankov return EQN_TOK__MAX; 420*c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) 421*c66b8046SYuri Pankov break; 422*c66b8046SYuri Pankov if (++lim > EQN_NEST_MAX) { 423*c66b8046SYuri Pankov mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, 424*c66b8046SYuri Pankov ep->node->line, ep->node->pos, NULL); 425*c66b8046SYuri Pankov return EQN_TOK_EOF; 426*c66b8046SYuri Pankov } 427*c66b8046SYuri Pankov 428*c66b8046SYuri Pankov /* Replace a defined name with its string value. */ 429*c66b8046SYuri Pankov if ((diff = def->valsz - ep->toksz) > 0) { 430*c66b8046SYuri Pankov start = ep->start - ep->data; 431*c66b8046SYuri Pankov ep->sz += diff; 432*c66b8046SYuri Pankov ep->data = mandoc_realloc(ep->data, ep->sz + 1); 433*c66b8046SYuri Pankov ep->start = ep->data + start; 434*c66b8046SYuri Pankov } 435*c66b8046SYuri Pankov if (diff) 436*c66b8046SYuri Pankov memmove(ep->start + def->valsz, ep->start + ep->toksz, 437*c66b8046SYuri Pankov strlen(ep->start + ep->toksz) + 1); 438*c66b8046SYuri Pankov memcpy(ep->start, def->val, def->valsz); 439*c66b8046SYuri Pankov last_len = ep->start - ep->data + def->valsz; 440*c66b8046SYuri Pankov } 441*c66b8046SYuri Pankov if (mode != MODE_TOK) 442*c66b8046SYuri Pankov return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX; 443*c66b8046SYuri Pankov if (quoted) { 444*c66b8046SYuri Pankov ep->start = mandoc_strndup(ep->start, ep->toksz); 445*c66b8046SYuri Pankov return EQN_TOK_QUOTED; 446*c66b8046SYuri Pankov } 447*c66b8046SYuri Pankov for (tok = 0; tok < EQN_TOK__MAX; tok++) 448*c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, 449*c66b8046SYuri Pankov eqn_toks[tok], strlen(eqn_toks[tok]))) 450*c66b8046SYuri Pankov return tok; 451*c66b8046SYuri Pankov 452*c66b8046SYuri Pankov for (i = 0; i < EQNSYM__MAX; i++) { 453*c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, 454*c66b8046SYuri Pankov eqnsyms[i].str, strlen(eqnsyms[i].str))) { 455*c66b8046SYuri Pankov mandoc_asprintf(&ep->start, 456*c66b8046SYuri Pankov "\\[%s]", eqnsyms[i].sym); 457*c66b8046SYuri Pankov return EQN_TOK_SYM; 458*c66b8046SYuri Pankov } 459*c66b8046SYuri Pankov } 460*c66b8046SYuri Pankov ep->start = mandoc_strndup(ep->start, ep->toksz); 461*c66b8046SYuri Pankov for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++) 462*c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, 463*c66b8046SYuri Pankov eqn_func[i], strlen(eqn_func[i]))) 464*c66b8046SYuri Pankov return EQN_TOK_FUNC; 465371584c2SYuri Pankov return EQN_TOK__MAX; 466260e9a87SYuri Pankov } 467260e9a87SYuri Pankov 468*c66b8046SYuri Pankov void 469260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp) 47095c635efSGarrett D'Amore { 47195c635efSGarrett D'Amore 472260e9a87SYuri Pankov if (bp->first) 473260e9a87SYuri Pankov eqn_box_free(bp->first); 474260e9a87SYuri Pankov if (bp->next) 475260e9a87SYuri Pankov eqn_box_free(bp->next); 47695c635efSGarrett D'Amore 477260e9a87SYuri Pankov free(bp->text); 478260e9a87SYuri Pankov free(bp->left); 479260e9a87SYuri Pankov free(bp->right); 480260e9a87SYuri Pankov free(bp->top); 481260e9a87SYuri Pankov free(bp->bottom); 482260e9a87SYuri Pankov free(bp); 48395c635efSGarrett D'Amore } 48495c635efSGarrett D'Amore 485260e9a87SYuri Pankov /* 486260e9a87SYuri Pankov * Allocate a box as the last child of the parent node. 487260e9a87SYuri Pankov */ 488260e9a87SYuri Pankov static struct eqn_box * 489260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) 490260e9a87SYuri Pankov { 491260e9a87SYuri Pankov struct eqn_box *bp; 492260e9a87SYuri Pankov 493260e9a87SYuri Pankov bp = mandoc_calloc(1, sizeof(struct eqn_box)); 494260e9a87SYuri Pankov bp->parent = parent; 495260e9a87SYuri Pankov bp->parent->args++; 496260e9a87SYuri Pankov bp->expectargs = UINT_MAX; 497*c66b8046SYuri Pankov bp->font = bp->parent->font; 498260e9a87SYuri Pankov bp->size = ep->gsize; 499260e9a87SYuri Pankov 500260e9a87SYuri Pankov if (NULL != parent->first) { 501260e9a87SYuri Pankov parent->last->next = bp; 502260e9a87SYuri Pankov bp->prev = parent->last; 503260e9a87SYuri Pankov } else 504260e9a87SYuri Pankov parent->first = bp; 505260e9a87SYuri Pankov 506260e9a87SYuri Pankov parent->last = bp; 507371584c2SYuri Pankov return bp; 508260e9a87SYuri Pankov } 509260e9a87SYuri Pankov 510260e9a87SYuri Pankov /* 511260e9a87SYuri Pankov * Reparent the current last node (of the current parent) under a new 512260e9a87SYuri Pankov * EQN_SUBEXPR as the first element. 513260e9a87SYuri Pankov * Then return the new parent. 514260e9a87SYuri Pankov * The new EQN_SUBEXPR will have a two-child limit. 515260e9a87SYuri Pankov */ 516260e9a87SYuri Pankov static struct eqn_box * 517*c66b8046SYuri Pankov eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent) 518260e9a87SYuri Pankov { 519260e9a87SYuri Pankov struct eqn_box *b, *newb; 520260e9a87SYuri Pankov 521260e9a87SYuri Pankov assert(NULL != parent->last); 522260e9a87SYuri Pankov b = parent->last; 523260e9a87SYuri Pankov if (parent->last == parent->first) 524260e9a87SYuri Pankov parent->first = NULL; 525260e9a87SYuri Pankov parent->args--; 526260e9a87SYuri Pankov parent->last = b->prev; 527260e9a87SYuri Pankov b->prev = NULL; 528260e9a87SYuri Pankov newb = eqn_box_alloc(ep, parent); 529260e9a87SYuri Pankov newb->type = EQN_SUBEXPR; 530260e9a87SYuri Pankov newb->expectargs = 2; 531260e9a87SYuri Pankov newb->args = 1; 532260e9a87SYuri Pankov newb->first = newb->last = b; 533260e9a87SYuri Pankov newb->first->next = NULL; 534260e9a87SYuri Pankov b->parent = newb; 535371584c2SYuri Pankov return newb; 536260e9a87SYuri Pankov } 537260e9a87SYuri Pankov 538260e9a87SYuri Pankov /* 539260e9a87SYuri Pankov * Parse the "delim" control statement. 540260e9a87SYuri Pankov */ 541260e9a87SYuri Pankov static void 542260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep) 543260e9a87SYuri Pankov { 544*c66b8046SYuri Pankov if (ep->end[0] == '\0' || ep->end[1] == '\0') { 545260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 546*c66b8046SYuri Pankov ep->node->line, ep->node->pos, "delim"); 547*c66b8046SYuri Pankov if (ep->end[0] != '\0') 548*c66b8046SYuri Pankov ep->end++; 549*c66b8046SYuri Pankov } else if (strncmp(ep->end, "off", 3) == 0) { 550260e9a87SYuri Pankov ep->delim = 0; 551*c66b8046SYuri Pankov ep->end += 3; 552*c66b8046SYuri Pankov } else if (strncmp(ep->end, "on", 2) == 0) { 553260e9a87SYuri Pankov if (ep->odelim && ep->cdelim) 554260e9a87SYuri Pankov ep->delim = 1; 555*c66b8046SYuri Pankov ep->end += 2; 556*c66b8046SYuri Pankov } else { 557*c66b8046SYuri Pankov ep->odelim = *ep->end++; 558*c66b8046SYuri Pankov ep->cdelim = *ep->end++; 559260e9a87SYuri Pankov ep->delim = 1; 560260e9a87SYuri Pankov } 561260e9a87SYuri Pankov } 562260e9a87SYuri Pankov 563260e9a87SYuri Pankov /* 564260e9a87SYuri Pankov * Undefine a previously-defined string. 565260e9a87SYuri Pankov */ 566260e9a87SYuri Pankov static void 567260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep) 568260e9a87SYuri Pankov { 569260e9a87SYuri Pankov struct eqn_def *def; 570260e9a87SYuri Pankov 571*c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { 572260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 573*c66b8046SYuri Pankov ep->node->line, ep->node->pos, "undef"); 574260e9a87SYuri Pankov return; 575260e9a87SYuri Pankov } 576*c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) 577260e9a87SYuri Pankov return; 578260e9a87SYuri Pankov free(def->key); 579260e9a87SYuri Pankov free(def->val); 580260e9a87SYuri Pankov def->key = def->val = NULL; 581260e9a87SYuri Pankov def->keysz = def->valsz = 0; 582260e9a87SYuri Pankov } 583260e9a87SYuri Pankov 584260e9a87SYuri Pankov static void 585260e9a87SYuri Pankov eqn_def(struct eqn_node *ep) 58695c635efSGarrett D'Amore { 58795c635efSGarrett D'Amore struct eqn_def *def; 58895c635efSGarrett D'Amore int i; 58995c635efSGarrett D'Amore 590*c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { 591260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 592*c66b8046SYuri Pankov ep->node->line, ep->node->pos, "define"); 593260e9a87SYuri Pankov return; 59495c635efSGarrett D'Amore } 59595c635efSGarrett D'Amore 59695c635efSGarrett D'Amore /* 59795c635efSGarrett D'Amore * Search for a key that already exists. 59895c635efSGarrett D'Amore * Create a new key if none is found. 59995c635efSGarrett D'Amore */ 600*c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) { 60195c635efSGarrett D'Amore /* Find holes in string array. */ 60295c635efSGarrett D'Amore for (i = 0; i < (int)ep->defsz; i++) 60395c635efSGarrett D'Amore if (0 == ep->defs[i].keysz) 60495c635efSGarrett D'Amore break; 60595c635efSGarrett D'Amore 60695c635efSGarrett D'Amore if (i == (int)ep->defsz) { 60795c635efSGarrett D'Amore ep->defsz++; 608260e9a87SYuri Pankov ep->defs = mandoc_reallocarray(ep->defs, 609260e9a87SYuri Pankov ep->defsz, sizeof(struct eqn_def)); 61095c635efSGarrett D'Amore ep->defs[i].key = ep->defs[i].val = NULL; 61195c635efSGarrett D'Amore } 61295c635efSGarrett D'Amore 613260e9a87SYuri Pankov def = ep->defs + i; 614260e9a87SYuri Pankov free(def->key); 615*c66b8046SYuri Pankov def->key = mandoc_strndup(ep->start, ep->toksz); 616*c66b8046SYuri Pankov def->keysz = ep->toksz; 61795c635efSGarrett D'Amore } 61895c635efSGarrett D'Amore 619*c66b8046SYuri Pankov if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { 620260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, 621*c66b8046SYuri Pankov ep->node->line, ep->node->pos, "define %s", def->key); 622260e9a87SYuri Pankov free(def->key); 623260e9a87SYuri Pankov free(def->val); 624260e9a87SYuri Pankov def->key = def->val = NULL; 625260e9a87SYuri Pankov def->keysz = def->valsz = 0; 626260e9a87SYuri Pankov return; 62795c635efSGarrett D'Amore } 628260e9a87SYuri Pankov free(def->val); 629*c66b8046SYuri Pankov def->val = mandoc_strndup(ep->start, ep->toksz); 630*c66b8046SYuri Pankov def->valsz = ep->toksz; 63195c635efSGarrett D'Amore } 63295c635efSGarrett D'Amore 633*c66b8046SYuri Pankov void 634*c66b8046SYuri Pankov eqn_parse(struct eqn_node *ep) 63595c635efSGarrett D'Amore { 636*c66b8046SYuri Pankov struct eqn_box *cur, *nbox, *parent, *split; 637*c66b8046SYuri Pankov const char *cp, *cpn; 638260e9a87SYuri Pankov char *p; 639*c66b8046SYuri Pankov enum eqn_tok tok; 640*c66b8046SYuri Pankov enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; 641260e9a87SYuri Pankov int size; 64295c635efSGarrett D'Amore 643*c66b8046SYuri Pankov parent = ep->node->eqn; 644260e9a87SYuri Pankov assert(parent != NULL); 645260e9a87SYuri Pankov 646260e9a87SYuri Pankov /* 647260e9a87SYuri Pankov * Empty equation. 648260e9a87SYuri Pankov * Do not add it to the high-level syntax tree. 649260e9a87SYuri Pankov */ 650260e9a87SYuri Pankov 651260e9a87SYuri Pankov if (ep->data == NULL) 652*c66b8046SYuri Pankov return; 653*c66b8046SYuri Pankov 654*c66b8046SYuri Pankov ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); 655260e9a87SYuri Pankov 656260e9a87SYuri Pankov next_tok: 657*c66b8046SYuri Pankov tok = eqn_next(ep, MODE_TOK); 658260e9a87SYuri Pankov switch (tok) { 659*c66b8046SYuri Pankov case EQN_TOK_UNDEF: 660260e9a87SYuri Pankov eqn_undef(ep); 661260e9a87SYuri Pankov break; 662*c66b8046SYuri Pankov case EQN_TOK_NDEFINE: 663*c66b8046SYuri Pankov case EQN_TOK_DEFINE: 664260e9a87SYuri Pankov eqn_def(ep); 665260e9a87SYuri Pankov break; 666*c66b8046SYuri Pankov case EQN_TOK_TDEFINE: 667*c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || 668*c66b8046SYuri Pankov eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) 669260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 670*c66b8046SYuri Pankov ep->node->line, ep->node->pos, "tdefine"); 671260e9a87SYuri Pankov break; 672*c66b8046SYuri Pankov case EQN_TOK_DELIM: 673260e9a87SYuri Pankov eqn_delim(ep); 674260e9a87SYuri Pankov break; 675*c66b8046SYuri Pankov case EQN_TOK_GFONT: 676*c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) 677260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 678*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 679260e9a87SYuri Pankov break; 680*c66b8046SYuri Pankov case EQN_TOK_MARK: 681*c66b8046SYuri Pankov case EQN_TOK_LINEUP: 682260e9a87SYuri Pankov /* Ignore these. */ 683260e9a87SYuri Pankov break; 684*c66b8046SYuri Pankov case EQN_TOK_DYAD: 685*c66b8046SYuri Pankov case EQN_TOK_VEC: 686*c66b8046SYuri Pankov case EQN_TOK_UNDER: 687*c66b8046SYuri Pankov case EQN_TOK_BAR: 688*c66b8046SYuri Pankov case EQN_TOK_TILDE: 689*c66b8046SYuri Pankov case EQN_TOK_HAT: 690*c66b8046SYuri Pankov case EQN_TOK_DOT: 691*c66b8046SYuri Pankov case EQN_TOK_DOTDOT: 692260e9a87SYuri Pankov if (parent->last == NULL) { 693260e9a87SYuri Pankov mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, 694*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 695260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 696260e9a87SYuri Pankov cur->type = EQN_TEXT; 697260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 69895c635efSGarrett D'Amore } 699*c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent); 700*c66b8046SYuri Pankov parent->type = EQN_LIST; 701260e9a87SYuri Pankov parent->expectargs = 1; 702*c66b8046SYuri Pankov parent->font = EQNFONT_ROMAN; 703260e9a87SYuri Pankov switch (tok) { 704*c66b8046SYuri Pankov case EQN_TOK_DOTDOT: 705*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[ad]"); 706260e9a87SYuri Pankov break; 707*c66b8046SYuri Pankov case EQN_TOK_VEC: 708*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[->]"); 709260e9a87SYuri Pankov break; 710*c66b8046SYuri Pankov case EQN_TOK_DYAD: 711*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[<>]"); 712260e9a87SYuri Pankov break; 713*c66b8046SYuri Pankov case EQN_TOK_TILDE: 714*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[a~]"); 715260e9a87SYuri Pankov break; 716*c66b8046SYuri Pankov case EQN_TOK_UNDER: 717*c66b8046SYuri Pankov parent->bottom = mandoc_strdup("\\[ul]"); 718260e9a87SYuri Pankov break; 719*c66b8046SYuri Pankov case EQN_TOK_BAR: 720*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[rn]"); 721260e9a87SYuri Pankov break; 722*c66b8046SYuri Pankov case EQN_TOK_DOT: 723*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[a.]"); 724260e9a87SYuri Pankov break; 725*c66b8046SYuri Pankov case EQN_TOK_HAT: 726*c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[ha]"); 727260e9a87SYuri Pankov break; 728260e9a87SYuri Pankov default: 729260e9a87SYuri Pankov abort(); 730260e9a87SYuri Pankov } 731260e9a87SYuri Pankov parent = parent->parent; 732260e9a87SYuri Pankov break; 733*c66b8046SYuri Pankov case EQN_TOK_FWD: 734*c66b8046SYuri Pankov case EQN_TOK_BACK: 735*c66b8046SYuri Pankov case EQN_TOK_DOWN: 736*c66b8046SYuri Pankov case EQN_TOK_UP: 737*c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) 738260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 739*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 740260e9a87SYuri Pankov break; 741*c66b8046SYuri Pankov case EQN_TOK_FAT: 742*c66b8046SYuri Pankov case EQN_TOK_ROMAN: 743*c66b8046SYuri Pankov case EQN_TOK_ITALIC: 744*c66b8046SYuri Pankov case EQN_TOK_BOLD: 745260e9a87SYuri Pankov while (parent->args == parent->expectargs) 746260e9a87SYuri Pankov parent = parent->parent; 747260e9a87SYuri Pankov /* 748260e9a87SYuri Pankov * These values apply to the next word or sequence of 749260e9a87SYuri Pankov * words; thus, we mark that we'll have a child with 750260e9a87SYuri Pankov * exactly one of those. 751260e9a87SYuri Pankov */ 752260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 753*c66b8046SYuri Pankov parent->type = EQN_LIST; 754260e9a87SYuri Pankov parent->expectargs = 1; 755260e9a87SYuri Pankov switch (tok) { 756*c66b8046SYuri Pankov case EQN_TOK_FAT: 757260e9a87SYuri Pankov parent->font = EQNFONT_FAT; 758260e9a87SYuri Pankov break; 759*c66b8046SYuri Pankov case EQN_TOK_ROMAN: 760260e9a87SYuri Pankov parent->font = EQNFONT_ROMAN; 761260e9a87SYuri Pankov break; 762*c66b8046SYuri Pankov case EQN_TOK_ITALIC: 763260e9a87SYuri Pankov parent->font = EQNFONT_ITALIC; 764260e9a87SYuri Pankov break; 765*c66b8046SYuri Pankov case EQN_TOK_BOLD: 766260e9a87SYuri Pankov parent->font = EQNFONT_BOLD; 767260e9a87SYuri Pankov break; 768260e9a87SYuri Pankov default: 769260e9a87SYuri Pankov abort(); 770260e9a87SYuri Pankov } 771260e9a87SYuri Pankov break; 772*c66b8046SYuri Pankov case EQN_TOK_SIZE: 773*c66b8046SYuri Pankov case EQN_TOK_GSIZE: 774260e9a87SYuri Pankov /* Accept two values: integral size and a single. */ 775*c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 776260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 777*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 778260e9a87SYuri Pankov break; 779260e9a87SYuri Pankov } 780*c66b8046SYuri Pankov size = mandoc_strntoi(ep->start, ep->toksz, 10); 781260e9a87SYuri Pankov if (-1 == size) { 782260e9a87SYuri Pankov mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, 783*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 784260e9a87SYuri Pankov break; 785260e9a87SYuri Pankov } 786260e9a87SYuri Pankov if (EQN_TOK_GSIZE == tok) { 787260e9a87SYuri Pankov ep->gsize = size; 788260e9a87SYuri Pankov break; 789260e9a87SYuri Pankov } 790*c66b8046SYuri Pankov while (parent->args == parent->expectargs) 791*c66b8046SYuri Pankov parent = parent->parent; 792260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 793*c66b8046SYuri Pankov parent->type = EQN_LIST; 794260e9a87SYuri Pankov parent->expectargs = 1; 795260e9a87SYuri Pankov parent->size = size; 796260e9a87SYuri Pankov break; 797*c66b8046SYuri Pankov case EQN_TOK_FROM: 798*c66b8046SYuri Pankov case EQN_TOK_TO: 799*c66b8046SYuri Pankov case EQN_TOK_SUB: 800*c66b8046SYuri Pankov case EQN_TOK_SUP: 801260e9a87SYuri Pankov /* 802260e9a87SYuri Pankov * We have a left-right-associative expression. 803260e9a87SYuri Pankov * Repivot under a positional node, open a child scope 804260e9a87SYuri Pankov * and keep on reading. 805260e9a87SYuri Pankov */ 806260e9a87SYuri Pankov if (parent->last == NULL) { 807260e9a87SYuri Pankov mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, 808*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 809260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 810260e9a87SYuri Pankov cur->type = EQN_TEXT; 811260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 812260e9a87SYuri Pankov } 813*c66b8046SYuri Pankov while (parent->expectargs == 1 && parent->args == 1) 814*c66b8046SYuri Pankov parent = parent->parent; 815*c66b8046SYuri Pankov if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) { 816*c66b8046SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 817*c66b8046SYuri Pankov if (cur->pos == EQNPOS_SUB || 818*c66b8046SYuri Pankov cur->pos == EQNPOS_SUP || 819*c66b8046SYuri Pankov cur->pos == EQNPOS_SUBSUP || 820*c66b8046SYuri Pankov cur->pos == EQNPOS_SQRT || 821*c66b8046SYuri Pankov cur->pos == EQNPOS_OVER) 822*c66b8046SYuri Pankov break; 823*c66b8046SYuri Pankov if (cur != NULL) 824*c66b8046SYuri Pankov parent = cur->parent; 825*c66b8046SYuri Pankov } 826*c66b8046SYuri Pankov if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) { 827260e9a87SYuri Pankov parent->expectargs = 3; 828260e9a87SYuri Pankov parent->pos = EQNPOS_SUBSUP; 829260e9a87SYuri Pankov break; 830260e9a87SYuri Pankov } 831*c66b8046SYuri Pankov if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) { 832260e9a87SYuri Pankov parent->expectargs = 3; 833260e9a87SYuri Pankov parent->pos = EQNPOS_FROMTO; 834260e9a87SYuri Pankov break; 835260e9a87SYuri Pankov } 836*c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent); 837260e9a87SYuri Pankov switch (tok) { 838*c66b8046SYuri Pankov case EQN_TOK_FROM: 839*c66b8046SYuri Pankov parent->pos = EQNPOS_FROM; 840260e9a87SYuri Pankov break; 841*c66b8046SYuri Pankov case EQN_TOK_TO: 842*c66b8046SYuri Pankov parent->pos = EQNPOS_TO; 843260e9a87SYuri Pankov break; 844*c66b8046SYuri Pankov case EQN_TOK_SUP: 845*c66b8046SYuri Pankov parent->pos = EQNPOS_SUP; 846260e9a87SYuri Pankov break; 847*c66b8046SYuri Pankov case EQN_TOK_SUB: 848*c66b8046SYuri Pankov parent->pos = EQNPOS_SUB; 849260e9a87SYuri Pankov break; 850260e9a87SYuri Pankov default: 851260e9a87SYuri Pankov abort(); 852260e9a87SYuri Pankov } 853260e9a87SYuri Pankov break; 854*c66b8046SYuri Pankov case EQN_TOK_SQRT: 855260e9a87SYuri Pankov while (parent->args == parent->expectargs) 856260e9a87SYuri Pankov parent = parent->parent; 857260e9a87SYuri Pankov /* 858260e9a87SYuri Pankov * Accept a left-right-associative set of arguments just 859260e9a87SYuri Pankov * like sub and sup and friends but without rebalancing 860260e9a87SYuri Pankov * under a pivot. 861260e9a87SYuri Pankov */ 862260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 863260e9a87SYuri Pankov parent->type = EQN_SUBEXPR; 864260e9a87SYuri Pankov parent->pos = EQNPOS_SQRT; 865260e9a87SYuri Pankov parent->expectargs = 1; 866260e9a87SYuri Pankov break; 867*c66b8046SYuri Pankov case EQN_TOK_OVER: 868260e9a87SYuri Pankov /* 869260e9a87SYuri Pankov * We have a right-left-associative fraction. 870260e9a87SYuri Pankov * Close out anything that's currently open, then 871260e9a87SYuri Pankov * rebalance and continue reading. 872260e9a87SYuri Pankov */ 873260e9a87SYuri Pankov if (parent->last == NULL) { 874260e9a87SYuri Pankov mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, 875*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 876260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 877260e9a87SYuri Pankov cur->type = EQN_TEXT; 878260e9a87SYuri Pankov cur->text = mandoc_strdup(""); 879260e9a87SYuri Pankov } 880*c66b8046SYuri Pankov while (parent->args == parent->expectargs) 881*c66b8046SYuri Pankov parent = parent->parent; 882260e9a87SYuri Pankov while (EQN_SUBEXPR == parent->type) 883260e9a87SYuri Pankov parent = parent->parent; 884*c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent); 885*c66b8046SYuri Pankov parent->pos = EQNPOS_OVER; 886260e9a87SYuri Pankov break; 887*c66b8046SYuri Pankov case EQN_TOK_RIGHT: 888*c66b8046SYuri Pankov case EQN_TOK_BRACE_CLOSE: 889260e9a87SYuri Pankov /* 890260e9a87SYuri Pankov * Close out the existing brace. 891260e9a87SYuri Pankov * FIXME: this is a shitty sentinel: we should really 892260e9a87SYuri Pankov * have a native EQN_BRACE type or whatnot. 893260e9a87SYuri Pankov */ 894260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 895260e9a87SYuri Pankov if (cur->type == EQN_LIST && 896*c66b8046SYuri Pankov cur->expectargs > 1 && 897260e9a87SYuri Pankov (tok == EQN_TOK_BRACE_CLOSE || 898260e9a87SYuri Pankov cur->left != NULL)) 899260e9a87SYuri Pankov break; 900260e9a87SYuri Pankov if (cur == NULL) { 901260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, 902*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 903260e9a87SYuri Pankov break; 904260e9a87SYuri Pankov } 905260e9a87SYuri Pankov parent = cur; 906260e9a87SYuri Pankov if (EQN_TOK_RIGHT == tok) { 907*c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 908260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, 909*c66b8046SYuri Pankov ep->parse, ep->node->line, 910*c66b8046SYuri Pankov ep->node->pos, eqn_toks[tok]); 911260e9a87SYuri Pankov break; 912260e9a87SYuri Pankov } 913260e9a87SYuri Pankov /* Handling depends on right/left. */ 914*c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) 915*c66b8046SYuri Pankov parent->right = mandoc_strdup("\\[rc]"); 916*c66b8046SYuri Pankov else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) 917*c66b8046SYuri Pankov parent->right = mandoc_strdup("\\[rf]"); 918*c66b8046SYuri Pankov else 919*c66b8046SYuri Pankov parent->right = 920*c66b8046SYuri Pankov mandoc_strndup(ep->start, ep->toksz); 921260e9a87SYuri Pankov } 922260e9a87SYuri Pankov parent = parent->parent; 923371584c2SYuri Pankov if (tok == EQN_TOK_BRACE_CLOSE && 924260e9a87SYuri Pankov (parent->type == EQN_PILE || 925260e9a87SYuri Pankov parent->type == EQN_MATRIX)) 926260e9a87SYuri Pankov parent = parent->parent; 927260e9a87SYuri Pankov /* Close out any "singleton" lists. */ 928*c66b8046SYuri Pankov while (parent->type == EQN_LIST && 929*c66b8046SYuri Pankov parent->expectargs == 1 && 930*c66b8046SYuri Pankov parent->args == 1) 931260e9a87SYuri Pankov parent = parent->parent; 932260e9a87SYuri Pankov break; 933*c66b8046SYuri Pankov case EQN_TOK_BRACE_OPEN: 934*c66b8046SYuri Pankov case EQN_TOK_LEFT: 935260e9a87SYuri Pankov /* 936260e9a87SYuri Pankov * If we already have something in the stack and we're 937260e9a87SYuri Pankov * in an expression, then rewind til we're not any more 938260e9a87SYuri Pankov * (just like with the text node). 939260e9a87SYuri Pankov */ 940260e9a87SYuri Pankov while (parent->args == parent->expectargs) 941260e9a87SYuri Pankov parent = parent->parent; 942260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok && 943*c66b8046SYuri Pankov eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 944260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, 945*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 946260e9a87SYuri Pankov break; 947260e9a87SYuri Pankov } 948260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 949260e9a87SYuri Pankov parent->type = EQN_LIST; 950260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok) { 951*c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) 952*c66b8046SYuri Pankov parent->left = mandoc_strdup("\\[lc]"); 953*c66b8046SYuri Pankov else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) 954*c66b8046SYuri Pankov parent->left = mandoc_strdup("\\[lf]"); 955*c66b8046SYuri Pankov else 956*c66b8046SYuri Pankov parent->left = 957*c66b8046SYuri Pankov mandoc_strndup(ep->start, ep->toksz); 958260e9a87SYuri Pankov } 959260e9a87SYuri Pankov break; 960*c66b8046SYuri Pankov case EQN_TOK_PILE: 961*c66b8046SYuri Pankov case EQN_TOK_LPILE: 962*c66b8046SYuri Pankov case EQN_TOK_RPILE: 963*c66b8046SYuri Pankov case EQN_TOK_CPILE: 964*c66b8046SYuri Pankov case EQN_TOK_CCOL: 965*c66b8046SYuri Pankov case EQN_TOK_LCOL: 966*c66b8046SYuri Pankov case EQN_TOK_RCOL: 967260e9a87SYuri Pankov while (parent->args == parent->expectargs) 968260e9a87SYuri Pankov parent = parent->parent; 969260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 970260e9a87SYuri Pankov parent->type = EQN_PILE; 971260e9a87SYuri Pankov parent->expectargs = 1; 972260e9a87SYuri Pankov break; 973*c66b8046SYuri Pankov case EQN_TOK_ABOVE: 974260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent) 975260e9a87SYuri Pankov if (cur->type == EQN_PILE) 976260e9a87SYuri Pankov break; 977260e9a87SYuri Pankov if (cur == NULL) { 978260e9a87SYuri Pankov mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, 979*c66b8046SYuri Pankov ep->node->line, ep->node->pos, eqn_toks[tok]); 980260e9a87SYuri Pankov break; 981260e9a87SYuri Pankov } 982260e9a87SYuri Pankov parent = eqn_box_alloc(ep, cur); 983260e9a87SYuri Pankov parent->type = EQN_LIST; 984260e9a87SYuri Pankov break; 985*c66b8046SYuri Pankov case EQN_TOK_MATRIX: 986260e9a87SYuri Pankov while (parent->args == parent->expectargs) 987260e9a87SYuri Pankov parent = parent->parent; 988260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent); 989260e9a87SYuri Pankov parent->type = EQN_MATRIX; 990260e9a87SYuri Pankov parent->expectargs = 1; 991260e9a87SYuri Pankov break; 992*c66b8046SYuri Pankov case EQN_TOK_EOF: 993*c66b8046SYuri Pankov return; 994*c66b8046SYuri Pankov case EQN_TOK__MAX: 995*c66b8046SYuri Pankov case EQN_TOK_FUNC: 996*c66b8046SYuri Pankov case EQN_TOK_QUOTED: 997*c66b8046SYuri Pankov case EQN_TOK_SYM: 998*c66b8046SYuri Pankov p = ep->start; 999*c66b8046SYuri Pankov assert(p != NULL); 1000260e9a87SYuri Pankov /* 1001260e9a87SYuri Pankov * If we already have something in the stack and we're 1002260e9a87SYuri Pankov * in an expression, then rewind til we're not any more. 1003260e9a87SYuri Pankov */ 1004260e9a87SYuri Pankov while (parent->args == parent->expectargs) 1005260e9a87SYuri Pankov parent = parent->parent; 1006260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent); 1007260e9a87SYuri Pankov cur->type = EQN_TEXT; 1008*c66b8046SYuri Pankov cur->text = p; 1009*c66b8046SYuri Pankov switch (tok) { 1010*c66b8046SYuri Pankov case EQN_TOK_FUNC: 1011*c66b8046SYuri Pankov cur->font = EQNFONT_ROMAN; 1012*c66b8046SYuri Pankov break; 1013*c66b8046SYuri Pankov case EQN_TOK_QUOTED: 1014*c66b8046SYuri Pankov if (cur->font == EQNFONT_NONE) 1015*c66b8046SYuri Pankov cur->font = EQNFONT_ITALIC; 1016*c66b8046SYuri Pankov break; 1017*c66b8046SYuri Pankov case EQN_TOK_SYM: 1018*c66b8046SYuri Pankov break; 1019*c66b8046SYuri Pankov default: 1020*c66b8046SYuri Pankov if (cur->font != EQNFONT_NONE || *p == '\0') 1021*c66b8046SYuri Pankov break; 1022*c66b8046SYuri Pankov cpn = p - 1; 1023*c66b8046SYuri Pankov ccln = CCL_LET; 1024*c66b8046SYuri Pankov split = NULL; 1025*c66b8046SYuri Pankov for (;;) { 1026*c66b8046SYuri Pankov /* Advance to next character. */ 1027*c66b8046SYuri Pankov cp = cpn++; 1028*c66b8046SYuri Pankov ccl = ccln; 1029*c66b8046SYuri Pankov ccln = isalpha((unsigned char)*cpn) ? CCL_LET : 1030*c66b8046SYuri Pankov isdigit((unsigned char)*cpn) || 1031*c66b8046SYuri Pankov (*cpn == '.' && (ccl == CCL_DIG || 1032*c66b8046SYuri Pankov isdigit((unsigned char)cpn[1]))) ? 1033*c66b8046SYuri Pankov CCL_DIG : CCL_PUN; 1034*c66b8046SYuri Pankov /* No boundary before first character. */ 1035*c66b8046SYuri Pankov if (cp < p) 1036*c66b8046SYuri Pankov continue; 1037*c66b8046SYuri Pankov cur->font = ccl == CCL_LET ? 1038*c66b8046SYuri Pankov EQNFONT_ITALIC : EQNFONT_ROMAN; 1039*c66b8046SYuri Pankov if (*cp == '\\') 1040*c66b8046SYuri Pankov mandoc_escape(&cpn, NULL, NULL); 1041*c66b8046SYuri Pankov /* No boundary after last character. */ 1042*c66b8046SYuri Pankov if (*cpn == '\0') 1043*c66b8046SYuri Pankov break; 1044*c66b8046SYuri Pankov if (ccln == ccl && *cp != ',' && *cpn != ',') 1045*c66b8046SYuri Pankov continue; 1046*c66b8046SYuri Pankov /* Boundary found, split the text. */ 1047*c66b8046SYuri Pankov if (parent->args == parent->expectargs) { 1048*c66b8046SYuri Pankov /* Remove the text from the tree. */ 1049*c66b8046SYuri Pankov if (cur->prev == NULL) 1050*c66b8046SYuri Pankov parent->first = cur->next; 1051*c66b8046SYuri Pankov else 1052*c66b8046SYuri Pankov cur->prev->next = NULL; 1053*c66b8046SYuri Pankov parent->last = cur->prev; 1054*c66b8046SYuri Pankov parent->args--; 1055*c66b8046SYuri Pankov /* Set up a list instead. */ 1056*c66b8046SYuri Pankov split = eqn_box_alloc(ep, parent); 1057*c66b8046SYuri Pankov split->type = EQN_LIST; 1058*c66b8046SYuri Pankov /* Insert the word into the list. */ 1059*c66b8046SYuri Pankov split->first = split->last = cur; 1060*c66b8046SYuri Pankov cur->parent = split; 1061*c66b8046SYuri Pankov cur->prev = NULL; 1062*c66b8046SYuri Pankov parent = split; 1063*c66b8046SYuri Pankov } 1064*c66b8046SYuri Pankov /* Append a new text box. */ 1065*c66b8046SYuri Pankov nbox = eqn_box_alloc(ep, parent); 1066*c66b8046SYuri Pankov nbox->type = EQN_TEXT; 1067*c66b8046SYuri Pankov nbox->text = mandoc_strdup(cpn); 1068*c66b8046SYuri Pankov /* Truncate the old box. */ 1069*c66b8046SYuri Pankov p = mandoc_strndup(cur->text, 1070*c66b8046SYuri Pankov cpn - cur->text); 1071*c66b8046SYuri Pankov free(cur->text); 1072*c66b8046SYuri Pankov cur->text = p; 1073*c66b8046SYuri Pankov /* Setup to process the new box. */ 1074*c66b8046SYuri Pankov cur = nbox; 1075*c66b8046SYuri Pankov p = nbox->text; 1076*c66b8046SYuri Pankov cpn = p - 1; 1077*c66b8046SYuri Pankov ccln = CCL_LET; 1078*c66b8046SYuri Pankov } 1079*c66b8046SYuri Pankov if (split != NULL) 1080*c66b8046SYuri Pankov parent = split->parent; 1081260e9a87SYuri Pankov break; 1082260e9a87SYuri Pankov } 1083260e9a87SYuri Pankov break; 1084*c66b8046SYuri Pankov default: 1085*c66b8046SYuri Pankov abort(); 1086260e9a87SYuri Pankov } 1087260e9a87SYuri Pankov goto next_tok; 1088260e9a87SYuri Pankov } 1089260e9a87SYuri Pankov 1090260e9a87SYuri Pankov void 1091260e9a87SYuri Pankov eqn_free(struct eqn_node *p) 109295c635efSGarrett D'Amore { 109395c635efSGarrett D'Amore int i; 109495c635efSGarrett D'Amore 1095260e9a87SYuri Pankov for (i = 0; i < (int)p->defsz; i++) { 1096260e9a87SYuri Pankov free(p->defs[i].key); 1097260e9a87SYuri Pankov free(p->defs[i].val); 1098260e9a87SYuri Pankov } 1099260e9a87SYuri Pankov 1100260e9a87SYuri Pankov free(p->data); 1101260e9a87SYuri Pankov free(p->defs); 1102260e9a87SYuri Pankov free(p); 110395c635efSGarrett D'Amore } 1104