1*7295610fSBaptiste Daroussin /* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4*7295610fSBaptiste Daroussin * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 2061d06d6bSBaptiste Daroussin #include <sys/types.h> 2161d06d6bSBaptiste Daroussin 2261d06d6bSBaptiste Daroussin #include <assert.h> 2361d06d6bSBaptiste Daroussin #include <ctype.h> 2461d06d6bSBaptiste Daroussin #include <limits.h> 2561d06d6bSBaptiste Daroussin #include <stdio.h> 2661d06d6bSBaptiste Daroussin #include <stdlib.h> 2761d06d6bSBaptiste Daroussin #include <string.h> 2861d06d6bSBaptiste Daroussin #include <time.h> 2961d06d6bSBaptiste Daroussin 3061d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 3161d06d6bSBaptiste Daroussin #include "mandoc.h" 3261d06d6bSBaptiste Daroussin #include "roff.h" 33*7295610fSBaptiste Daroussin #include "eqn.h" 3461d06d6bSBaptiste Daroussin #include "libmandoc.h" 35*7295610fSBaptiste Daroussin #include "eqn_parse.h" 3661d06d6bSBaptiste Daroussin 3761d06d6bSBaptiste Daroussin #define EQN_NEST_MAX 128 /* maximum nesting of defines */ 3861d06d6bSBaptiste Daroussin #define STRNEQ(p1, sz1, p2, sz2) \ 3961d06d6bSBaptiste Daroussin ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) 4061d06d6bSBaptiste Daroussin 4161d06d6bSBaptiste Daroussin enum eqn_tok { 4261d06d6bSBaptiste Daroussin EQN_TOK_DYAD = 0, 4361d06d6bSBaptiste Daroussin EQN_TOK_VEC, 4461d06d6bSBaptiste Daroussin EQN_TOK_UNDER, 4561d06d6bSBaptiste Daroussin EQN_TOK_BAR, 4661d06d6bSBaptiste Daroussin EQN_TOK_TILDE, 4761d06d6bSBaptiste Daroussin EQN_TOK_HAT, 4861d06d6bSBaptiste Daroussin EQN_TOK_DOT, 4961d06d6bSBaptiste Daroussin EQN_TOK_DOTDOT, 5061d06d6bSBaptiste Daroussin EQN_TOK_FWD, 5161d06d6bSBaptiste Daroussin EQN_TOK_BACK, 5261d06d6bSBaptiste Daroussin EQN_TOK_DOWN, 5361d06d6bSBaptiste Daroussin EQN_TOK_UP, 5461d06d6bSBaptiste Daroussin EQN_TOK_FAT, 5561d06d6bSBaptiste Daroussin EQN_TOK_ROMAN, 5661d06d6bSBaptiste Daroussin EQN_TOK_ITALIC, 5761d06d6bSBaptiste Daroussin EQN_TOK_BOLD, 5861d06d6bSBaptiste Daroussin EQN_TOK_SIZE, 5961d06d6bSBaptiste Daroussin EQN_TOK_SUB, 6061d06d6bSBaptiste Daroussin EQN_TOK_SUP, 6161d06d6bSBaptiste Daroussin EQN_TOK_SQRT, 6261d06d6bSBaptiste Daroussin EQN_TOK_OVER, 6361d06d6bSBaptiste Daroussin EQN_TOK_FROM, 6461d06d6bSBaptiste Daroussin EQN_TOK_TO, 6561d06d6bSBaptiste Daroussin EQN_TOK_BRACE_OPEN, 6661d06d6bSBaptiste Daroussin EQN_TOK_BRACE_CLOSE, 6761d06d6bSBaptiste Daroussin EQN_TOK_GSIZE, 6861d06d6bSBaptiste Daroussin EQN_TOK_GFONT, 6961d06d6bSBaptiste Daroussin EQN_TOK_MARK, 7061d06d6bSBaptiste Daroussin EQN_TOK_LINEUP, 7161d06d6bSBaptiste Daroussin EQN_TOK_LEFT, 7261d06d6bSBaptiste Daroussin EQN_TOK_RIGHT, 7361d06d6bSBaptiste Daroussin EQN_TOK_PILE, 7461d06d6bSBaptiste Daroussin EQN_TOK_LPILE, 7561d06d6bSBaptiste Daroussin EQN_TOK_RPILE, 7661d06d6bSBaptiste Daroussin EQN_TOK_CPILE, 7761d06d6bSBaptiste Daroussin EQN_TOK_MATRIX, 7861d06d6bSBaptiste Daroussin EQN_TOK_CCOL, 7961d06d6bSBaptiste Daroussin EQN_TOK_LCOL, 8061d06d6bSBaptiste Daroussin EQN_TOK_RCOL, 8161d06d6bSBaptiste Daroussin EQN_TOK_DELIM, 8261d06d6bSBaptiste Daroussin EQN_TOK_DEFINE, 8361d06d6bSBaptiste Daroussin EQN_TOK_TDEFINE, 8461d06d6bSBaptiste Daroussin EQN_TOK_NDEFINE, 8561d06d6bSBaptiste Daroussin EQN_TOK_UNDEF, 8661d06d6bSBaptiste Daroussin EQN_TOK_ABOVE, 8761d06d6bSBaptiste Daroussin EQN_TOK__MAX, 8861d06d6bSBaptiste Daroussin EQN_TOK_FUNC, 8961d06d6bSBaptiste Daroussin EQN_TOK_QUOTED, 9061d06d6bSBaptiste Daroussin EQN_TOK_SYM, 9161d06d6bSBaptiste Daroussin EQN_TOK_EOF 9261d06d6bSBaptiste Daroussin }; 9361d06d6bSBaptiste Daroussin 9461d06d6bSBaptiste Daroussin static const char *eqn_toks[EQN_TOK__MAX] = { 9561d06d6bSBaptiste Daroussin "dyad", /* EQN_TOK_DYAD */ 9661d06d6bSBaptiste Daroussin "vec", /* EQN_TOK_VEC */ 9761d06d6bSBaptiste Daroussin "under", /* EQN_TOK_UNDER */ 9861d06d6bSBaptiste Daroussin "bar", /* EQN_TOK_BAR */ 9961d06d6bSBaptiste Daroussin "tilde", /* EQN_TOK_TILDE */ 10061d06d6bSBaptiste Daroussin "hat", /* EQN_TOK_HAT */ 10161d06d6bSBaptiste Daroussin "dot", /* EQN_TOK_DOT */ 10261d06d6bSBaptiste Daroussin "dotdot", /* EQN_TOK_DOTDOT */ 10361d06d6bSBaptiste Daroussin "fwd", /* EQN_TOK_FWD * */ 10461d06d6bSBaptiste Daroussin "back", /* EQN_TOK_BACK */ 10561d06d6bSBaptiste Daroussin "down", /* EQN_TOK_DOWN */ 10661d06d6bSBaptiste Daroussin "up", /* EQN_TOK_UP */ 10761d06d6bSBaptiste Daroussin "fat", /* EQN_TOK_FAT */ 10861d06d6bSBaptiste Daroussin "roman", /* EQN_TOK_ROMAN */ 10961d06d6bSBaptiste Daroussin "italic", /* EQN_TOK_ITALIC */ 11061d06d6bSBaptiste Daroussin "bold", /* EQN_TOK_BOLD */ 11161d06d6bSBaptiste Daroussin "size", /* EQN_TOK_SIZE */ 11261d06d6bSBaptiste Daroussin "sub", /* EQN_TOK_SUB */ 11361d06d6bSBaptiste Daroussin "sup", /* EQN_TOK_SUP */ 11461d06d6bSBaptiste Daroussin "sqrt", /* EQN_TOK_SQRT */ 11561d06d6bSBaptiste Daroussin "over", /* EQN_TOK_OVER */ 11661d06d6bSBaptiste Daroussin "from", /* EQN_TOK_FROM */ 11761d06d6bSBaptiste Daroussin "to", /* EQN_TOK_TO */ 11861d06d6bSBaptiste Daroussin "{", /* EQN_TOK_BRACE_OPEN */ 11961d06d6bSBaptiste Daroussin "}", /* EQN_TOK_BRACE_CLOSE */ 12061d06d6bSBaptiste Daroussin "gsize", /* EQN_TOK_GSIZE */ 12161d06d6bSBaptiste Daroussin "gfont", /* EQN_TOK_GFONT */ 12261d06d6bSBaptiste Daroussin "mark", /* EQN_TOK_MARK */ 12361d06d6bSBaptiste Daroussin "lineup", /* EQN_TOK_LINEUP */ 12461d06d6bSBaptiste Daroussin "left", /* EQN_TOK_LEFT */ 12561d06d6bSBaptiste Daroussin "right", /* EQN_TOK_RIGHT */ 12661d06d6bSBaptiste Daroussin "pile", /* EQN_TOK_PILE */ 12761d06d6bSBaptiste Daroussin "lpile", /* EQN_TOK_LPILE */ 12861d06d6bSBaptiste Daroussin "rpile", /* EQN_TOK_RPILE */ 12961d06d6bSBaptiste Daroussin "cpile", /* EQN_TOK_CPILE */ 13061d06d6bSBaptiste Daroussin "matrix", /* EQN_TOK_MATRIX */ 13161d06d6bSBaptiste Daroussin "ccol", /* EQN_TOK_CCOL */ 13261d06d6bSBaptiste Daroussin "lcol", /* EQN_TOK_LCOL */ 13361d06d6bSBaptiste Daroussin "rcol", /* EQN_TOK_RCOL */ 13461d06d6bSBaptiste Daroussin "delim", /* EQN_TOK_DELIM */ 13561d06d6bSBaptiste Daroussin "define", /* EQN_TOK_DEFINE */ 13661d06d6bSBaptiste Daroussin "tdefine", /* EQN_TOK_TDEFINE */ 13761d06d6bSBaptiste Daroussin "ndefine", /* EQN_TOK_NDEFINE */ 13861d06d6bSBaptiste Daroussin "undef", /* EQN_TOK_UNDEF */ 13961d06d6bSBaptiste Daroussin "above", /* EQN_TOK_ABOVE */ 14061d06d6bSBaptiste Daroussin }; 14161d06d6bSBaptiste Daroussin 14261d06d6bSBaptiste Daroussin static const char *const eqn_func[] = { 14361d06d6bSBaptiste Daroussin "acos", "acsc", "and", "arc", "asec", "asin", "atan", 14461d06d6bSBaptiste Daroussin "cos", "cosh", "coth", "csc", "det", "exp", "for", 14561d06d6bSBaptiste Daroussin "if", "lim", "ln", "log", "max", "min", 14661d06d6bSBaptiste Daroussin "sec", "sin", "sinh", "tan", "tanh", "Im", "Re", 14761d06d6bSBaptiste Daroussin }; 14861d06d6bSBaptiste Daroussin 14961d06d6bSBaptiste Daroussin enum eqn_symt { 15061d06d6bSBaptiste Daroussin EQNSYM_alpha = 0, 15161d06d6bSBaptiste Daroussin EQNSYM_beta, 15261d06d6bSBaptiste Daroussin EQNSYM_chi, 15361d06d6bSBaptiste Daroussin EQNSYM_delta, 15461d06d6bSBaptiste Daroussin EQNSYM_epsilon, 15561d06d6bSBaptiste Daroussin EQNSYM_eta, 15661d06d6bSBaptiste Daroussin EQNSYM_gamma, 15761d06d6bSBaptiste Daroussin EQNSYM_iota, 15861d06d6bSBaptiste Daroussin EQNSYM_kappa, 15961d06d6bSBaptiste Daroussin EQNSYM_lambda, 16061d06d6bSBaptiste Daroussin EQNSYM_mu, 16161d06d6bSBaptiste Daroussin EQNSYM_nu, 16261d06d6bSBaptiste Daroussin EQNSYM_omega, 16361d06d6bSBaptiste Daroussin EQNSYM_omicron, 16461d06d6bSBaptiste Daroussin EQNSYM_phi, 16561d06d6bSBaptiste Daroussin EQNSYM_pi, 16661d06d6bSBaptiste Daroussin EQNSYM_ps, 16761d06d6bSBaptiste Daroussin EQNSYM_rho, 16861d06d6bSBaptiste Daroussin EQNSYM_sigma, 16961d06d6bSBaptiste Daroussin EQNSYM_tau, 17061d06d6bSBaptiste Daroussin EQNSYM_theta, 17161d06d6bSBaptiste Daroussin EQNSYM_upsilon, 17261d06d6bSBaptiste Daroussin EQNSYM_xi, 17361d06d6bSBaptiste Daroussin EQNSYM_zeta, 17461d06d6bSBaptiste Daroussin EQNSYM_DELTA, 17561d06d6bSBaptiste Daroussin EQNSYM_GAMMA, 17661d06d6bSBaptiste Daroussin EQNSYM_LAMBDA, 17761d06d6bSBaptiste Daroussin EQNSYM_OMEGA, 17861d06d6bSBaptiste Daroussin EQNSYM_PHI, 17961d06d6bSBaptiste Daroussin EQNSYM_PI, 18061d06d6bSBaptiste Daroussin EQNSYM_PSI, 18161d06d6bSBaptiste Daroussin EQNSYM_SIGMA, 18261d06d6bSBaptiste Daroussin EQNSYM_THETA, 18361d06d6bSBaptiste Daroussin EQNSYM_UPSILON, 18461d06d6bSBaptiste Daroussin EQNSYM_XI, 18561d06d6bSBaptiste Daroussin EQNSYM_inter, 18661d06d6bSBaptiste Daroussin EQNSYM_union, 18761d06d6bSBaptiste Daroussin EQNSYM_prod, 18861d06d6bSBaptiste Daroussin EQNSYM_int, 18961d06d6bSBaptiste Daroussin EQNSYM_sum, 19061d06d6bSBaptiste Daroussin EQNSYM_grad, 19161d06d6bSBaptiste Daroussin EQNSYM_del, 19261d06d6bSBaptiste Daroussin EQNSYM_times, 19361d06d6bSBaptiste Daroussin EQNSYM_cdot, 19461d06d6bSBaptiste Daroussin EQNSYM_nothing, 19561d06d6bSBaptiste Daroussin EQNSYM_approx, 19661d06d6bSBaptiste Daroussin EQNSYM_prime, 19761d06d6bSBaptiste Daroussin EQNSYM_half, 19861d06d6bSBaptiste Daroussin EQNSYM_partial, 19961d06d6bSBaptiste Daroussin EQNSYM_inf, 20061d06d6bSBaptiste Daroussin EQNSYM_muchgreat, 20161d06d6bSBaptiste Daroussin EQNSYM_muchless, 20261d06d6bSBaptiste Daroussin EQNSYM_larrow, 20361d06d6bSBaptiste Daroussin EQNSYM_rarrow, 20461d06d6bSBaptiste Daroussin EQNSYM_pm, 20561d06d6bSBaptiste Daroussin EQNSYM_nequal, 20661d06d6bSBaptiste Daroussin EQNSYM_equiv, 20761d06d6bSBaptiste Daroussin EQNSYM_lessequal, 20861d06d6bSBaptiste Daroussin EQNSYM_moreequal, 20961d06d6bSBaptiste Daroussin EQNSYM_minus, 21061d06d6bSBaptiste Daroussin EQNSYM__MAX 21161d06d6bSBaptiste Daroussin }; 21261d06d6bSBaptiste Daroussin 21361d06d6bSBaptiste Daroussin struct eqnsym { 21461d06d6bSBaptiste Daroussin const char *str; 21561d06d6bSBaptiste Daroussin const char *sym; 21661d06d6bSBaptiste Daroussin }; 21761d06d6bSBaptiste Daroussin 21861d06d6bSBaptiste Daroussin static const struct eqnsym eqnsyms[EQNSYM__MAX] = { 21961d06d6bSBaptiste Daroussin { "alpha", "*a" }, /* EQNSYM_alpha */ 22061d06d6bSBaptiste Daroussin { "beta", "*b" }, /* EQNSYM_beta */ 22161d06d6bSBaptiste Daroussin { "chi", "*x" }, /* EQNSYM_chi */ 22261d06d6bSBaptiste Daroussin { "delta", "*d" }, /* EQNSYM_delta */ 22361d06d6bSBaptiste Daroussin { "epsilon", "*e" }, /* EQNSYM_epsilon */ 22461d06d6bSBaptiste Daroussin { "eta", "*y" }, /* EQNSYM_eta */ 22561d06d6bSBaptiste Daroussin { "gamma", "*g" }, /* EQNSYM_gamma */ 22661d06d6bSBaptiste Daroussin { "iota", "*i" }, /* EQNSYM_iota */ 22761d06d6bSBaptiste Daroussin { "kappa", "*k" }, /* EQNSYM_kappa */ 22861d06d6bSBaptiste Daroussin { "lambda", "*l" }, /* EQNSYM_lambda */ 22961d06d6bSBaptiste Daroussin { "mu", "*m" }, /* EQNSYM_mu */ 23061d06d6bSBaptiste Daroussin { "nu", "*n" }, /* EQNSYM_nu */ 23161d06d6bSBaptiste Daroussin { "omega", "*w" }, /* EQNSYM_omega */ 23261d06d6bSBaptiste Daroussin { "omicron", "*o" }, /* EQNSYM_omicron */ 23361d06d6bSBaptiste Daroussin { "phi", "*f" }, /* EQNSYM_phi */ 23461d06d6bSBaptiste Daroussin { "pi", "*p" }, /* EQNSYM_pi */ 23561d06d6bSBaptiste Daroussin { "psi", "*q" }, /* EQNSYM_psi */ 23661d06d6bSBaptiste Daroussin { "rho", "*r" }, /* EQNSYM_rho */ 23761d06d6bSBaptiste Daroussin { "sigma", "*s" }, /* EQNSYM_sigma */ 23861d06d6bSBaptiste Daroussin { "tau", "*t" }, /* EQNSYM_tau */ 23961d06d6bSBaptiste Daroussin { "theta", "*h" }, /* EQNSYM_theta */ 24061d06d6bSBaptiste Daroussin { "upsilon", "*u" }, /* EQNSYM_upsilon */ 24161d06d6bSBaptiste Daroussin { "xi", "*c" }, /* EQNSYM_xi */ 24261d06d6bSBaptiste Daroussin { "zeta", "*z" }, /* EQNSYM_zeta */ 24361d06d6bSBaptiste Daroussin { "DELTA", "*D" }, /* EQNSYM_DELTA */ 24461d06d6bSBaptiste Daroussin { "GAMMA", "*G" }, /* EQNSYM_GAMMA */ 24561d06d6bSBaptiste Daroussin { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */ 24661d06d6bSBaptiste Daroussin { "OMEGA", "*W" }, /* EQNSYM_OMEGA */ 24761d06d6bSBaptiste Daroussin { "PHI", "*F" }, /* EQNSYM_PHI */ 24861d06d6bSBaptiste Daroussin { "PI", "*P" }, /* EQNSYM_PI */ 24961d06d6bSBaptiste Daroussin { "PSI", "*Q" }, /* EQNSYM_PSI */ 25061d06d6bSBaptiste Daroussin { "SIGMA", "*S" }, /* EQNSYM_SIGMA */ 25161d06d6bSBaptiste Daroussin { "THETA", "*H" }, /* EQNSYM_THETA */ 25261d06d6bSBaptiste Daroussin { "UPSILON", "*U" }, /* EQNSYM_UPSILON */ 25361d06d6bSBaptiste Daroussin { "XI", "*C" }, /* EQNSYM_XI */ 25461d06d6bSBaptiste Daroussin { "inter", "ca" }, /* EQNSYM_inter */ 25561d06d6bSBaptiste Daroussin { "union", "cu" }, /* EQNSYM_union */ 25661d06d6bSBaptiste Daroussin { "prod", "product" }, /* EQNSYM_prod */ 25761d06d6bSBaptiste Daroussin { "int", "integral" }, /* EQNSYM_int */ 25861d06d6bSBaptiste Daroussin { "sum", "sum" }, /* EQNSYM_sum */ 25961d06d6bSBaptiste Daroussin { "grad", "gr" }, /* EQNSYM_grad */ 26061d06d6bSBaptiste Daroussin { "del", "gr" }, /* EQNSYM_del */ 26161d06d6bSBaptiste Daroussin { "times", "mu" }, /* EQNSYM_times */ 26261d06d6bSBaptiste Daroussin { "cdot", "pc" }, /* EQNSYM_cdot */ 26361d06d6bSBaptiste Daroussin { "nothing", "&" }, /* EQNSYM_nothing */ 26461d06d6bSBaptiste Daroussin { "approx", "~~" }, /* EQNSYM_approx */ 26561d06d6bSBaptiste Daroussin { "prime", "fm" }, /* EQNSYM_prime */ 26661d06d6bSBaptiste Daroussin { "half", "12" }, /* EQNSYM_half */ 26761d06d6bSBaptiste Daroussin { "partial", "pd" }, /* EQNSYM_partial */ 26861d06d6bSBaptiste Daroussin { "inf", "if" }, /* EQNSYM_inf */ 26961d06d6bSBaptiste Daroussin { ">>", ">>" }, /* EQNSYM_muchgreat */ 27061d06d6bSBaptiste Daroussin { "<<", "<<" }, /* EQNSYM_muchless */ 27161d06d6bSBaptiste Daroussin { "<-", "<-" }, /* EQNSYM_larrow */ 27261d06d6bSBaptiste Daroussin { "->", "->" }, /* EQNSYM_rarrow */ 27361d06d6bSBaptiste Daroussin { "+-", "+-" }, /* EQNSYM_pm */ 27461d06d6bSBaptiste Daroussin { "!=", "!=" }, /* EQNSYM_nequal */ 27561d06d6bSBaptiste Daroussin { "==", "==" }, /* EQNSYM_equiv */ 27661d06d6bSBaptiste Daroussin { "<=", "<=" }, /* EQNSYM_lessequal */ 27761d06d6bSBaptiste Daroussin { ">=", ">=" }, /* EQNSYM_moreequal */ 27861d06d6bSBaptiste Daroussin { "-", "mi" }, /* EQNSYM_minus */ 27961d06d6bSBaptiste Daroussin }; 28061d06d6bSBaptiste Daroussin 28161d06d6bSBaptiste Daroussin enum parse_mode { 28261d06d6bSBaptiste Daroussin MODE_QUOTED, 28361d06d6bSBaptiste Daroussin MODE_NOSUB, 28461d06d6bSBaptiste Daroussin MODE_SUB, 28561d06d6bSBaptiste Daroussin MODE_TOK 28661d06d6bSBaptiste Daroussin }; 28761d06d6bSBaptiste Daroussin 288*7295610fSBaptiste Daroussin struct eqn_def { 289*7295610fSBaptiste Daroussin char *key; 290*7295610fSBaptiste Daroussin size_t keysz; 291*7295610fSBaptiste Daroussin char *val; 292*7295610fSBaptiste Daroussin size_t valsz; 293*7295610fSBaptiste Daroussin }; 294*7295610fSBaptiste Daroussin 29561d06d6bSBaptiste Daroussin static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); 29661d06d6bSBaptiste Daroussin static struct eqn_box *eqn_box_makebinary(struct eqn_node *, 29761d06d6bSBaptiste Daroussin struct eqn_box *); 29861d06d6bSBaptiste Daroussin static void eqn_def(struct eqn_node *); 29961d06d6bSBaptiste Daroussin static struct eqn_def *eqn_def_find(struct eqn_node *); 30061d06d6bSBaptiste Daroussin static void eqn_delim(struct eqn_node *); 30161d06d6bSBaptiste Daroussin static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); 30261d06d6bSBaptiste Daroussin static void eqn_undef(struct eqn_node *); 30361d06d6bSBaptiste Daroussin 30461d06d6bSBaptiste Daroussin 30561d06d6bSBaptiste Daroussin struct eqn_node * 306*7295610fSBaptiste Daroussin eqn_alloc(void) 30761d06d6bSBaptiste Daroussin { 30861d06d6bSBaptiste Daroussin struct eqn_node *ep; 30961d06d6bSBaptiste Daroussin 31061d06d6bSBaptiste Daroussin ep = mandoc_calloc(1, sizeof(*ep)); 31161d06d6bSBaptiste Daroussin ep->gsize = EQN_DEFSIZE; 31261d06d6bSBaptiste Daroussin return ep; 31361d06d6bSBaptiste Daroussin } 31461d06d6bSBaptiste Daroussin 31561d06d6bSBaptiste Daroussin void 31661d06d6bSBaptiste Daroussin eqn_reset(struct eqn_node *ep) 31761d06d6bSBaptiste Daroussin { 31861d06d6bSBaptiste Daroussin free(ep->data); 31961d06d6bSBaptiste Daroussin ep->data = ep->start = ep->end = NULL; 32061d06d6bSBaptiste Daroussin ep->sz = ep->toksz = 0; 32161d06d6bSBaptiste Daroussin } 32261d06d6bSBaptiste Daroussin 32361d06d6bSBaptiste Daroussin void 32461d06d6bSBaptiste Daroussin eqn_read(struct eqn_node *ep, const char *p) 32561d06d6bSBaptiste Daroussin { 32661d06d6bSBaptiste Daroussin char *cp; 32761d06d6bSBaptiste Daroussin 32861d06d6bSBaptiste Daroussin if (ep->data == NULL) { 32961d06d6bSBaptiste Daroussin ep->sz = strlen(p); 33061d06d6bSBaptiste Daroussin ep->data = mandoc_strdup(p); 33161d06d6bSBaptiste Daroussin } else { 33261d06d6bSBaptiste Daroussin ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p); 33361d06d6bSBaptiste Daroussin free(ep->data); 33461d06d6bSBaptiste Daroussin ep->data = cp; 33561d06d6bSBaptiste Daroussin } 33661d06d6bSBaptiste Daroussin ep->sz += 1; 33761d06d6bSBaptiste Daroussin } 33861d06d6bSBaptiste Daroussin 33961d06d6bSBaptiste Daroussin /* 34061d06d6bSBaptiste Daroussin * Find the key "key" of the give size within our eqn-defined values. 34161d06d6bSBaptiste Daroussin */ 34261d06d6bSBaptiste Daroussin static struct eqn_def * 34361d06d6bSBaptiste Daroussin eqn_def_find(struct eqn_node *ep) 34461d06d6bSBaptiste Daroussin { 34561d06d6bSBaptiste Daroussin int i; 34661d06d6bSBaptiste Daroussin 34761d06d6bSBaptiste Daroussin for (i = 0; i < (int)ep->defsz; i++) 34861d06d6bSBaptiste Daroussin if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, 34961d06d6bSBaptiste Daroussin ep->defs[i].keysz, ep->start, ep->toksz)) 35061d06d6bSBaptiste Daroussin return &ep->defs[i]; 35161d06d6bSBaptiste Daroussin 35261d06d6bSBaptiste Daroussin return NULL; 35361d06d6bSBaptiste Daroussin } 35461d06d6bSBaptiste Daroussin 35561d06d6bSBaptiste Daroussin /* 35661d06d6bSBaptiste Daroussin * Parse a token from the input text. The modes are: 35761d06d6bSBaptiste Daroussin * MODE_QUOTED: Use *ep->start as the delimiter; the token ends 35861d06d6bSBaptiste Daroussin * before its next occurence. Do not interpret the token in any 35961d06d6bSBaptiste Daroussin * way and return EQN_TOK_QUOTED. All other modes behave like 36061d06d6bSBaptiste Daroussin * MODE_QUOTED when *ep->start is '"'. 36161d06d6bSBaptiste Daroussin * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it; 36261d06d6bSBaptiste Daroussin * otherwise, it ends before the next whitespace or brace. 36361d06d6bSBaptiste Daroussin * Do not interpret the token and return EQN_TOK__MAX. 36461d06d6bSBaptiste Daroussin * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an 36561d06d6bSBaptiste Daroussin * alias created with define. If it is an alias, replace it with 36661d06d6bSBaptiste Daroussin * its string value and reparse. 36761d06d6bSBaptiste Daroussin * MODE_TOK: Like MODE_SUB, but also check the token against the list 36861d06d6bSBaptiste Daroussin * of tokens, and if there is a match, return that token. Otherwise, 36961d06d6bSBaptiste Daroussin * if the token matches a symbol, return EQN_TOK_SYM; if it matches 37061d06d6bSBaptiste Daroussin * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for 37161d06d6bSBaptiste Daroussin * a token match, *ep->start is set to an allocated string that the 37261d06d6bSBaptiste Daroussin * caller is expected to free. 37361d06d6bSBaptiste Daroussin * All modes skip whitespace following the end of the token. 37461d06d6bSBaptiste Daroussin */ 37561d06d6bSBaptiste Daroussin static enum eqn_tok 37661d06d6bSBaptiste Daroussin eqn_next(struct eqn_node *ep, enum parse_mode mode) 37761d06d6bSBaptiste Daroussin { 37861d06d6bSBaptiste Daroussin static int last_len, lim; 37961d06d6bSBaptiste Daroussin 38061d06d6bSBaptiste Daroussin struct eqn_def *def; 38161d06d6bSBaptiste Daroussin size_t start; 38261d06d6bSBaptiste Daroussin int diff, i, quoted; 38361d06d6bSBaptiste Daroussin enum eqn_tok tok; 38461d06d6bSBaptiste Daroussin 38561d06d6bSBaptiste Daroussin /* 38661d06d6bSBaptiste Daroussin * Reset the recursion counter after advancing 38761d06d6bSBaptiste Daroussin * beyond the end of the previous substitution. 38861d06d6bSBaptiste Daroussin */ 38961d06d6bSBaptiste Daroussin if (ep->end - ep->data >= last_len) 39061d06d6bSBaptiste Daroussin lim = 0; 39161d06d6bSBaptiste Daroussin 39261d06d6bSBaptiste Daroussin ep->start = ep->end; 39361d06d6bSBaptiste Daroussin quoted = mode == MODE_QUOTED; 39461d06d6bSBaptiste Daroussin for (;;) { 39561d06d6bSBaptiste Daroussin switch (*ep->start) { 39661d06d6bSBaptiste Daroussin case '\0': 39761d06d6bSBaptiste Daroussin ep->toksz = 0; 39861d06d6bSBaptiste Daroussin return EQN_TOK_EOF; 39961d06d6bSBaptiste Daroussin case '"': 40061d06d6bSBaptiste Daroussin quoted = 1; 40161d06d6bSBaptiste Daroussin break; 40261d06d6bSBaptiste Daroussin default: 40361d06d6bSBaptiste Daroussin break; 40461d06d6bSBaptiste Daroussin } 40561d06d6bSBaptiste Daroussin if (quoted) { 40661d06d6bSBaptiste Daroussin ep->end = strchr(ep->start + 1, *ep->start); 40761d06d6bSBaptiste Daroussin ep->start++; /* Skip opening quote. */ 40861d06d6bSBaptiste Daroussin if (ep->end == NULL) { 409*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_QUOTE, 41061d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, NULL); 41161d06d6bSBaptiste Daroussin ep->end = strchr(ep->start, '\0'); 41261d06d6bSBaptiste Daroussin } 41361d06d6bSBaptiste Daroussin } else { 41461d06d6bSBaptiste Daroussin ep->end = ep->start + 1; 41561d06d6bSBaptiste Daroussin if (*ep->start != '{' && *ep->start != '}') 41661d06d6bSBaptiste Daroussin ep->end += strcspn(ep->end, " ^~\"{}\t"); 41761d06d6bSBaptiste Daroussin } 41861d06d6bSBaptiste Daroussin ep->toksz = ep->end - ep->start; 41961d06d6bSBaptiste Daroussin if (quoted && *ep->end != '\0') 42061d06d6bSBaptiste Daroussin ep->end++; /* Skip closing quote. */ 42161d06d6bSBaptiste Daroussin while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL) 42261d06d6bSBaptiste Daroussin ep->end++; 42361d06d6bSBaptiste Daroussin if (quoted) /* Cannot return, may have to strndup. */ 42461d06d6bSBaptiste Daroussin break; 42561d06d6bSBaptiste Daroussin if (mode == MODE_NOSUB) 42661d06d6bSBaptiste Daroussin return EQN_TOK__MAX; 42761d06d6bSBaptiste Daroussin if ((def = eqn_def_find(ep)) == NULL) 42861d06d6bSBaptiste Daroussin break; 42961d06d6bSBaptiste Daroussin if (++lim > EQN_NEST_MAX) { 430*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ROFFLOOP, 43161d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, NULL); 43261d06d6bSBaptiste Daroussin return EQN_TOK_EOF; 43361d06d6bSBaptiste Daroussin } 43461d06d6bSBaptiste Daroussin 43561d06d6bSBaptiste Daroussin /* Replace a defined name with its string value. */ 43661d06d6bSBaptiste Daroussin if ((diff = def->valsz - ep->toksz) > 0) { 43761d06d6bSBaptiste Daroussin start = ep->start - ep->data; 43861d06d6bSBaptiste Daroussin ep->sz += diff; 43961d06d6bSBaptiste Daroussin ep->data = mandoc_realloc(ep->data, ep->sz + 1); 44061d06d6bSBaptiste Daroussin ep->start = ep->data + start; 44161d06d6bSBaptiste Daroussin } 44261d06d6bSBaptiste Daroussin if (diff) 44361d06d6bSBaptiste Daroussin memmove(ep->start + def->valsz, ep->start + ep->toksz, 44461d06d6bSBaptiste Daroussin strlen(ep->start + ep->toksz) + 1); 44561d06d6bSBaptiste Daroussin memcpy(ep->start, def->val, def->valsz); 44661d06d6bSBaptiste Daroussin last_len = ep->start - ep->data + def->valsz; 44761d06d6bSBaptiste Daroussin } 44861d06d6bSBaptiste Daroussin if (mode != MODE_TOK) 44961d06d6bSBaptiste Daroussin return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX; 45061d06d6bSBaptiste Daroussin if (quoted) { 45161d06d6bSBaptiste Daroussin ep->start = mandoc_strndup(ep->start, ep->toksz); 45261d06d6bSBaptiste Daroussin return EQN_TOK_QUOTED; 45361d06d6bSBaptiste Daroussin } 45461d06d6bSBaptiste Daroussin for (tok = 0; tok < EQN_TOK__MAX; tok++) 45561d06d6bSBaptiste Daroussin if (STRNEQ(ep->start, ep->toksz, 45661d06d6bSBaptiste Daroussin eqn_toks[tok], strlen(eqn_toks[tok]))) 45761d06d6bSBaptiste Daroussin return tok; 45861d06d6bSBaptiste Daroussin 45961d06d6bSBaptiste Daroussin for (i = 0; i < EQNSYM__MAX; i++) { 46061d06d6bSBaptiste Daroussin if (STRNEQ(ep->start, ep->toksz, 46161d06d6bSBaptiste Daroussin eqnsyms[i].str, strlen(eqnsyms[i].str))) { 46261d06d6bSBaptiste Daroussin mandoc_asprintf(&ep->start, 46361d06d6bSBaptiste Daroussin "\\[%s]", eqnsyms[i].sym); 46461d06d6bSBaptiste Daroussin return EQN_TOK_SYM; 46561d06d6bSBaptiste Daroussin } 46661d06d6bSBaptiste Daroussin } 46761d06d6bSBaptiste Daroussin ep->start = mandoc_strndup(ep->start, ep->toksz); 46861d06d6bSBaptiste Daroussin for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++) 46961d06d6bSBaptiste Daroussin if (STRNEQ(ep->start, ep->toksz, 47061d06d6bSBaptiste Daroussin eqn_func[i], strlen(eqn_func[i]))) 47161d06d6bSBaptiste Daroussin return EQN_TOK_FUNC; 47261d06d6bSBaptiste Daroussin return EQN_TOK__MAX; 47361d06d6bSBaptiste Daroussin } 47461d06d6bSBaptiste Daroussin 47561d06d6bSBaptiste Daroussin void 47661d06d6bSBaptiste Daroussin eqn_box_free(struct eqn_box *bp) 47761d06d6bSBaptiste Daroussin { 478*7295610fSBaptiste Daroussin if (bp == NULL) 479*7295610fSBaptiste Daroussin return; 48061d06d6bSBaptiste Daroussin 48161d06d6bSBaptiste Daroussin if (bp->first) 48261d06d6bSBaptiste Daroussin eqn_box_free(bp->first); 48361d06d6bSBaptiste Daroussin if (bp->next) 48461d06d6bSBaptiste Daroussin eqn_box_free(bp->next); 48561d06d6bSBaptiste Daroussin 48661d06d6bSBaptiste Daroussin free(bp->text); 48761d06d6bSBaptiste Daroussin free(bp->left); 48861d06d6bSBaptiste Daroussin free(bp->right); 48961d06d6bSBaptiste Daroussin free(bp->top); 49061d06d6bSBaptiste Daroussin free(bp->bottom); 49161d06d6bSBaptiste Daroussin free(bp); 49261d06d6bSBaptiste Daroussin } 49361d06d6bSBaptiste Daroussin 494*7295610fSBaptiste Daroussin struct eqn_box * 495*7295610fSBaptiste Daroussin eqn_box_new(void) 496*7295610fSBaptiste Daroussin { 497*7295610fSBaptiste Daroussin struct eqn_box *bp; 498*7295610fSBaptiste Daroussin 499*7295610fSBaptiste Daroussin bp = mandoc_calloc(1, sizeof(*bp)); 500*7295610fSBaptiste Daroussin bp->expectargs = UINT_MAX; 501*7295610fSBaptiste Daroussin return bp; 502*7295610fSBaptiste Daroussin } 503*7295610fSBaptiste Daroussin 50461d06d6bSBaptiste Daroussin /* 50561d06d6bSBaptiste Daroussin * Allocate a box as the last child of the parent node. 50661d06d6bSBaptiste Daroussin */ 50761d06d6bSBaptiste Daroussin static struct eqn_box * 50861d06d6bSBaptiste Daroussin eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) 50961d06d6bSBaptiste Daroussin { 51061d06d6bSBaptiste Daroussin struct eqn_box *bp; 51161d06d6bSBaptiste Daroussin 512*7295610fSBaptiste Daroussin bp = eqn_box_new(); 51361d06d6bSBaptiste Daroussin bp->parent = parent; 51461d06d6bSBaptiste Daroussin bp->parent->args++; 51561d06d6bSBaptiste Daroussin bp->font = bp->parent->font; 51661d06d6bSBaptiste Daroussin bp->size = ep->gsize; 51761d06d6bSBaptiste Daroussin 51861d06d6bSBaptiste Daroussin if (NULL != parent->first) { 51961d06d6bSBaptiste Daroussin parent->last->next = bp; 52061d06d6bSBaptiste Daroussin bp->prev = parent->last; 52161d06d6bSBaptiste Daroussin } else 52261d06d6bSBaptiste Daroussin parent->first = bp; 52361d06d6bSBaptiste Daroussin 52461d06d6bSBaptiste Daroussin parent->last = bp; 52561d06d6bSBaptiste Daroussin return bp; 52661d06d6bSBaptiste Daroussin } 52761d06d6bSBaptiste Daroussin 52861d06d6bSBaptiste Daroussin /* 52961d06d6bSBaptiste Daroussin * Reparent the current last node (of the current parent) under a new 53061d06d6bSBaptiste Daroussin * EQN_SUBEXPR as the first element. 53161d06d6bSBaptiste Daroussin * Then return the new parent. 53261d06d6bSBaptiste Daroussin * The new EQN_SUBEXPR will have a two-child limit. 53361d06d6bSBaptiste Daroussin */ 53461d06d6bSBaptiste Daroussin static struct eqn_box * 53561d06d6bSBaptiste Daroussin eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent) 53661d06d6bSBaptiste Daroussin { 53761d06d6bSBaptiste Daroussin struct eqn_box *b, *newb; 53861d06d6bSBaptiste Daroussin 53961d06d6bSBaptiste Daroussin assert(NULL != parent->last); 54061d06d6bSBaptiste Daroussin b = parent->last; 54161d06d6bSBaptiste Daroussin if (parent->last == parent->first) 54261d06d6bSBaptiste Daroussin parent->first = NULL; 54361d06d6bSBaptiste Daroussin parent->args--; 54461d06d6bSBaptiste Daroussin parent->last = b->prev; 54561d06d6bSBaptiste Daroussin b->prev = NULL; 54661d06d6bSBaptiste Daroussin newb = eqn_box_alloc(ep, parent); 54761d06d6bSBaptiste Daroussin newb->type = EQN_SUBEXPR; 54861d06d6bSBaptiste Daroussin newb->expectargs = 2; 54961d06d6bSBaptiste Daroussin newb->args = 1; 55061d06d6bSBaptiste Daroussin newb->first = newb->last = b; 55161d06d6bSBaptiste Daroussin newb->first->next = NULL; 55261d06d6bSBaptiste Daroussin b->parent = newb; 55361d06d6bSBaptiste Daroussin return newb; 55461d06d6bSBaptiste Daroussin } 55561d06d6bSBaptiste Daroussin 55661d06d6bSBaptiste Daroussin /* 55761d06d6bSBaptiste Daroussin * Parse the "delim" control statement. 55861d06d6bSBaptiste Daroussin */ 55961d06d6bSBaptiste Daroussin static void 56061d06d6bSBaptiste Daroussin eqn_delim(struct eqn_node *ep) 56161d06d6bSBaptiste Daroussin { 56261d06d6bSBaptiste Daroussin if (ep->end[0] == '\0' || ep->end[1] == '\0') { 563*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, 56461d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, "delim"); 56561d06d6bSBaptiste Daroussin if (ep->end[0] != '\0') 56661d06d6bSBaptiste Daroussin ep->end++; 56761d06d6bSBaptiste Daroussin } else if (strncmp(ep->end, "off", 3) == 0) { 56861d06d6bSBaptiste Daroussin ep->delim = 0; 56961d06d6bSBaptiste Daroussin ep->end += 3; 57061d06d6bSBaptiste Daroussin } else if (strncmp(ep->end, "on", 2) == 0) { 57161d06d6bSBaptiste Daroussin if (ep->odelim && ep->cdelim) 57261d06d6bSBaptiste Daroussin ep->delim = 1; 57361d06d6bSBaptiste Daroussin ep->end += 2; 57461d06d6bSBaptiste Daroussin } else { 57561d06d6bSBaptiste Daroussin ep->odelim = *ep->end++; 57661d06d6bSBaptiste Daroussin ep->cdelim = *ep->end++; 57761d06d6bSBaptiste Daroussin ep->delim = 1; 57861d06d6bSBaptiste Daroussin } 57961d06d6bSBaptiste Daroussin } 58061d06d6bSBaptiste Daroussin 58161d06d6bSBaptiste Daroussin /* 58261d06d6bSBaptiste Daroussin * Undefine a previously-defined string. 58361d06d6bSBaptiste Daroussin */ 58461d06d6bSBaptiste Daroussin static void 58561d06d6bSBaptiste Daroussin eqn_undef(struct eqn_node *ep) 58661d06d6bSBaptiste Daroussin { 58761d06d6bSBaptiste Daroussin struct eqn_def *def; 58861d06d6bSBaptiste Daroussin 58961d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { 590*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, 59161d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, "undef"); 59261d06d6bSBaptiste Daroussin return; 59361d06d6bSBaptiste Daroussin } 59461d06d6bSBaptiste Daroussin if ((def = eqn_def_find(ep)) == NULL) 59561d06d6bSBaptiste Daroussin return; 59661d06d6bSBaptiste Daroussin free(def->key); 59761d06d6bSBaptiste Daroussin free(def->val); 59861d06d6bSBaptiste Daroussin def->key = def->val = NULL; 59961d06d6bSBaptiste Daroussin def->keysz = def->valsz = 0; 60061d06d6bSBaptiste Daroussin } 60161d06d6bSBaptiste Daroussin 60261d06d6bSBaptiste Daroussin static void 60361d06d6bSBaptiste Daroussin eqn_def(struct eqn_node *ep) 60461d06d6bSBaptiste Daroussin { 60561d06d6bSBaptiste Daroussin struct eqn_def *def; 60661d06d6bSBaptiste Daroussin int i; 60761d06d6bSBaptiste Daroussin 60861d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { 609*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, 61061d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, "define"); 61161d06d6bSBaptiste Daroussin return; 61261d06d6bSBaptiste Daroussin } 61361d06d6bSBaptiste Daroussin 61461d06d6bSBaptiste Daroussin /* 61561d06d6bSBaptiste Daroussin * Search for a key that already exists. 61661d06d6bSBaptiste Daroussin * Create a new key if none is found. 61761d06d6bSBaptiste Daroussin */ 61861d06d6bSBaptiste Daroussin if ((def = eqn_def_find(ep)) == NULL) { 61961d06d6bSBaptiste Daroussin /* Find holes in string array. */ 62061d06d6bSBaptiste Daroussin for (i = 0; i < (int)ep->defsz; i++) 62161d06d6bSBaptiste Daroussin if (0 == ep->defs[i].keysz) 62261d06d6bSBaptiste Daroussin break; 62361d06d6bSBaptiste Daroussin 62461d06d6bSBaptiste Daroussin if (i == (int)ep->defsz) { 62561d06d6bSBaptiste Daroussin ep->defsz++; 62661d06d6bSBaptiste Daroussin ep->defs = mandoc_reallocarray(ep->defs, 62761d06d6bSBaptiste Daroussin ep->defsz, sizeof(struct eqn_def)); 62861d06d6bSBaptiste Daroussin ep->defs[i].key = ep->defs[i].val = NULL; 62961d06d6bSBaptiste Daroussin } 63061d06d6bSBaptiste Daroussin 63161d06d6bSBaptiste Daroussin def = ep->defs + i; 63261d06d6bSBaptiste Daroussin free(def->key); 63361d06d6bSBaptiste Daroussin def->key = mandoc_strndup(ep->start, ep->toksz); 63461d06d6bSBaptiste Daroussin def->keysz = ep->toksz; 63561d06d6bSBaptiste Daroussin } 63661d06d6bSBaptiste Daroussin 63761d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { 638*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, 63961d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, "define %s", def->key); 64061d06d6bSBaptiste Daroussin free(def->key); 64161d06d6bSBaptiste Daroussin free(def->val); 64261d06d6bSBaptiste Daroussin def->key = def->val = NULL; 64361d06d6bSBaptiste Daroussin def->keysz = def->valsz = 0; 64461d06d6bSBaptiste Daroussin return; 64561d06d6bSBaptiste Daroussin } 64661d06d6bSBaptiste Daroussin free(def->val); 64761d06d6bSBaptiste Daroussin def->val = mandoc_strndup(ep->start, ep->toksz); 64861d06d6bSBaptiste Daroussin def->valsz = ep->toksz; 64961d06d6bSBaptiste Daroussin } 65061d06d6bSBaptiste Daroussin 65161d06d6bSBaptiste Daroussin void 65261d06d6bSBaptiste Daroussin eqn_parse(struct eqn_node *ep) 65361d06d6bSBaptiste Daroussin { 65461d06d6bSBaptiste Daroussin struct eqn_box *cur, *nbox, *parent, *split; 65561d06d6bSBaptiste Daroussin const char *cp, *cpn; 65661d06d6bSBaptiste Daroussin char *p; 65761d06d6bSBaptiste Daroussin enum eqn_tok tok; 65861d06d6bSBaptiste Daroussin enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; 65961d06d6bSBaptiste Daroussin int size; 66061d06d6bSBaptiste Daroussin 66161d06d6bSBaptiste Daroussin parent = ep->node->eqn; 66261d06d6bSBaptiste Daroussin assert(parent != NULL); 66361d06d6bSBaptiste Daroussin 66461d06d6bSBaptiste Daroussin /* 66561d06d6bSBaptiste Daroussin * Empty equation. 66661d06d6bSBaptiste Daroussin * Do not add it to the high-level syntax tree. 66761d06d6bSBaptiste Daroussin */ 66861d06d6bSBaptiste Daroussin 66961d06d6bSBaptiste Daroussin if (ep->data == NULL) 67061d06d6bSBaptiste Daroussin return; 67161d06d6bSBaptiste Daroussin 67261d06d6bSBaptiste Daroussin ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); 67361d06d6bSBaptiste Daroussin 67461d06d6bSBaptiste Daroussin next_tok: 67561d06d6bSBaptiste Daroussin tok = eqn_next(ep, MODE_TOK); 67661d06d6bSBaptiste Daroussin switch (tok) { 67761d06d6bSBaptiste Daroussin case EQN_TOK_UNDEF: 67861d06d6bSBaptiste Daroussin eqn_undef(ep); 67961d06d6bSBaptiste Daroussin break; 68061d06d6bSBaptiste Daroussin case EQN_TOK_NDEFINE: 68161d06d6bSBaptiste Daroussin case EQN_TOK_DEFINE: 68261d06d6bSBaptiste Daroussin eqn_def(ep); 68361d06d6bSBaptiste Daroussin break; 68461d06d6bSBaptiste Daroussin case EQN_TOK_TDEFINE: 68561d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || 68661d06d6bSBaptiste Daroussin eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) 687*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, 68861d06d6bSBaptiste Daroussin ep->node->line, ep->node->pos, "tdefine"); 68961d06d6bSBaptiste Daroussin break; 69061d06d6bSBaptiste Daroussin case EQN_TOK_DELIM: 69161d06d6bSBaptiste Daroussin eqn_delim(ep); 69261d06d6bSBaptiste Daroussin break; 69361d06d6bSBaptiste Daroussin case EQN_TOK_GFONT: 69461d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) 695*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 696*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 69761d06d6bSBaptiste Daroussin break; 69861d06d6bSBaptiste Daroussin case EQN_TOK_MARK: 69961d06d6bSBaptiste Daroussin case EQN_TOK_LINEUP: 70061d06d6bSBaptiste Daroussin /* Ignore these. */ 70161d06d6bSBaptiste Daroussin break; 70261d06d6bSBaptiste Daroussin case EQN_TOK_DYAD: 70361d06d6bSBaptiste Daroussin case EQN_TOK_VEC: 70461d06d6bSBaptiste Daroussin case EQN_TOK_UNDER: 70561d06d6bSBaptiste Daroussin case EQN_TOK_BAR: 70661d06d6bSBaptiste Daroussin case EQN_TOK_TILDE: 70761d06d6bSBaptiste Daroussin case EQN_TOK_HAT: 70861d06d6bSBaptiste Daroussin case EQN_TOK_DOT: 70961d06d6bSBaptiste Daroussin case EQN_TOK_DOTDOT: 71061d06d6bSBaptiste Daroussin if (parent->last == NULL) { 711*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, 712*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 71361d06d6bSBaptiste Daroussin cur = eqn_box_alloc(ep, parent); 71461d06d6bSBaptiste Daroussin cur->type = EQN_TEXT; 71561d06d6bSBaptiste Daroussin cur->text = mandoc_strdup(""); 71661d06d6bSBaptiste Daroussin } 71761d06d6bSBaptiste Daroussin parent = eqn_box_makebinary(ep, parent); 71861d06d6bSBaptiste Daroussin parent->type = EQN_LIST; 71961d06d6bSBaptiste Daroussin parent->expectargs = 1; 72061d06d6bSBaptiste Daroussin parent->font = EQNFONT_ROMAN; 72161d06d6bSBaptiste Daroussin switch (tok) { 72261d06d6bSBaptiste Daroussin case EQN_TOK_DOTDOT: 72361d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[ad]"); 72461d06d6bSBaptiste Daroussin break; 72561d06d6bSBaptiste Daroussin case EQN_TOK_VEC: 72661d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[->]"); 72761d06d6bSBaptiste Daroussin break; 72861d06d6bSBaptiste Daroussin case EQN_TOK_DYAD: 72961d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[<>]"); 73061d06d6bSBaptiste Daroussin break; 73161d06d6bSBaptiste Daroussin case EQN_TOK_TILDE: 73261d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[a~]"); 73361d06d6bSBaptiste Daroussin break; 73461d06d6bSBaptiste Daroussin case EQN_TOK_UNDER: 73561d06d6bSBaptiste Daroussin parent->bottom = mandoc_strdup("\\[ul]"); 73661d06d6bSBaptiste Daroussin break; 73761d06d6bSBaptiste Daroussin case EQN_TOK_BAR: 73861d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[rn]"); 73961d06d6bSBaptiste Daroussin break; 74061d06d6bSBaptiste Daroussin case EQN_TOK_DOT: 74161d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[a.]"); 74261d06d6bSBaptiste Daroussin break; 74361d06d6bSBaptiste Daroussin case EQN_TOK_HAT: 74461d06d6bSBaptiste Daroussin parent->top = mandoc_strdup("\\[ha]"); 74561d06d6bSBaptiste Daroussin break; 74661d06d6bSBaptiste Daroussin default: 74761d06d6bSBaptiste Daroussin abort(); 74861d06d6bSBaptiste Daroussin } 74961d06d6bSBaptiste Daroussin parent = parent->parent; 75061d06d6bSBaptiste Daroussin break; 75161d06d6bSBaptiste Daroussin case EQN_TOK_FWD: 75261d06d6bSBaptiste Daroussin case EQN_TOK_BACK: 75361d06d6bSBaptiste Daroussin case EQN_TOK_DOWN: 75461d06d6bSBaptiste Daroussin case EQN_TOK_UP: 75561d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) 756*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 757*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 75861d06d6bSBaptiste Daroussin break; 75961d06d6bSBaptiste Daroussin case EQN_TOK_FAT: 76061d06d6bSBaptiste Daroussin case EQN_TOK_ROMAN: 76161d06d6bSBaptiste Daroussin case EQN_TOK_ITALIC: 76261d06d6bSBaptiste Daroussin case EQN_TOK_BOLD: 76361d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 76461d06d6bSBaptiste Daroussin parent = parent->parent; 76561d06d6bSBaptiste Daroussin /* 76661d06d6bSBaptiste Daroussin * These values apply to the next word or sequence of 76761d06d6bSBaptiste Daroussin * words; thus, we mark that we'll have a child with 76861d06d6bSBaptiste Daroussin * exactly one of those. 76961d06d6bSBaptiste Daroussin */ 77061d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, parent); 77161d06d6bSBaptiste Daroussin parent->type = EQN_LIST; 77261d06d6bSBaptiste Daroussin parent->expectargs = 1; 77361d06d6bSBaptiste Daroussin switch (tok) { 77461d06d6bSBaptiste Daroussin case EQN_TOK_FAT: 77561d06d6bSBaptiste Daroussin parent->font = EQNFONT_FAT; 77661d06d6bSBaptiste Daroussin break; 77761d06d6bSBaptiste Daroussin case EQN_TOK_ROMAN: 77861d06d6bSBaptiste Daroussin parent->font = EQNFONT_ROMAN; 77961d06d6bSBaptiste Daroussin break; 78061d06d6bSBaptiste Daroussin case EQN_TOK_ITALIC: 78161d06d6bSBaptiste Daroussin parent->font = EQNFONT_ITALIC; 78261d06d6bSBaptiste Daroussin break; 78361d06d6bSBaptiste Daroussin case EQN_TOK_BOLD: 78461d06d6bSBaptiste Daroussin parent->font = EQNFONT_BOLD; 78561d06d6bSBaptiste Daroussin break; 78661d06d6bSBaptiste Daroussin default: 78761d06d6bSBaptiste Daroussin abort(); 78861d06d6bSBaptiste Daroussin } 78961d06d6bSBaptiste Daroussin break; 79061d06d6bSBaptiste Daroussin case EQN_TOK_SIZE: 79161d06d6bSBaptiste Daroussin case EQN_TOK_GSIZE: 79261d06d6bSBaptiste Daroussin /* Accept two values: integral size and a single. */ 79361d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 794*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 795*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 79661d06d6bSBaptiste Daroussin break; 79761d06d6bSBaptiste Daroussin } 79861d06d6bSBaptiste Daroussin size = mandoc_strntoi(ep->start, ep->toksz, 10); 79961d06d6bSBaptiste Daroussin if (-1 == size) { 800*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line, 801*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 80261d06d6bSBaptiste Daroussin break; 80361d06d6bSBaptiste Daroussin } 80461d06d6bSBaptiste Daroussin if (EQN_TOK_GSIZE == tok) { 80561d06d6bSBaptiste Daroussin ep->gsize = size; 80661d06d6bSBaptiste Daroussin break; 80761d06d6bSBaptiste Daroussin } 80861d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 80961d06d6bSBaptiste Daroussin parent = parent->parent; 81061d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, parent); 81161d06d6bSBaptiste Daroussin parent->type = EQN_LIST; 81261d06d6bSBaptiste Daroussin parent->expectargs = 1; 81361d06d6bSBaptiste Daroussin parent->size = size; 81461d06d6bSBaptiste Daroussin break; 81561d06d6bSBaptiste Daroussin case EQN_TOK_FROM: 81661d06d6bSBaptiste Daroussin case EQN_TOK_TO: 81761d06d6bSBaptiste Daroussin case EQN_TOK_SUB: 81861d06d6bSBaptiste Daroussin case EQN_TOK_SUP: 81961d06d6bSBaptiste Daroussin /* 82061d06d6bSBaptiste Daroussin * We have a left-right-associative expression. 82161d06d6bSBaptiste Daroussin * Repivot under a positional node, open a child scope 82261d06d6bSBaptiste Daroussin * and keep on reading. 82361d06d6bSBaptiste Daroussin */ 82461d06d6bSBaptiste Daroussin if (parent->last == NULL) { 825*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, 826*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 82761d06d6bSBaptiste Daroussin cur = eqn_box_alloc(ep, parent); 82861d06d6bSBaptiste Daroussin cur->type = EQN_TEXT; 82961d06d6bSBaptiste Daroussin cur->text = mandoc_strdup(""); 83061d06d6bSBaptiste Daroussin } 83161d06d6bSBaptiste Daroussin while (parent->expectargs == 1 && parent->args == 1) 83261d06d6bSBaptiste Daroussin parent = parent->parent; 83361d06d6bSBaptiste Daroussin if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) { 83461d06d6bSBaptiste Daroussin for (cur = parent; cur != NULL; cur = cur->parent) 83561d06d6bSBaptiste Daroussin if (cur->pos == EQNPOS_SUB || 83661d06d6bSBaptiste Daroussin cur->pos == EQNPOS_SUP || 83761d06d6bSBaptiste Daroussin cur->pos == EQNPOS_SUBSUP || 83861d06d6bSBaptiste Daroussin cur->pos == EQNPOS_SQRT || 83961d06d6bSBaptiste Daroussin cur->pos == EQNPOS_OVER) 84061d06d6bSBaptiste Daroussin break; 84161d06d6bSBaptiste Daroussin if (cur != NULL) 84261d06d6bSBaptiste Daroussin parent = cur->parent; 84361d06d6bSBaptiste Daroussin } 84461d06d6bSBaptiste Daroussin if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) { 84561d06d6bSBaptiste Daroussin parent->expectargs = 3; 84661d06d6bSBaptiste Daroussin parent->pos = EQNPOS_SUBSUP; 84761d06d6bSBaptiste Daroussin break; 84861d06d6bSBaptiste Daroussin } 84961d06d6bSBaptiste Daroussin if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) { 85061d06d6bSBaptiste Daroussin parent->expectargs = 3; 85161d06d6bSBaptiste Daroussin parent->pos = EQNPOS_FROMTO; 85261d06d6bSBaptiste Daroussin break; 85361d06d6bSBaptiste Daroussin } 85461d06d6bSBaptiste Daroussin parent = eqn_box_makebinary(ep, parent); 85561d06d6bSBaptiste Daroussin switch (tok) { 85661d06d6bSBaptiste Daroussin case EQN_TOK_FROM: 85761d06d6bSBaptiste Daroussin parent->pos = EQNPOS_FROM; 85861d06d6bSBaptiste Daroussin break; 85961d06d6bSBaptiste Daroussin case EQN_TOK_TO: 86061d06d6bSBaptiste Daroussin parent->pos = EQNPOS_TO; 86161d06d6bSBaptiste Daroussin break; 86261d06d6bSBaptiste Daroussin case EQN_TOK_SUP: 86361d06d6bSBaptiste Daroussin parent->pos = EQNPOS_SUP; 86461d06d6bSBaptiste Daroussin break; 86561d06d6bSBaptiste Daroussin case EQN_TOK_SUB: 86661d06d6bSBaptiste Daroussin parent->pos = EQNPOS_SUB; 86761d06d6bSBaptiste Daroussin break; 86861d06d6bSBaptiste Daroussin default: 86961d06d6bSBaptiste Daroussin abort(); 87061d06d6bSBaptiste Daroussin } 87161d06d6bSBaptiste Daroussin break; 87261d06d6bSBaptiste Daroussin case EQN_TOK_SQRT: 87361d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 87461d06d6bSBaptiste Daroussin parent = parent->parent; 87561d06d6bSBaptiste Daroussin /* 87661d06d6bSBaptiste Daroussin * Accept a left-right-associative set of arguments just 87761d06d6bSBaptiste Daroussin * like sub and sup and friends but without rebalancing 87861d06d6bSBaptiste Daroussin * under a pivot. 87961d06d6bSBaptiste Daroussin */ 88061d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, parent); 88161d06d6bSBaptiste Daroussin parent->type = EQN_SUBEXPR; 88261d06d6bSBaptiste Daroussin parent->pos = EQNPOS_SQRT; 88361d06d6bSBaptiste Daroussin parent->expectargs = 1; 88461d06d6bSBaptiste Daroussin break; 88561d06d6bSBaptiste Daroussin case EQN_TOK_OVER: 88661d06d6bSBaptiste Daroussin /* 88761d06d6bSBaptiste Daroussin * We have a right-left-associative fraction. 88861d06d6bSBaptiste Daroussin * Close out anything that's currently open, then 88961d06d6bSBaptiste Daroussin * rebalance and continue reading. 89061d06d6bSBaptiste Daroussin */ 89161d06d6bSBaptiste Daroussin if (parent->last == NULL) { 892*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, 893*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 89461d06d6bSBaptiste Daroussin cur = eqn_box_alloc(ep, parent); 89561d06d6bSBaptiste Daroussin cur->type = EQN_TEXT; 89661d06d6bSBaptiste Daroussin cur->text = mandoc_strdup(""); 89761d06d6bSBaptiste Daroussin } 89861d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 89961d06d6bSBaptiste Daroussin parent = parent->parent; 90061d06d6bSBaptiste Daroussin while (EQN_SUBEXPR == parent->type) 90161d06d6bSBaptiste Daroussin parent = parent->parent; 90261d06d6bSBaptiste Daroussin parent = eqn_box_makebinary(ep, parent); 90361d06d6bSBaptiste Daroussin parent->pos = EQNPOS_OVER; 90461d06d6bSBaptiste Daroussin break; 90561d06d6bSBaptiste Daroussin case EQN_TOK_RIGHT: 90661d06d6bSBaptiste Daroussin case EQN_TOK_BRACE_CLOSE: 90761d06d6bSBaptiste Daroussin /* 90861d06d6bSBaptiste Daroussin * Close out the existing brace. 90961d06d6bSBaptiste Daroussin * FIXME: this is a shitty sentinel: we should really 91061d06d6bSBaptiste Daroussin * have a native EQN_BRACE type or whatnot. 91161d06d6bSBaptiste Daroussin */ 91261d06d6bSBaptiste Daroussin for (cur = parent; cur != NULL; cur = cur->parent) 91361d06d6bSBaptiste Daroussin if (cur->type == EQN_LIST && 91461d06d6bSBaptiste Daroussin cur->expectargs > 1 && 91561d06d6bSBaptiste Daroussin (tok == EQN_TOK_BRACE_CLOSE || 91661d06d6bSBaptiste Daroussin cur->left != NULL)) 91761d06d6bSBaptiste Daroussin break; 91861d06d6bSBaptiste Daroussin if (cur == NULL) { 919*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line, 920*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 92161d06d6bSBaptiste Daroussin break; 92261d06d6bSBaptiste Daroussin } 92361d06d6bSBaptiste Daroussin parent = cur; 92461d06d6bSBaptiste Daroussin if (EQN_TOK_RIGHT == tok) { 92561d06d6bSBaptiste Daroussin if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 92661d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, 927*7295610fSBaptiste Daroussin ep->node->line, ep->node->pos, 928*7295610fSBaptiste Daroussin "%s", eqn_toks[tok]); 92961d06d6bSBaptiste Daroussin break; 93061d06d6bSBaptiste Daroussin } 93161d06d6bSBaptiste Daroussin /* Handling depends on right/left. */ 93261d06d6bSBaptiste Daroussin if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) 93361d06d6bSBaptiste Daroussin parent->right = mandoc_strdup("\\[rc]"); 93461d06d6bSBaptiste Daroussin else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) 93561d06d6bSBaptiste Daroussin parent->right = mandoc_strdup("\\[rf]"); 93661d06d6bSBaptiste Daroussin else 93761d06d6bSBaptiste Daroussin parent->right = 93861d06d6bSBaptiste Daroussin mandoc_strndup(ep->start, ep->toksz); 93961d06d6bSBaptiste Daroussin } 94061d06d6bSBaptiste Daroussin parent = parent->parent; 94161d06d6bSBaptiste Daroussin if (tok == EQN_TOK_BRACE_CLOSE && 94261d06d6bSBaptiste Daroussin (parent->type == EQN_PILE || 94361d06d6bSBaptiste Daroussin parent->type == EQN_MATRIX)) 94461d06d6bSBaptiste Daroussin parent = parent->parent; 94561d06d6bSBaptiste Daroussin /* Close out any "singleton" lists. */ 94661d06d6bSBaptiste Daroussin while (parent->type == EQN_LIST && 94761d06d6bSBaptiste Daroussin parent->expectargs == 1 && 94861d06d6bSBaptiste Daroussin parent->args == 1) 94961d06d6bSBaptiste Daroussin parent = parent->parent; 95061d06d6bSBaptiste Daroussin break; 95161d06d6bSBaptiste Daroussin case EQN_TOK_BRACE_OPEN: 95261d06d6bSBaptiste Daroussin case EQN_TOK_LEFT: 95361d06d6bSBaptiste Daroussin /* 95461d06d6bSBaptiste Daroussin * If we already have something in the stack and we're 95561d06d6bSBaptiste Daroussin * in an expression, then rewind til we're not any more 95661d06d6bSBaptiste Daroussin * (just like with the text node). 95761d06d6bSBaptiste Daroussin */ 95861d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 95961d06d6bSBaptiste Daroussin parent = parent->parent; 96061d06d6bSBaptiste Daroussin if (EQN_TOK_LEFT == tok && 96161d06d6bSBaptiste Daroussin eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { 962*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, 963*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 96461d06d6bSBaptiste Daroussin break; 96561d06d6bSBaptiste Daroussin } 96661d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, parent); 96761d06d6bSBaptiste Daroussin parent->type = EQN_LIST; 96861d06d6bSBaptiste Daroussin if (EQN_TOK_LEFT == tok) { 96961d06d6bSBaptiste Daroussin if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) 97061d06d6bSBaptiste Daroussin parent->left = mandoc_strdup("\\[lc]"); 97161d06d6bSBaptiste Daroussin else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) 97261d06d6bSBaptiste Daroussin parent->left = mandoc_strdup("\\[lf]"); 97361d06d6bSBaptiste Daroussin else 97461d06d6bSBaptiste Daroussin parent->left = 97561d06d6bSBaptiste Daroussin mandoc_strndup(ep->start, ep->toksz); 97661d06d6bSBaptiste Daroussin } 97761d06d6bSBaptiste Daroussin break; 97861d06d6bSBaptiste Daroussin case EQN_TOK_PILE: 97961d06d6bSBaptiste Daroussin case EQN_TOK_LPILE: 98061d06d6bSBaptiste Daroussin case EQN_TOK_RPILE: 98161d06d6bSBaptiste Daroussin case EQN_TOK_CPILE: 98261d06d6bSBaptiste Daroussin case EQN_TOK_CCOL: 98361d06d6bSBaptiste Daroussin case EQN_TOK_LCOL: 98461d06d6bSBaptiste Daroussin case EQN_TOK_RCOL: 98561d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 98661d06d6bSBaptiste Daroussin parent = parent->parent; 98761d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, parent); 98861d06d6bSBaptiste Daroussin parent->type = EQN_PILE; 98961d06d6bSBaptiste Daroussin parent->expectargs = 1; 99061d06d6bSBaptiste Daroussin break; 99161d06d6bSBaptiste Daroussin case EQN_TOK_ABOVE: 99261d06d6bSBaptiste Daroussin for (cur = parent; cur != NULL; cur = cur->parent) 99361d06d6bSBaptiste Daroussin if (cur->type == EQN_PILE) 99461d06d6bSBaptiste Daroussin break; 99561d06d6bSBaptiste Daroussin if (cur == NULL) { 996*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line, 997*7295610fSBaptiste Daroussin ep->node->pos, "%s", eqn_toks[tok]); 99861d06d6bSBaptiste Daroussin break; 99961d06d6bSBaptiste Daroussin } 100061d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, cur); 100161d06d6bSBaptiste Daroussin parent->type = EQN_LIST; 100261d06d6bSBaptiste Daroussin break; 100361d06d6bSBaptiste Daroussin case EQN_TOK_MATRIX: 100461d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 100561d06d6bSBaptiste Daroussin parent = parent->parent; 100661d06d6bSBaptiste Daroussin parent = eqn_box_alloc(ep, parent); 100761d06d6bSBaptiste Daroussin parent->type = EQN_MATRIX; 100861d06d6bSBaptiste Daroussin parent->expectargs = 1; 100961d06d6bSBaptiste Daroussin break; 101061d06d6bSBaptiste Daroussin case EQN_TOK_EOF: 101161d06d6bSBaptiste Daroussin return; 101261d06d6bSBaptiste Daroussin case EQN_TOK__MAX: 101361d06d6bSBaptiste Daroussin case EQN_TOK_FUNC: 101461d06d6bSBaptiste Daroussin case EQN_TOK_QUOTED: 101561d06d6bSBaptiste Daroussin case EQN_TOK_SYM: 101661d06d6bSBaptiste Daroussin p = ep->start; 101761d06d6bSBaptiste Daroussin assert(p != NULL); 101861d06d6bSBaptiste Daroussin /* 101961d06d6bSBaptiste Daroussin * If we already have something in the stack and we're 102061d06d6bSBaptiste Daroussin * in an expression, then rewind til we're not any more. 102161d06d6bSBaptiste Daroussin */ 102261d06d6bSBaptiste Daroussin while (parent->args == parent->expectargs) 102361d06d6bSBaptiste Daroussin parent = parent->parent; 102461d06d6bSBaptiste Daroussin cur = eqn_box_alloc(ep, parent); 102561d06d6bSBaptiste Daroussin cur->type = EQN_TEXT; 102661d06d6bSBaptiste Daroussin cur->text = p; 102761d06d6bSBaptiste Daroussin switch (tok) { 102861d06d6bSBaptiste Daroussin case EQN_TOK_FUNC: 102961d06d6bSBaptiste Daroussin cur->font = EQNFONT_ROMAN; 103061d06d6bSBaptiste Daroussin break; 103161d06d6bSBaptiste Daroussin case EQN_TOK_QUOTED: 103261d06d6bSBaptiste Daroussin if (cur->font == EQNFONT_NONE) 103361d06d6bSBaptiste Daroussin cur->font = EQNFONT_ITALIC; 103461d06d6bSBaptiste Daroussin break; 103561d06d6bSBaptiste Daroussin case EQN_TOK_SYM: 103661d06d6bSBaptiste Daroussin break; 103761d06d6bSBaptiste Daroussin default: 103861d06d6bSBaptiste Daroussin if (cur->font != EQNFONT_NONE || *p == '\0') 103961d06d6bSBaptiste Daroussin break; 104061d06d6bSBaptiste Daroussin cpn = p - 1; 104161d06d6bSBaptiste Daroussin ccln = CCL_LET; 104261d06d6bSBaptiste Daroussin split = NULL; 104361d06d6bSBaptiste Daroussin for (;;) { 104461d06d6bSBaptiste Daroussin /* Advance to next character. */ 104561d06d6bSBaptiste Daroussin cp = cpn++; 104661d06d6bSBaptiste Daroussin ccl = ccln; 104761d06d6bSBaptiste Daroussin ccln = isalpha((unsigned char)*cpn) ? CCL_LET : 104861d06d6bSBaptiste Daroussin isdigit((unsigned char)*cpn) || 104961d06d6bSBaptiste Daroussin (*cpn == '.' && (ccl == CCL_DIG || 105061d06d6bSBaptiste Daroussin isdigit((unsigned char)cpn[1]))) ? 105161d06d6bSBaptiste Daroussin CCL_DIG : CCL_PUN; 105261d06d6bSBaptiste Daroussin /* No boundary before first character. */ 105361d06d6bSBaptiste Daroussin if (cp < p) 105461d06d6bSBaptiste Daroussin continue; 105561d06d6bSBaptiste Daroussin cur->font = ccl == CCL_LET ? 105661d06d6bSBaptiste Daroussin EQNFONT_ITALIC : EQNFONT_ROMAN; 105761d06d6bSBaptiste Daroussin if (*cp == '\\') 105861d06d6bSBaptiste Daroussin mandoc_escape(&cpn, NULL, NULL); 105961d06d6bSBaptiste Daroussin /* No boundary after last character. */ 106061d06d6bSBaptiste Daroussin if (*cpn == '\0') 106161d06d6bSBaptiste Daroussin break; 106261d06d6bSBaptiste Daroussin if (ccln == ccl && *cp != ',' && *cpn != ',') 106361d06d6bSBaptiste Daroussin continue; 106461d06d6bSBaptiste Daroussin /* Boundary found, split the text. */ 106561d06d6bSBaptiste Daroussin if (parent->args == parent->expectargs) { 106661d06d6bSBaptiste Daroussin /* Remove the text from the tree. */ 106761d06d6bSBaptiste Daroussin if (cur->prev == NULL) 106861d06d6bSBaptiste Daroussin parent->first = cur->next; 106961d06d6bSBaptiste Daroussin else 107061d06d6bSBaptiste Daroussin cur->prev->next = NULL; 107161d06d6bSBaptiste Daroussin parent->last = cur->prev; 107261d06d6bSBaptiste Daroussin parent->args--; 107361d06d6bSBaptiste Daroussin /* Set up a list instead. */ 107461d06d6bSBaptiste Daroussin split = eqn_box_alloc(ep, parent); 107561d06d6bSBaptiste Daroussin split->type = EQN_LIST; 107661d06d6bSBaptiste Daroussin /* Insert the word into the list. */ 107761d06d6bSBaptiste Daroussin split->first = split->last = cur; 107861d06d6bSBaptiste Daroussin cur->parent = split; 107961d06d6bSBaptiste Daroussin cur->prev = NULL; 108061d06d6bSBaptiste Daroussin parent = split; 108161d06d6bSBaptiste Daroussin } 108261d06d6bSBaptiste Daroussin /* Append a new text box. */ 108361d06d6bSBaptiste Daroussin nbox = eqn_box_alloc(ep, parent); 108461d06d6bSBaptiste Daroussin nbox->type = EQN_TEXT; 108561d06d6bSBaptiste Daroussin nbox->text = mandoc_strdup(cpn); 108661d06d6bSBaptiste Daroussin /* Truncate the old box. */ 108761d06d6bSBaptiste Daroussin p = mandoc_strndup(cur->text, 108861d06d6bSBaptiste Daroussin cpn - cur->text); 108961d06d6bSBaptiste Daroussin free(cur->text); 109061d06d6bSBaptiste Daroussin cur->text = p; 109161d06d6bSBaptiste Daroussin /* Setup to process the new box. */ 109261d06d6bSBaptiste Daroussin cur = nbox; 109361d06d6bSBaptiste Daroussin p = nbox->text; 109461d06d6bSBaptiste Daroussin cpn = p - 1; 109561d06d6bSBaptiste Daroussin ccln = CCL_LET; 109661d06d6bSBaptiste Daroussin } 109761d06d6bSBaptiste Daroussin if (split != NULL) 109861d06d6bSBaptiste Daroussin parent = split->parent; 109961d06d6bSBaptiste Daroussin break; 110061d06d6bSBaptiste Daroussin } 110161d06d6bSBaptiste Daroussin break; 110261d06d6bSBaptiste Daroussin default: 110361d06d6bSBaptiste Daroussin abort(); 110461d06d6bSBaptiste Daroussin } 110561d06d6bSBaptiste Daroussin goto next_tok; 110661d06d6bSBaptiste Daroussin } 110761d06d6bSBaptiste Daroussin 110861d06d6bSBaptiste Daroussin void 110961d06d6bSBaptiste Daroussin eqn_free(struct eqn_node *p) 111061d06d6bSBaptiste Daroussin { 111161d06d6bSBaptiste Daroussin int i; 111261d06d6bSBaptiste Daroussin 1113*7295610fSBaptiste Daroussin if (p == NULL) 1114*7295610fSBaptiste Daroussin return; 1115*7295610fSBaptiste Daroussin 111661d06d6bSBaptiste Daroussin for (i = 0; i < (int)p->defsz; i++) { 111761d06d6bSBaptiste Daroussin free(p->defs[i].key); 111861d06d6bSBaptiste Daroussin free(p->defs[i].val); 111961d06d6bSBaptiste Daroussin } 112061d06d6bSBaptiste Daroussin 112161d06d6bSBaptiste Daroussin free(p->data); 112261d06d6bSBaptiste Daroussin free(p->defs); 112361d06d6bSBaptiste Daroussin free(p); 112461d06d6bSBaptiste Daroussin } 1125