1*4d131170SRobert Mustacchi /* $Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3260e9a87SYuri Pankov * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*4d131170SRobert Mustacchi * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore *
695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore *
1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore */
1895c635efSGarrett D'Amore #include "config.h"
19260e9a87SYuri Pankov
20260e9a87SYuri Pankov #include <sys/types.h>
2195c635efSGarrett D'Amore
2295c635efSGarrett D'Amore #include <assert.h>
23c66b8046SYuri Pankov #include <ctype.h>
2495c635efSGarrett D'Amore #include <limits.h>
2595c635efSGarrett D'Amore #include <stdio.h>
2695c635efSGarrett D'Amore #include <stdlib.h>
2795c635efSGarrett D'Amore #include <string.h>
2895c635efSGarrett D'Amore #include <time.h>
2995c635efSGarrett D'Amore
30260e9a87SYuri Pankov #include "mandoc_aux.h"
31c66b8046SYuri Pankov #include "mandoc.h"
32c66b8046SYuri Pankov #include "roff.h"
33cec8643bSMichal Nowak #include "eqn.h"
3495c635efSGarrett D'Amore #include "libmandoc.h"
35cec8643bSMichal Nowak #include "eqn_parse.h"
3695c635efSGarrett D'Amore
3795c635efSGarrett D'Amore #define EQN_NEST_MAX 128 /* maximum nesting of defines */
38260e9a87SYuri Pankov #define STRNEQ(p1, sz1, p2, sz2) \
39260e9a87SYuri Pankov ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
4095c635efSGarrett D'Amore
41260e9a87SYuri Pankov enum eqn_tok {
42260e9a87SYuri Pankov EQN_TOK_DYAD = 0,
43260e9a87SYuri Pankov EQN_TOK_VEC,
44260e9a87SYuri Pankov EQN_TOK_UNDER,
45260e9a87SYuri Pankov EQN_TOK_BAR,
46260e9a87SYuri Pankov EQN_TOK_TILDE,
47260e9a87SYuri Pankov EQN_TOK_HAT,
48260e9a87SYuri Pankov EQN_TOK_DOT,
49260e9a87SYuri Pankov EQN_TOK_DOTDOT,
50260e9a87SYuri Pankov EQN_TOK_FWD,
51260e9a87SYuri Pankov EQN_TOK_BACK,
52260e9a87SYuri Pankov EQN_TOK_DOWN,
53260e9a87SYuri Pankov EQN_TOK_UP,
54260e9a87SYuri Pankov EQN_TOK_FAT,
55260e9a87SYuri Pankov EQN_TOK_ROMAN,
56260e9a87SYuri Pankov EQN_TOK_ITALIC,
57260e9a87SYuri Pankov EQN_TOK_BOLD,
58260e9a87SYuri Pankov EQN_TOK_SIZE,
59260e9a87SYuri Pankov EQN_TOK_SUB,
60260e9a87SYuri Pankov EQN_TOK_SUP,
61260e9a87SYuri Pankov EQN_TOK_SQRT,
62260e9a87SYuri Pankov EQN_TOK_OVER,
63260e9a87SYuri Pankov EQN_TOK_FROM,
64260e9a87SYuri Pankov EQN_TOK_TO,
65260e9a87SYuri Pankov EQN_TOK_BRACE_OPEN,
66260e9a87SYuri Pankov EQN_TOK_BRACE_CLOSE,
67260e9a87SYuri Pankov EQN_TOK_GSIZE,
68260e9a87SYuri Pankov EQN_TOK_GFONT,
69260e9a87SYuri Pankov EQN_TOK_MARK,
70260e9a87SYuri Pankov EQN_TOK_LINEUP,
71260e9a87SYuri Pankov EQN_TOK_LEFT,
72260e9a87SYuri Pankov EQN_TOK_RIGHT,
73260e9a87SYuri Pankov EQN_TOK_PILE,
74260e9a87SYuri Pankov EQN_TOK_LPILE,
75260e9a87SYuri Pankov EQN_TOK_RPILE,
76260e9a87SYuri Pankov EQN_TOK_CPILE,
77260e9a87SYuri Pankov EQN_TOK_MATRIX,
78260e9a87SYuri Pankov EQN_TOK_CCOL,
79260e9a87SYuri Pankov EQN_TOK_LCOL,
80260e9a87SYuri Pankov EQN_TOK_RCOL,
81260e9a87SYuri Pankov EQN_TOK_DELIM,
82260e9a87SYuri Pankov EQN_TOK_DEFINE,
83260e9a87SYuri Pankov EQN_TOK_TDEFINE,
84260e9a87SYuri Pankov EQN_TOK_NDEFINE,
85260e9a87SYuri Pankov EQN_TOK_UNDEF,
86260e9a87SYuri Pankov EQN_TOK_ABOVE,
87c66b8046SYuri Pankov EQN_TOK__MAX,
88c66b8046SYuri Pankov EQN_TOK_FUNC,
89c66b8046SYuri Pankov EQN_TOK_QUOTED,
90c66b8046SYuri Pankov EQN_TOK_SYM,
91c66b8046SYuri Pankov EQN_TOK_EOF
92260e9a87SYuri Pankov };
93260e9a87SYuri Pankov
94260e9a87SYuri Pankov static const char *eqn_toks[EQN_TOK__MAX] = {
95260e9a87SYuri Pankov "dyad", /* EQN_TOK_DYAD */
96260e9a87SYuri Pankov "vec", /* EQN_TOK_VEC */
97260e9a87SYuri Pankov "under", /* EQN_TOK_UNDER */
98260e9a87SYuri Pankov "bar", /* EQN_TOK_BAR */
99260e9a87SYuri Pankov "tilde", /* EQN_TOK_TILDE */
100260e9a87SYuri Pankov "hat", /* EQN_TOK_HAT */
101260e9a87SYuri Pankov "dot", /* EQN_TOK_DOT */
102260e9a87SYuri Pankov "dotdot", /* EQN_TOK_DOTDOT */
103260e9a87SYuri Pankov "fwd", /* EQN_TOK_FWD * */
104260e9a87SYuri Pankov "back", /* EQN_TOK_BACK */
105260e9a87SYuri Pankov "down", /* EQN_TOK_DOWN */
106260e9a87SYuri Pankov "up", /* EQN_TOK_UP */
107260e9a87SYuri Pankov "fat", /* EQN_TOK_FAT */
108260e9a87SYuri Pankov "roman", /* EQN_TOK_ROMAN */
109260e9a87SYuri Pankov "italic", /* EQN_TOK_ITALIC */
110260e9a87SYuri Pankov "bold", /* EQN_TOK_BOLD */
111260e9a87SYuri Pankov "size", /* EQN_TOK_SIZE */
112260e9a87SYuri Pankov "sub", /* EQN_TOK_SUB */
113260e9a87SYuri Pankov "sup", /* EQN_TOK_SUP */
114260e9a87SYuri Pankov "sqrt", /* EQN_TOK_SQRT */
115260e9a87SYuri Pankov "over", /* EQN_TOK_OVER */
116260e9a87SYuri Pankov "from", /* EQN_TOK_FROM */
117260e9a87SYuri Pankov "to", /* EQN_TOK_TO */
118260e9a87SYuri Pankov "{", /* EQN_TOK_BRACE_OPEN */
119260e9a87SYuri Pankov "}", /* EQN_TOK_BRACE_CLOSE */
120260e9a87SYuri Pankov "gsize", /* EQN_TOK_GSIZE */
121260e9a87SYuri Pankov "gfont", /* EQN_TOK_GFONT */
122260e9a87SYuri Pankov "mark", /* EQN_TOK_MARK */
123260e9a87SYuri Pankov "lineup", /* EQN_TOK_LINEUP */
124260e9a87SYuri Pankov "left", /* EQN_TOK_LEFT */
125260e9a87SYuri Pankov "right", /* EQN_TOK_RIGHT */
126260e9a87SYuri Pankov "pile", /* EQN_TOK_PILE */
127260e9a87SYuri Pankov "lpile", /* EQN_TOK_LPILE */
128260e9a87SYuri Pankov "rpile", /* EQN_TOK_RPILE */
129260e9a87SYuri Pankov "cpile", /* EQN_TOK_CPILE */
130260e9a87SYuri Pankov "matrix", /* EQN_TOK_MATRIX */
131260e9a87SYuri Pankov "ccol", /* EQN_TOK_CCOL */
132260e9a87SYuri Pankov "lcol", /* EQN_TOK_LCOL */
133260e9a87SYuri Pankov "rcol", /* EQN_TOK_RCOL */
134260e9a87SYuri Pankov "delim", /* EQN_TOK_DELIM */
135260e9a87SYuri Pankov "define", /* EQN_TOK_DEFINE */
136260e9a87SYuri Pankov "tdefine", /* EQN_TOK_TDEFINE */
137260e9a87SYuri Pankov "ndefine", /* EQN_TOK_NDEFINE */
138260e9a87SYuri Pankov "undef", /* EQN_TOK_UNDEF */
139260e9a87SYuri Pankov "above", /* EQN_TOK_ABOVE */
14095c635efSGarrett D'Amore };
14195c635efSGarrett D'Amore
142c66b8046SYuri Pankov static const char *const eqn_func[] = {
143c66b8046SYuri Pankov "acos", "acsc", "and", "arc", "asec", "asin", "atan",
144c66b8046SYuri Pankov "cos", "cosh", "coth", "csc", "det", "exp", "for",
145c66b8046SYuri Pankov "if", "lim", "ln", "log", "max", "min",
146c66b8046SYuri Pankov "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
147c66b8046SYuri Pankov };
148c66b8046SYuri Pankov
14995c635efSGarrett D'Amore enum eqn_symt {
150c66b8046SYuri Pankov EQNSYM_alpha = 0,
15195c635efSGarrett D'Amore EQNSYM_beta,
15295c635efSGarrett D'Amore EQNSYM_chi,
15395c635efSGarrett D'Amore EQNSYM_delta,
15495c635efSGarrett D'Amore EQNSYM_epsilon,
15595c635efSGarrett D'Amore EQNSYM_eta,
15695c635efSGarrett D'Amore EQNSYM_gamma,
15795c635efSGarrett D'Amore EQNSYM_iota,
15895c635efSGarrett D'Amore EQNSYM_kappa,
15995c635efSGarrett D'Amore EQNSYM_lambda,
16095c635efSGarrett D'Amore EQNSYM_mu,
16195c635efSGarrett D'Amore EQNSYM_nu,
16295c635efSGarrett D'Amore EQNSYM_omega,
16395c635efSGarrett D'Amore EQNSYM_omicron,
16495c635efSGarrett D'Amore EQNSYM_phi,
16595c635efSGarrett D'Amore EQNSYM_pi,
16695c635efSGarrett D'Amore EQNSYM_ps,
16795c635efSGarrett D'Amore EQNSYM_rho,
16895c635efSGarrett D'Amore EQNSYM_sigma,
16995c635efSGarrett D'Amore EQNSYM_tau,
17095c635efSGarrett D'Amore EQNSYM_theta,
17195c635efSGarrett D'Amore EQNSYM_upsilon,
17295c635efSGarrett D'Amore EQNSYM_xi,
17395c635efSGarrett D'Amore EQNSYM_zeta,
17495c635efSGarrett D'Amore EQNSYM_DELTA,
17595c635efSGarrett D'Amore EQNSYM_GAMMA,
17695c635efSGarrett D'Amore EQNSYM_LAMBDA,
17795c635efSGarrett D'Amore EQNSYM_OMEGA,
17895c635efSGarrett D'Amore EQNSYM_PHI,
17995c635efSGarrett D'Amore EQNSYM_PI,
18095c635efSGarrett D'Amore EQNSYM_PSI,
18195c635efSGarrett D'Amore EQNSYM_SIGMA,
18295c635efSGarrett D'Amore EQNSYM_THETA,
18395c635efSGarrett D'Amore EQNSYM_UPSILON,
18495c635efSGarrett D'Amore EQNSYM_XI,
18595c635efSGarrett D'Amore EQNSYM_inter,
18695c635efSGarrett D'Amore EQNSYM_union,
18795c635efSGarrett D'Amore EQNSYM_prod,
18895c635efSGarrett D'Amore EQNSYM_int,
18995c635efSGarrett D'Amore EQNSYM_sum,
19095c635efSGarrett D'Amore EQNSYM_grad,
19195c635efSGarrett D'Amore EQNSYM_del,
19295c635efSGarrett D'Amore EQNSYM_times,
19395c635efSGarrett D'Amore EQNSYM_cdot,
19495c635efSGarrett D'Amore EQNSYM_nothing,
19595c635efSGarrett D'Amore EQNSYM_approx,
19695c635efSGarrett D'Amore EQNSYM_prime,
19795c635efSGarrett D'Amore EQNSYM_half,
19895c635efSGarrett D'Amore EQNSYM_partial,
19995c635efSGarrett D'Amore EQNSYM_inf,
20095c635efSGarrett D'Amore EQNSYM_muchgreat,
20195c635efSGarrett D'Amore EQNSYM_muchless,
20295c635efSGarrett D'Amore EQNSYM_larrow,
20395c635efSGarrett D'Amore EQNSYM_rarrow,
20495c635efSGarrett D'Amore EQNSYM_pm,
20595c635efSGarrett D'Amore EQNSYM_nequal,
20695c635efSGarrett D'Amore EQNSYM_equiv,
20795c635efSGarrett D'Amore EQNSYM_lessequal,
20895c635efSGarrett D'Amore EQNSYM_moreequal,
209260e9a87SYuri Pankov EQNSYM_minus,
21095c635efSGarrett D'Amore EQNSYM__MAX
21195c635efSGarrett D'Amore };
21295c635efSGarrett D'Amore
21395c635efSGarrett D'Amore struct eqnsym {
214260e9a87SYuri Pankov const char *str;
21595c635efSGarrett D'Amore const char *sym;
21695c635efSGarrett D'Amore };
21795c635efSGarrett D'Amore
21895c635efSGarrett D'Amore static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
219260e9a87SYuri Pankov { "alpha", "*a" }, /* EQNSYM_alpha */
220260e9a87SYuri Pankov { "beta", "*b" }, /* EQNSYM_beta */
221260e9a87SYuri Pankov { "chi", "*x" }, /* EQNSYM_chi */
222260e9a87SYuri Pankov { "delta", "*d" }, /* EQNSYM_delta */
223260e9a87SYuri Pankov { "epsilon", "*e" }, /* EQNSYM_epsilon */
224260e9a87SYuri Pankov { "eta", "*y" }, /* EQNSYM_eta */
225260e9a87SYuri Pankov { "gamma", "*g" }, /* EQNSYM_gamma */
226260e9a87SYuri Pankov { "iota", "*i" }, /* EQNSYM_iota */
227260e9a87SYuri Pankov { "kappa", "*k" }, /* EQNSYM_kappa */
228260e9a87SYuri Pankov { "lambda", "*l" }, /* EQNSYM_lambda */
229260e9a87SYuri Pankov { "mu", "*m" }, /* EQNSYM_mu */
230260e9a87SYuri Pankov { "nu", "*n" }, /* EQNSYM_nu */
231260e9a87SYuri Pankov { "omega", "*w" }, /* EQNSYM_omega */
232260e9a87SYuri Pankov { "omicron", "*o" }, /* EQNSYM_omicron */
233260e9a87SYuri Pankov { "phi", "*f" }, /* EQNSYM_phi */
234260e9a87SYuri Pankov { "pi", "*p" }, /* EQNSYM_pi */
235260e9a87SYuri Pankov { "psi", "*q" }, /* EQNSYM_psi */
236260e9a87SYuri Pankov { "rho", "*r" }, /* EQNSYM_rho */
237260e9a87SYuri Pankov { "sigma", "*s" }, /* EQNSYM_sigma */
238260e9a87SYuri Pankov { "tau", "*t" }, /* EQNSYM_tau */
239260e9a87SYuri Pankov { "theta", "*h" }, /* EQNSYM_theta */
240260e9a87SYuri Pankov { "upsilon", "*u" }, /* EQNSYM_upsilon */
241260e9a87SYuri Pankov { "xi", "*c" }, /* EQNSYM_xi */
242260e9a87SYuri Pankov { "zeta", "*z" }, /* EQNSYM_zeta */
243260e9a87SYuri Pankov { "DELTA", "*D" }, /* EQNSYM_DELTA */
244260e9a87SYuri Pankov { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
245260e9a87SYuri Pankov { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
246260e9a87SYuri Pankov { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
247260e9a87SYuri Pankov { "PHI", "*F" }, /* EQNSYM_PHI */
248260e9a87SYuri Pankov { "PI", "*P" }, /* EQNSYM_PI */
249260e9a87SYuri Pankov { "PSI", "*Q" }, /* EQNSYM_PSI */
250260e9a87SYuri Pankov { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
251260e9a87SYuri Pankov { "THETA", "*H" }, /* EQNSYM_THETA */
252260e9a87SYuri Pankov { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
253260e9a87SYuri Pankov { "XI", "*C" }, /* EQNSYM_XI */
254260e9a87SYuri Pankov { "inter", "ca" }, /* EQNSYM_inter */
255260e9a87SYuri Pankov { "union", "cu" }, /* EQNSYM_union */
256260e9a87SYuri Pankov { "prod", "product" }, /* EQNSYM_prod */
257260e9a87SYuri Pankov { "int", "integral" }, /* EQNSYM_int */
258260e9a87SYuri Pankov { "sum", "sum" }, /* EQNSYM_sum */
259260e9a87SYuri Pankov { "grad", "gr" }, /* EQNSYM_grad */
260260e9a87SYuri Pankov { "del", "gr" }, /* EQNSYM_del */
261260e9a87SYuri Pankov { "times", "mu" }, /* EQNSYM_times */
262260e9a87SYuri Pankov { "cdot", "pc" }, /* EQNSYM_cdot */
263260e9a87SYuri Pankov { "nothing", "&" }, /* EQNSYM_nothing */
264260e9a87SYuri Pankov { "approx", "~~" }, /* EQNSYM_approx */
265260e9a87SYuri Pankov { "prime", "fm" }, /* EQNSYM_prime */
266260e9a87SYuri Pankov { "half", "12" }, /* EQNSYM_half */
267260e9a87SYuri Pankov { "partial", "pd" }, /* EQNSYM_partial */
268260e9a87SYuri Pankov { "inf", "if" }, /* EQNSYM_inf */
269260e9a87SYuri Pankov { ">>", ">>" }, /* EQNSYM_muchgreat */
270260e9a87SYuri Pankov { "<<", "<<" }, /* EQNSYM_muchless */
271260e9a87SYuri Pankov { "<-", "<-" }, /* EQNSYM_larrow */
272260e9a87SYuri Pankov { "->", "->" }, /* EQNSYM_rarrow */
273260e9a87SYuri Pankov { "+-", "+-" }, /* EQNSYM_pm */
274260e9a87SYuri Pankov { "!=", "!=" }, /* EQNSYM_nequal */
275260e9a87SYuri Pankov { "==", "==" }, /* EQNSYM_equiv */
276260e9a87SYuri Pankov { "<=", "<=" }, /* EQNSYM_lessequal */
277260e9a87SYuri Pankov { ">=", ">=" }, /* EQNSYM_moreequal */
278260e9a87SYuri Pankov { "-", "mi" }, /* EQNSYM_minus */
27995c635efSGarrett D'Amore };
28095c635efSGarrett D'Amore
281c66b8046SYuri Pankov enum parse_mode {
282c66b8046SYuri Pankov MODE_QUOTED,
283c66b8046SYuri Pankov MODE_NOSUB,
284c66b8046SYuri Pankov MODE_SUB,
285c66b8046SYuri Pankov MODE_TOK
286c66b8046SYuri Pankov };
287c66b8046SYuri Pankov
288cec8643bSMichal Nowak struct eqn_def {
289cec8643bSMichal Nowak char *key;
290cec8643bSMichal Nowak size_t keysz;
291cec8643bSMichal Nowak char *val;
292cec8643bSMichal Nowak size_t valsz;
293cec8643bSMichal Nowak };
294cec8643bSMichal Nowak
295260e9a87SYuri Pankov static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
296260e9a87SYuri Pankov static struct eqn_box *eqn_box_makebinary(struct eqn_node *,
297c66b8046SYuri Pankov struct eqn_box *);
298260e9a87SYuri Pankov static void eqn_def(struct eqn_node *);
299c66b8046SYuri Pankov static struct eqn_def *eqn_def_find(struct eqn_node *);
300260e9a87SYuri Pankov static void eqn_delim(struct eqn_node *);
301c66b8046SYuri Pankov static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode);
302260e9a87SYuri Pankov static void eqn_undef(struct eqn_node *);
303260e9a87SYuri Pankov
304260e9a87SYuri Pankov
30595c635efSGarrett D'Amore struct eqn_node *
eqn_alloc(void)306cec8643bSMichal Nowak eqn_alloc(void)
30795c635efSGarrett D'Amore {
308c66b8046SYuri Pankov struct eqn_node *ep;
30995c635efSGarrett D'Amore
310c66b8046SYuri Pankov ep = mandoc_calloc(1, sizeof(*ep));
311c66b8046SYuri Pankov ep->gsize = EQN_DEFSIZE;
312c66b8046SYuri Pankov return ep;
313c66b8046SYuri Pankov }
31495c635efSGarrett D'Amore
315c66b8046SYuri Pankov void
eqn_reset(struct eqn_node * ep)316c66b8046SYuri Pankov eqn_reset(struct eqn_node *ep)
317c66b8046SYuri Pankov {
318c66b8046SYuri Pankov free(ep->data);
319c66b8046SYuri Pankov ep->data = ep->start = ep->end = NULL;
320c66b8046SYuri Pankov ep->sz = ep->toksz = 0;
321c66b8046SYuri Pankov }
32295c635efSGarrett D'Amore
323c66b8046SYuri Pankov void
eqn_read(struct eqn_node * ep,const char * p)324c66b8046SYuri Pankov eqn_read(struct eqn_node *ep, const char *p)
325c66b8046SYuri Pankov {
326c66b8046SYuri Pankov char *cp;
327c66b8046SYuri Pankov
328c66b8046SYuri Pankov if (ep->data == NULL) {
329c66b8046SYuri Pankov ep->sz = strlen(p);
330c66b8046SYuri Pankov ep->data = mandoc_strdup(p);
331c66b8046SYuri Pankov } else {
332c66b8046SYuri Pankov ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
333c66b8046SYuri Pankov free(ep->data);
334c66b8046SYuri Pankov ep->data = cp;
335c66b8046SYuri Pankov }
336c66b8046SYuri Pankov ep->sz += 1;
33795c635efSGarrett D'Amore }
33895c635efSGarrett D'Amore
339260e9a87SYuri Pankov /*
340260e9a87SYuri Pankov * Find the key "key" of the give size within our eqn-defined values.
341260e9a87SYuri Pankov */
342260e9a87SYuri Pankov static struct eqn_def *
eqn_def_find(struct eqn_node * ep)343c66b8046SYuri Pankov eqn_def_find(struct eqn_node *ep)
34495c635efSGarrett D'Amore {
34595c635efSGarrett D'Amore int i;
34695c635efSGarrett D'Amore
347260e9a87SYuri Pankov for (i = 0; i < (int)ep->defsz; i++)
348260e9a87SYuri Pankov if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
349c66b8046SYuri Pankov ep->defs[i].keysz, ep->start, ep->toksz))
350371584c2SYuri Pankov return &ep->defs[i];
35195c635efSGarrett D'Amore
352371584c2SYuri Pankov return NULL;
35395c635efSGarrett D'Amore }
35495c635efSGarrett D'Amore
355260e9a87SYuri Pankov /*
356c66b8046SYuri Pankov * Parse a token from the input text. The modes are:
357c66b8046SYuri Pankov * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
358c66b8046SYuri Pankov * before its next occurence. Do not interpret the token in any
359c66b8046SYuri Pankov * way and return EQN_TOK_QUOTED. All other modes behave like
360c66b8046SYuri Pankov * MODE_QUOTED when *ep->start is '"'.
361c66b8046SYuri Pankov * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
362c66b8046SYuri Pankov * otherwise, it ends before the next whitespace or brace.
363c66b8046SYuri Pankov * Do not interpret the token and return EQN_TOK__MAX.
364c66b8046SYuri Pankov * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
365c66b8046SYuri Pankov * alias created with define. If it is an alias, replace it with
366c66b8046SYuri Pankov * its string value and reparse.
367c66b8046SYuri Pankov * MODE_TOK: Like MODE_SUB, but also check the token against the list
368c66b8046SYuri Pankov * of tokens, and if there is a match, return that token. Otherwise,
369c66b8046SYuri Pankov * if the token matches a symbol, return EQN_TOK_SYM; if it matches
370c66b8046SYuri Pankov * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
371c66b8046SYuri Pankov * a token match, *ep->start is set to an allocated string that the
372c66b8046SYuri Pankov * caller is expected to free.
373c66b8046SYuri Pankov * All modes skip whitespace following the end of the token.
374260e9a87SYuri Pankov */
375260e9a87SYuri Pankov static enum eqn_tok
eqn_next(struct eqn_node * ep,enum parse_mode mode)376c66b8046SYuri Pankov eqn_next(struct eqn_node *ep, enum parse_mode mode)
377260e9a87SYuri Pankov {
378c66b8046SYuri Pankov static int last_len, lim;
379260e9a87SYuri Pankov
380c66b8046SYuri Pankov struct eqn_def *def;
381c66b8046SYuri Pankov size_t start;
382c66b8046SYuri Pankov int diff, i, quoted;
383c66b8046SYuri Pankov enum eqn_tok tok;
384260e9a87SYuri Pankov
385c66b8046SYuri Pankov /*
386c66b8046SYuri Pankov * Reset the recursion counter after advancing
387c66b8046SYuri Pankov * beyond the end of the previous substitution.
388c66b8046SYuri Pankov */
389c66b8046SYuri Pankov if (ep->end - ep->data >= last_len)
390c66b8046SYuri Pankov lim = 0;
391260e9a87SYuri Pankov
392c66b8046SYuri Pankov ep->start = ep->end;
393c66b8046SYuri Pankov quoted = mode == MODE_QUOTED;
394c66b8046SYuri Pankov for (;;) {
395c66b8046SYuri Pankov switch (*ep->start) {
396c66b8046SYuri Pankov case '\0':
397c66b8046SYuri Pankov ep->toksz = 0;
398371584c2SYuri Pankov return EQN_TOK_EOF;
399c66b8046SYuri Pankov case '"':
400c66b8046SYuri Pankov quoted = 1;
401c66b8046SYuri Pankov break;
402*4d131170SRobert Mustacchi case ' ':
403*4d131170SRobert Mustacchi case '\t':
404*4d131170SRobert Mustacchi case '~':
405*4d131170SRobert Mustacchi case '^':
406*4d131170SRobert Mustacchi if (quoted)
407*4d131170SRobert Mustacchi break;
408*4d131170SRobert Mustacchi ep->start++;
409*4d131170SRobert Mustacchi continue;
410c66b8046SYuri Pankov default:
411c66b8046SYuri Pankov break;
412c66b8046SYuri Pankov }
413260e9a87SYuri Pankov if (quoted) {
414c66b8046SYuri Pankov ep->end = strchr(ep->start + 1, *ep->start);
415c66b8046SYuri Pankov ep->start++; /* Skip opening quote. */
416c66b8046SYuri Pankov if (ep->end == NULL) {
417cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_QUOTE,
418c66b8046SYuri Pankov ep->node->line, ep->node->pos, NULL);
419c66b8046SYuri Pankov ep->end = strchr(ep->start, '\0');
420c66b8046SYuri Pankov }
421c66b8046SYuri Pankov } else {
422c66b8046SYuri Pankov ep->end = ep->start + 1;
423c66b8046SYuri Pankov if (*ep->start != '{' && *ep->start != '}')
424c66b8046SYuri Pankov ep->end += strcspn(ep->end, " ^~\"{}\t");
425c66b8046SYuri Pankov }
426c66b8046SYuri Pankov ep->toksz = ep->end - ep->start;
427c66b8046SYuri Pankov if (quoted && *ep->end != '\0')
428c66b8046SYuri Pankov ep->end++; /* Skip closing quote. */
429c66b8046SYuri Pankov while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
430c66b8046SYuri Pankov ep->end++;
431c66b8046SYuri Pankov if (quoted) /* Cannot return, may have to strndup. */
432c66b8046SYuri Pankov break;
433c66b8046SYuri Pankov if (mode == MODE_NOSUB)
434c66b8046SYuri Pankov return EQN_TOK__MAX;
435c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL)
436c66b8046SYuri Pankov break;
437c66b8046SYuri Pankov if (++lim > EQN_NEST_MAX) {
438cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ROFFLOOP,
439c66b8046SYuri Pankov ep->node->line, ep->node->pos, NULL);
440c66b8046SYuri Pankov return EQN_TOK_EOF;
441c66b8046SYuri Pankov }
442c66b8046SYuri Pankov
443c66b8046SYuri Pankov /* Replace a defined name with its string value. */
444c66b8046SYuri Pankov if ((diff = def->valsz - ep->toksz) > 0) {
445c66b8046SYuri Pankov start = ep->start - ep->data;
446c66b8046SYuri Pankov ep->sz += diff;
447c66b8046SYuri Pankov ep->data = mandoc_realloc(ep->data, ep->sz + 1);
448c66b8046SYuri Pankov ep->start = ep->data + start;
449c66b8046SYuri Pankov }
450c66b8046SYuri Pankov if (diff)
451c66b8046SYuri Pankov memmove(ep->start + def->valsz, ep->start + ep->toksz,
452c66b8046SYuri Pankov strlen(ep->start + ep->toksz) + 1);
453c66b8046SYuri Pankov memcpy(ep->start, def->val, def->valsz);
454c66b8046SYuri Pankov last_len = ep->start - ep->data + def->valsz;
455c66b8046SYuri Pankov }
456c66b8046SYuri Pankov if (mode != MODE_TOK)
457c66b8046SYuri Pankov return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
458c66b8046SYuri Pankov if (quoted) {
459c66b8046SYuri Pankov ep->start = mandoc_strndup(ep->start, ep->toksz);
460c66b8046SYuri Pankov return EQN_TOK_QUOTED;
461c66b8046SYuri Pankov }
462c66b8046SYuri Pankov for (tok = 0; tok < EQN_TOK__MAX; tok++)
463c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz,
464c66b8046SYuri Pankov eqn_toks[tok], strlen(eqn_toks[tok])))
465c66b8046SYuri Pankov return tok;
466c66b8046SYuri Pankov
467c66b8046SYuri Pankov for (i = 0; i < EQNSYM__MAX; i++) {
468c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz,
469c66b8046SYuri Pankov eqnsyms[i].str, strlen(eqnsyms[i].str))) {
470c66b8046SYuri Pankov mandoc_asprintf(&ep->start,
471c66b8046SYuri Pankov "\\[%s]", eqnsyms[i].sym);
472c66b8046SYuri Pankov return EQN_TOK_SYM;
473c66b8046SYuri Pankov }
474c66b8046SYuri Pankov }
475c66b8046SYuri Pankov ep->start = mandoc_strndup(ep->start, ep->toksz);
476c66b8046SYuri Pankov for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
477c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz,
478c66b8046SYuri Pankov eqn_func[i], strlen(eqn_func[i])))
479c66b8046SYuri Pankov return EQN_TOK_FUNC;
480371584c2SYuri Pankov return EQN_TOK__MAX;
481260e9a87SYuri Pankov }
482260e9a87SYuri Pankov
483c66b8046SYuri Pankov void
eqn_box_free(struct eqn_box * bp)484260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp)
48595c635efSGarrett D'Amore {
486cec8643bSMichal Nowak if (bp == NULL)
487cec8643bSMichal Nowak return;
48895c635efSGarrett D'Amore
489260e9a87SYuri Pankov if (bp->first)
490260e9a87SYuri Pankov eqn_box_free(bp->first);
491260e9a87SYuri Pankov if (bp->next)
492260e9a87SYuri Pankov eqn_box_free(bp->next);
49395c635efSGarrett D'Amore
494260e9a87SYuri Pankov free(bp->text);
495260e9a87SYuri Pankov free(bp->left);
496260e9a87SYuri Pankov free(bp->right);
497260e9a87SYuri Pankov free(bp->top);
498260e9a87SYuri Pankov free(bp->bottom);
499260e9a87SYuri Pankov free(bp);
50095c635efSGarrett D'Amore }
50195c635efSGarrett D'Amore
502cec8643bSMichal Nowak struct eqn_box *
eqn_box_new(void)503cec8643bSMichal Nowak eqn_box_new(void)
504cec8643bSMichal Nowak {
505cec8643bSMichal Nowak struct eqn_box *bp;
506cec8643bSMichal Nowak
507cec8643bSMichal Nowak bp = mandoc_calloc(1, sizeof(*bp));
508cec8643bSMichal Nowak bp->expectargs = UINT_MAX;
509cec8643bSMichal Nowak return bp;
510cec8643bSMichal Nowak }
511cec8643bSMichal Nowak
512260e9a87SYuri Pankov /*
513260e9a87SYuri Pankov * Allocate a box as the last child of the parent node.
514260e9a87SYuri Pankov */
515260e9a87SYuri Pankov static struct eqn_box *
eqn_box_alloc(struct eqn_node * ep,struct eqn_box * parent)516260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
517260e9a87SYuri Pankov {
518260e9a87SYuri Pankov struct eqn_box *bp;
519260e9a87SYuri Pankov
520cec8643bSMichal Nowak bp = eqn_box_new();
521260e9a87SYuri Pankov bp->parent = parent;
522260e9a87SYuri Pankov bp->parent->args++;
523c66b8046SYuri Pankov bp->font = bp->parent->font;
524260e9a87SYuri Pankov bp->size = ep->gsize;
525260e9a87SYuri Pankov
526260e9a87SYuri Pankov if (NULL != parent->first) {
527260e9a87SYuri Pankov parent->last->next = bp;
528260e9a87SYuri Pankov bp->prev = parent->last;
529260e9a87SYuri Pankov } else
530260e9a87SYuri Pankov parent->first = bp;
531260e9a87SYuri Pankov
532260e9a87SYuri Pankov parent->last = bp;
533371584c2SYuri Pankov return bp;
534260e9a87SYuri Pankov }
535260e9a87SYuri Pankov
536260e9a87SYuri Pankov /*
537260e9a87SYuri Pankov * Reparent the current last node (of the current parent) under a new
538260e9a87SYuri Pankov * EQN_SUBEXPR as the first element.
539260e9a87SYuri Pankov * Then return the new parent.
540260e9a87SYuri Pankov * The new EQN_SUBEXPR will have a two-child limit.
541260e9a87SYuri Pankov */
542260e9a87SYuri Pankov static struct eqn_box *
eqn_box_makebinary(struct eqn_node * ep,struct eqn_box * parent)543c66b8046SYuri Pankov eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
544260e9a87SYuri Pankov {
545260e9a87SYuri Pankov struct eqn_box *b, *newb;
546260e9a87SYuri Pankov
547260e9a87SYuri Pankov assert(NULL != parent->last);
548260e9a87SYuri Pankov b = parent->last;
549260e9a87SYuri Pankov if (parent->last == parent->first)
550260e9a87SYuri Pankov parent->first = NULL;
551260e9a87SYuri Pankov parent->args--;
552260e9a87SYuri Pankov parent->last = b->prev;
553260e9a87SYuri Pankov b->prev = NULL;
554260e9a87SYuri Pankov newb = eqn_box_alloc(ep, parent);
555260e9a87SYuri Pankov newb->type = EQN_SUBEXPR;
556260e9a87SYuri Pankov newb->expectargs = 2;
557260e9a87SYuri Pankov newb->args = 1;
558260e9a87SYuri Pankov newb->first = newb->last = b;
559260e9a87SYuri Pankov newb->first->next = NULL;
560260e9a87SYuri Pankov b->parent = newb;
561371584c2SYuri Pankov return newb;
562260e9a87SYuri Pankov }
563260e9a87SYuri Pankov
564260e9a87SYuri Pankov /*
565260e9a87SYuri Pankov * Parse the "delim" control statement.
566260e9a87SYuri Pankov */
567260e9a87SYuri Pankov static void
eqn_delim(struct eqn_node * ep)568260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep)
569260e9a87SYuri Pankov {
570c66b8046SYuri Pankov if (ep->end[0] == '\0' || ep->end[1] == '\0') {
571cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY,
572c66b8046SYuri Pankov ep->node->line, ep->node->pos, "delim");
573c66b8046SYuri Pankov if (ep->end[0] != '\0')
574c66b8046SYuri Pankov ep->end++;
575c66b8046SYuri Pankov } else if (strncmp(ep->end, "off", 3) == 0) {
576260e9a87SYuri Pankov ep->delim = 0;
577c66b8046SYuri Pankov ep->end += 3;
578c66b8046SYuri Pankov } else if (strncmp(ep->end, "on", 2) == 0) {
579260e9a87SYuri Pankov if (ep->odelim && ep->cdelim)
580260e9a87SYuri Pankov ep->delim = 1;
581c66b8046SYuri Pankov ep->end += 2;
582c66b8046SYuri Pankov } else {
583c66b8046SYuri Pankov ep->odelim = *ep->end++;
584c66b8046SYuri Pankov ep->cdelim = *ep->end++;
585260e9a87SYuri Pankov ep->delim = 1;
586260e9a87SYuri Pankov }
587260e9a87SYuri Pankov }
588260e9a87SYuri Pankov
589260e9a87SYuri Pankov /*
590260e9a87SYuri Pankov * Undefine a previously-defined string.
591260e9a87SYuri Pankov */
592260e9a87SYuri Pankov static void
eqn_undef(struct eqn_node * ep)593260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep)
594260e9a87SYuri Pankov {
595260e9a87SYuri Pankov struct eqn_def *def;
596260e9a87SYuri Pankov
597c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
598cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY,
599c66b8046SYuri Pankov ep->node->line, ep->node->pos, "undef");
600260e9a87SYuri Pankov return;
601260e9a87SYuri Pankov }
602c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL)
603260e9a87SYuri Pankov return;
604260e9a87SYuri Pankov free(def->key);
605260e9a87SYuri Pankov free(def->val);
606260e9a87SYuri Pankov def->key = def->val = NULL;
607260e9a87SYuri Pankov def->keysz = def->valsz = 0;
608260e9a87SYuri Pankov }
609260e9a87SYuri Pankov
610260e9a87SYuri Pankov static void
eqn_def(struct eqn_node * ep)611260e9a87SYuri Pankov eqn_def(struct eqn_node *ep)
61295c635efSGarrett D'Amore {
61395c635efSGarrett D'Amore struct eqn_def *def;
61495c635efSGarrett D'Amore int i;
61595c635efSGarrett D'Amore
616c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
617cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY,
618c66b8046SYuri Pankov ep->node->line, ep->node->pos, "define");
619260e9a87SYuri Pankov return;
62095c635efSGarrett D'Amore }
62195c635efSGarrett D'Amore
62295c635efSGarrett D'Amore /*
62395c635efSGarrett D'Amore * Search for a key that already exists.
62495c635efSGarrett D'Amore * Create a new key if none is found.
62595c635efSGarrett D'Amore */
626c66b8046SYuri Pankov if ((def = eqn_def_find(ep)) == NULL) {
62795c635efSGarrett D'Amore /* Find holes in string array. */
62895c635efSGarrett D'Amore for (i = 0; i < (int)ep->defsz; i++)
62995c635efSGarrett D'Amore if (0 == ep->defs[i].keysz)
63095c635efSGarrett D'Amore break;
63195c635efSGarrett D'Amore
63295c635efSGarrett D'Amore if (i == (int)ep->defsz) {
63395c635efSGarrett D'Amore ep->defsz++;
634260e9a87SYuri Pankov ep->defs = mandoc_reallocarray(ep->defs,
635260e9a87SYuri Pankov ep->defsz, sizeof(struct eqn_def));
63695c635efSGarrett D'Amore ep->defs[i].key = ep->defs[i].val = NULL;
63795c635efSGarrett D'Amore }
63895c635efSGarrett D'Amore
639260e9a87SYuri Pankov def = ep->defs + i;
640260e9a87SYuri Pankov free(def->key);
641c66b8046SYuri Pankov def->key = mandoc_strndup(ep->start, ep->toksz);
642c66b8046SYuri Pankov def->keysz = ep->toksz;
64395c635efSGarrett D'Amore }
64495c635efSGarrett D'Amore
645c66b8046SYuri Pankov if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
646cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY,
647c66b8046SYuri Pankov ep->node->line, ep->node->pos, "define %s", def->key);
648260e9a87SYuri Pankov free(def->key);
649260e9a87SYuri Pankov free(def->val);
650260e9a87SYuri Pankov def->key = def->val = NULL;
651260e9a87SYuri Pankov def->keysz = def->valsz = 0;
652260e9a87SYuri Pankov return;
65395c635efSGarrett D'Amore }
654260e9a87SYuri Pankov free(def->val);
655c66b8046SYuri Pankov def->val = mandoc_strndup(ep->start, ep->toksz);
656c66b8046SYuri Pankov def->valsz = ep->toksz;
65795c635efSGarrett D'Amore }
65895c635efSGarrett D'Amore
659c66b8046SYuri Pankov void
eqn_parse(struct eqn_node * ep)660c66b8046SYuri Pankov eqn_parse(struct eqn_node *ep)
66195c635efSGarrett D'Amore {
662c66b8046SYuri Pankov struct eqn_box *cur, *nbox, *parent, *split;
663c66b8046SYuri Pankov const char *cp, *cpn;
664260e9a87SYuri Pankov char *p;
665c66b8046SYuri Pankov enum eqn_tok tok;
666c66b8046SYuri Pankov enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
667260e9a87SYuri Pankov int size;
66895c635efSGarrett D'Amore
669c66b8046SYuri Pankov parent = ep->node->eqn;
670260e9a87SYuri Pankov assert(parent != NULL);
671260e9a87SYuri Pankov
672260e9a87SYuri Pankov /*
673260e9a87SYuri Pankov * Empty equation.
674260e9a87SYuri Pankov * Do not add it to the high-level syntax tree.
675260e9a87SYuri Pankov */
676260e9a87SYuri Pankov
677260e9a87SYuri Pankov if (ep->data == NULL)
678c66b8046SYuri Pankov return;
679c66b8046SYuri Pankov
680*4d131170SRobert Mustacchi ep->start = ep->end = ep->data;
681260e9a87SYuri Pankov
682260e9a87SYuri Pankov next_tok:
683c66b8046SYuri Pankov tok = eqn_next(ep, MODE_TOK);
684260e9a87SYuri Pankov switch (tok) {
685c66b8046SYuri Pankov case EQN_TOK_UNDEF:
686260e9a87SYuri Pankov eqn_undef(ep);
687260e9a87SYuri Pankov break;
688c66b8046SYuri Pankov case EQN_TOK_NDEFINE:
689c66b8046SYuri Pankov case EQN_TOK_DEFINE:
690260e9a87SYuri Pankov eqn_def(ep);
691260e9a87SYuri Pankov break;
692c66b8046SYuri Pankov case EQN_TOK_TDEFINE:
693c66b8046SYuri Pankov if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
694c66b8046SYuri Pankov eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
695cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY,
696c66b8046SYuri Pankov ep->node->line, ep->node->pos, "tdefine");
697260e9a87SYuri Pankov break;
698c66b8046SYuri Pankov case EQN_TOK_DELIM:
699260e9a87SYuri Pankov eqn_delim(ep);
700260e9a87SYuri Pankov break;
701c66b8046SYuri Pankov case EQN_TOK_GFONT:
702c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
703cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
704cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
705260e9a87SYuri Pankov break;
706c66b8046SYuri Pankov case EQN_TOK_MARK:
707c66b8046SYuri Pankov case EQN_TOK_LINEUP:
708260e9a87SYuri Pankov /* Ignore these. */
709260e9a87SYuri Pankov break;
710c66b8046SYuri Pankov case EQN_TOK_DYAD:
711c66b8046SYuri Pankov case EQN_TOK_VEC:
712c66b8046SYuri Pankov case EQN_TOK_UNDER:
713c66b8046SYuri Pankov case EQN_TOK_BAR:
714c66b8046SYuri Pankov case EQN_TOK_TILDE:
715c66b8046SYuri Pankov case EQN_TOK_HAT:
716c66b8046SYuri Pankov case EQN_TOK_DOT:
717c66b8046SYuri Pankov case EQN_TOK_DOTDOT:
718260e9a87SYuri Pankov if (parent->last == NULL) {
719cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
720cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
721260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent);
722260e9a87SYuri Pankov cur->type = EQN_TEXT;
723260e9a87SYuri Pankov cur->text = mandoc_strdup("");
72495c635efSGarrett D'Amore }
725c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent);
726c66b8046SYuri Pankov parent->type = EQN_LIST;
727260e9a87SYuri Pankov parent->expectargs = 1;
728c66b8046SYuri Pankov parent->font = EQNFONT_ROMAN;
729260e9a87SYuri Pankov switch (tok) {
730c66b8046SYuri Pankov case EQN_TOK_DOTDOT:
731c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[ad]");
732260e9a87SYuri Pankov break;
733c66b8046SYuri Pankov case EQN_TOK_VEC:
734c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[->]");
735260e9a87SYuri Pankov break;
736c66b8046SYuri Pankov case EQN_TOK_DYAD:
737c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[<>]");
738260e9a87SYuri Pankov break;
739c66b8046SYuri Pankov case EQN_TOK_TILDE:
740c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[a~]");
741260e9a87SYuri Pankov break;
742c66b8046SYuri Pankov case EQN_TOK_UNDER:
743c66b8046SYuri Pankov parent->bottom = mandoc_strdup("\\[ul]");
744260e9a87SYuri Pankov break;
745c66b8046SYuri Pankov case EQN_TOK_BAR:
746c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[rn]");
747260e9a87SYuri Pankov break;
748c66b8046SYuri Pankov case EQN_TOK_DOT:
749c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[a.]");
750260e9a87SYuri Pankov break;
751c66b8046SYuri Pankov case EQN_TOK_HAT:
752c66b8046SYuri Pankov parent->top = mandoc_strdup("\\[ha]");
753260e9a87SYuri Pankov break;
754260e9a87SYuri Pankov default:
755260e9a87SYuri Pankov abort();
756260e9a87SYuri Pankov }
757260e9a87SYuri Pankov parent = parent->parent;
758260e9a87SYuri Pankov break;
759c66b8046SYuri Pankov case EQN_TOK_FWD:
760c66b8046SYuri Pankov case EQN_TOK_BACK:
761c66b8046SYuri Pankov case EQN_TOK_DOWN:
762c66b8046SYuri Pankov case EQN_TOK_UP:
763c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
764cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
765cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
766260e9a87SYuri Pankov break;
767c66b8046SYuri Pankov case EQN_TOK_FAT:
768c66b8046SYuri Pankov case EQN_TOK_ROMAN:
769c66b8046SYuri Pankov case EQN_TOK_ITALIC:
770c66b8046SYuri Pankov case EQN_TOK_BOLD:
771260e9a87SYuri Pankov while (parent->args == parent->expectargs)
772260e9a87SYuri Pankov parent = parent->parent;
773260e9a87SYuri Pankov /*
774260e9a87SYuri Pankov * These values apply to the next word or sequence of
775260e9a87SYuri Pankov * words; thus, we mark that we'll have a child with
776260e9a87SYuri Pankov * exactly one of those.
777260e9a87SYuri Pankov */
778260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent);
779c66b8046SYuri Pankov parent->type = EQN_LIST;
780260e9a87SYuri Pankov parent->expectargs = 1;
781260e9a87SYuri Pankov switch (tok) {
782c66b8046SYuri Pankov case EQN_TOK_FAT:
783260e9a87SYuri Pankov parent->font = EQNFONT_FAT;
784260e9a87SYuri Pankov break;
785c66b8046SYuri Pankov case EQN_TOK_ROMAN:
786260e9a87SYuri Pankov parent->font = EQNFONT_ROMAN;
787260e9a87SYuri Pankov break;
788c66b8046SYuri Pankov case EQN_TOK_ITALIC:
789260e9a87SYuri Pankov parent->font = EQNFONT_ITALIC;
790260e9a87SYuri Pankov break;
791c66b8046SYuri Pankov case EQN_TOK_BOLD:
792260e9a87SYuri Pankov parent->font = EQNFONT_BOLD;
793260e9a87SYuri Pankov break;
794260e9a87SYuri Pankov default:
795260e9a87SYuri Pankov abort();
796260e9a87SYuri Pankov }
797260e9a87SYuri Pankov break;
798c66b8046SYuri Pankov case EQN_TOK_SIZE:
799c66b8046SYuri Pankov case EQN_TOK_GSIZE:
800260e9a87SYuri Pankov /* Accept two values: integral size and a single. */
801c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
802cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
803cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
804260e9a87SYuri Pankov break;
805260e9a87SYuri Pankov }
806c66b8046SYuri Pankov size = mandoc_strntoi(ep->start, ep->toksz, 10);
807260e9a87SYuri Pankov if (-1 == size) {
808cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
809cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
810260e9a87SYuri Pankov break;
811260e9a87SYuri Pankov }
812260e9a87SYuri Pankov if (EQN_TOK_GSIZE == tok) {
813260e9a87SYuri Pankov ep->gsize = size;
814260e9a87SYuri Pankov break;
815260e9a87SYuri Pankov }
816c66b8046SYuri Pankov while (parent->args == parent->expectargs)
817c66b8046SYuri Pankov parent = parent->parent;
818260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent);
819c66b8046SYuri Pankov parent->type = EQN_LIST;
820260e9a87SYuri Pankov parent->expectargs = 1;
821260e9a87SYuri Pankov parent->size = size;
822260e9a87SYuri Pankov break;
823c66b8046SYuri Pankov case EQN_TOK_FROM:
824c66b8046SYuri Pankov case EQN_TOK_TO:
825c66b8046SYuri Pankov case EQN_TOK_SUB:
826c66b8046SYuri Pankov case EQN_TOK_SUP:
827260e9a87SYuri Pankov /*
828260e9a87SYuri Pankov * We have a left-right-associative expression.
829260e9a87SYuri Pankov * Repivot under a positional node, open a child scope
830260e9a87SYuri Pankov * and keep on reading.
831260e9a87SYuri Pankov */
832260e9a87SYuri Pankov if (parent->last == NULL) {
833cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
834cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
835260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent);
836260e9a87SYuri Pankov cur->type = EQN_TEXT;
837260e9a87SYuri Pankov cur->text = mandoc_strdup("");
838260e9a87SYuri Pankov }
839c66b8046SYuri Pankov while (parent->expectargs == 1 && parent->args == 1)
840c66b8046SYuri Pankov parent = parent->parent;
841c66b8046SYuri Pankov if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) {
842c66b8046SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent)
843c66b8046SYuri Pankov if (cur->pos == EQNPOS_SUB ||
844c66b8046SYuri Pankov cur->pos == EQNPOS_SUP ||
845c66b8046SYuri Pankov cur->pos == EQNPOS_SUBSUP ||
846c66b8046SYuri Pankov cur->pos == EQNPOS_SQRT ||
847c66b8046SYuri Pankov cur->pos == EQNPOS_OVER)
848c66b8046SYuri Pankov break;
849c66b8046SYuri Pankov if (cur != NULL)
850c66b8046SYuri Pankov parent = cur->parent;
851c66b8046SYuri Pankov }
852c66b8046SYuri Pankov if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
853260e9a87SYuri Pankov parent->expectargs = 3;
854260e9a87SYuri Pankov parent->pos = EQNPOS_SUBSUP;
855260e9a87SYuri Pankov break;
856260e9a87SYuri Pankov }
857c66b8046SYuri Pankov if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
858260e9a87SYuri Pankov parent->expectargs = 3;
859260e9a87SYuri Pankov parent->pos = EQNPOS_FROMTO;
860260e9a87SYuri Pankov break;
861260e9a87SYuri Pankov }
862c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent);
863260e9a87SYuri Pankov switch (tok) {
864c66b8046SYuri Pankov case EQN_TOK_FROM:
865c66b8046SYuri Pankov parent->pos = EQNPOS_FROM;
866260e9a87SYuri Pankov break;
867c66b8046SYuri Pankov case EQN_TOK_TO:
868c66b8046SYuri Pankov parent->pos = EQNPOS_TO;
869260e9a87SYuri Pankov break;
870c66b8046SYuri Pankov case EQN_TOK_SUP:
871c66b8046SYuri Pankov parent->pos = EQNPOS_SUP;
872260e9a87SYuri Pankov break;
873c66b8046SYuri Pankov case EQN_TOK_SUB:
874c66b8046SYuri Pankov parent->pos = EQNPOS_SUB;
875260e9a87SYuri Pankov break;
876260e9a87SYuri Pankov default:
877260e9a87SYuri Pankov abort();
878260e9a87SYuri Pankov }
879260e9a87SYuri Pankov break;
880c66b8046SYuri Pankov case EQN_TOK_SQRT:
881260e9a87SYuri Pankov while (parent->args == parent->expectargs)
882260e9a87SYuri Pankov parent = parent->parent;
883260e9a87SYuri Pankov /*
884260e9a87SYuri Pankov * Accept a left-right-associative set of arguments just
885260e9a87SYuri Pankov * like sub and sup and friends but without rebalancing
886260e9a87SYuri Pankov * under a pivot.
887260e9a87SYuri Pankov */
888260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent);
889260e9a87SYuri Pankov parent->type = EQN_SUBEXPR;
890260e9a87SYuri Pankov parent->pos = EQNPOS_SQRT;
891260e9a87SYuri Pankov parent->expectargs = 1;
892260e9a87SYuri Pankov break;
893c66b8046SYuri Pankov case EQN_TOK_OVER:
894260e9a87SYuri Pankov /*
895260e9a87SYuri Pankov * We have a right-left-associative fraction.
896260e9a87SYuri Pankov * Close out anything that's currently open, then
897260e9a87SYuri Pankov * rebalance and continue reading.
898260e9a87SYuri Pankov */
899260e9a87SYuri Pankov if (parent->last == NULL) {
900cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
901cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
902260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent);
903260e9a87SYuri Pankov cur->type = EQN_TEXT;
904260e9a87SYuri Pankov cur->text = mandoc_strdup("");
905260e9a87SYuri Pankov }
906c66b8046SYuri Pankov while (parent->args == parent->expectargs)
907c66b8046SYuri Pankov parent = parent->parent;
908260e9a87SYuri Pankov while (EQN_SUBEXPR == parent->type)
909260e9a87SYuri Pankov parent = parent->parent;
910c66b8046SYuri Pankov parent = eqn_box_makebinary(ep, parent);
911c66b8046SYuri Pankov parent->pos = EQNPOS_OVER;
912260e9a87SYuri Pankov break;
913c66b8046SYuri Pankov case EQN_TOK_RIGHT:
914c66b8046SYuri Pankov case EQN_TOK_BRACE_CLOSE:
915260e9a87SYuri Pankov /*
916260e9a87SYuri Pankov * Close out the existing brace.
917260e9a87SYuri Pankov * FIXME: this is a shitty sentinel: we should really
918260e9a87SYuri Pankov * have a native EQN_BRACE type or whatnot.
919260e9a87SYuri Pankov */
920260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent)
921260e9a87SYuri Pankov if (cur->type == EQN_LIST &&
922c66b8046SYuri Pankov cur->expectargs > 1 &&
923260e9a87SYuri Pankov (tok == EQN_TOK_BRACE_CLOSE ||
924260e9a87SYuri Pankov cur->left != NULL))
925260e9a87SYuri Pankov break;
926260e9a87SYuri Pankov if (cur == NULL) {
927cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
928cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
929260e9a87SYuri Pankov break;
930260e9a87SYuri Pankov }
931260e9a87SYuri Pankov parent = cur;
932260e9a87SYuri Pankov if (EQN_TOK_RIGHT == tok) {
933c66b8046SYuri Pankov if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
934260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY,
935cec8643bSMichal Nowak ep->node->line, ep->node->pos,
936cec8643bSMichal Nowak "%s", eqn_toks[tok]);
937260e9a87SYuri Pankov break;
938260e9a87SYuri Pankov }
939260e9a87SYuri Pankov /* Handling depends on right/left. */
940c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
941c66b8046SYuri Pankov parent->right = mandoc_strdup("\\[rc]");
942c66b8046SYuri Pankov else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
943c66b8046SYuri Pankov parent->right = mandoc_strdup("\\[rf]");
944c66b8046SYuri Pankov else
945c66b8046SYuri Pankov parent->right =
946c66b8046SYuri Pankov mandoc_strndup(ep->start, ep->toksz);
947260e9a87SYuri Pankov }
948260e9a87SYuri Pankov parent = parent->parent;
949371584c2SYuri Pankov if (tok == EQN_TOK_BRACE_CLOSE &&
950260e9a87SYuri Pankov (parent->type == EQN_PILE ||
951260e9a87SYuri Pankov parent->type == EQN_MATRIX))
952260e9a87SYuri Pankov parent = parent->parent;
953260e9a87SYuri Pankov /* Close out any "singleton" lists. */
954c66b8046SYuri Pankov while (parent->type == EQN_LIST &&
955c66b8046SYuri Pankov parent->expectargs == 1 &&
956c66b8046SYuri Pankov parent->args == 1)
957260e9a87SYuri Pankov parent = parent->parent;
958260e9a87SYuri Pankov break;
959c66b8046SYuri Pankov case EQN_TOK_BRACE_OPEN:
960c66b8046SYuri Pankov case EQN_TOK_LEFT:
961260e9a87SYuri Pankov /*
962260e9a87SYuri Pankov * If we already have something in the stack and we're
963260e9a87SYuri Pankov * in an expression, then rewind til we're not any more
964260e9a87SYuri Pankov * (just like with the text node).
965260e9a87SYuri Pankov */
966260e9a87SYuri Pankov while (parent->args == parent->expectargs)
967260e9a87SYuri Pankov parent = parent->parent;
968260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok &&
969c66b8046SYuri Pankov eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
970cec8643bSMichal Nowak mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
971cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
972260e9a87SYuri Pankov break;
973260e9a87SYuri Pankov }
974260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent);
975260e9a87SYuri Pankov parent->type = EQN_LIST;
976260e9a87SYuri Pankov if (EQN_TOK_LEFT == tok) {
977c66b8046SYuri Pankov if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
978c66b8046SYuri Pankov parent->left = mandoc_strdup("\\[lc]");
979c66b8046SYuri Pankov else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
980c66b8046SYuri Pankov parent->left = mandoc_strdup("\\[lf]");
981c66b8046SYuri Pankov else
982c66b8046SYuri Pankov parent->left =
983c66b8046SYuri Pankov mandoc_strndup(ep->start, ep->toksz);
984260e9a87SYuri Pankov }
985260e9a87SYuri Pankov break;
986c66b8046SYuri Pankov case EQN_TOK_PILE:
987c66b8046SYuri Pankov case EQN_TOK_LPILE:
988c66b8046SYuri Pankov case EQN_TOK_RPILE:
989c66b8046SYuri Pankov case EQN_TOK_CPILE:
990c66b8046SYuri Pankov case EQN_TOK_CCOL:
991c66b8046SYuri Pankov case EQN_TOK_LCOL:
992c66b8046SYuri Pankov case EQN_TOK_RCOL:
993260e9a87SYuri Pankov while (parent->args == parent->expectargs)
994260e9a87SYuri Pankov parent = parent->parent;
995260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent);
996260e9a87SYuri Pankov parent->type = EQN_PILE;
997260e9a87SYuri Pankov parent->expectargs = 1;
998260e9a87SYuri Pankov break;
999c66b8046SYuri Pankov case EQN_TOK_ABOVE:
1000260e9a87SYuri Pankov for (cur = parent; cur != NULL; cur = cur->parent)
1001260e9a87SYuri Pankov if (cur->type == EQN_PILE)
1002260e9a87SYuri Pankov break;
1003260e9a87SYuri Pankov if (cur == NULL) {
1004cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
1005cec8643bSMichal Nowak ep->node->pos, "%s", eqn_toks[tok]);
1006260e9a87SYuri Pankov break;
1007260e9a87SYuri Pankov }
1008260e9a87SYuri Pankov parent = eqn_box_alloc(ep, cur);
1009260e9a87SYuri Pankov parent->type = EQN_LIST;
1010260e9a87SYuri Pankov break;
1011c66b8046SYuri Pankov case EQN_TOK_MATRIX:
1012260e9a87SYuri Pankov while (parent->args == parent->expectargs)
1013260e9a87SYuri Pankov parent = parent->parent;
1014260e9a87SYuri Pankov parent = eqn_box_alloc(ep, parent);
1015260e9a87SYuri Pankov parent->type = EQN_MATRIX;
1016260e9a87SYuri Pankov parent->expectargs = 1;
1017260e9a87SYuri Pankov break;
1018c66b8046SYuri Pankov case EQN_TOK_EOF:
1019c66b8046SYuri Pankov return;
1020c66b8046SYuri Pankov case EQN_TOK__MAX:
1021c66b8046SYuri Pankov case EQN_TOK_FUNC:
1022c66b8046SYuri Pankov case EQN_TOK_QUOTED:
1023c66b8046SYuri Pankov case EQN_TOK_SYM:
1024c66b8046SYuri Pankov p = ep->start;
1025c66b8046SYuri Pankov assert(p != NULL);
1026260e9a87SYuri Pankov /*
1027260e9a87SYuri Pankov * If we already have something in the stack and we're
1028260e9a87SYuri Pankov * in an expression, then rewind til we're not any more.
1029260e9a87SYuri Pankov */
1030260e9a87SYuri Pankov while (parent->args == parent->expectargs)
1031260e9a87SYuri Pankov parent = parent->parent;
1032260e9a87SYuri Pankov cur = eqn_box_alloc(ep, parent);
1033260e9a87SYuri Pankov cur->type = EQN_TEXT;
1034c66b8046SYuri Pankov cur->text = p;
1035c66b8046SYuri Pankov switch (tok) {
1036c66b8046SYuri Pankov case EQN_TOK_FUNC:
1037c66b8046SYuri Pankov cur->font = EQNFONT_ROMAN;
1038c66b8046SYuri Pankov break;
1039c66b8046SYuri Pankov case EQN_TOK_QUOTED:
1040c66b8046SYuri Pankov if (cur->font == EQNFONT_NONE)
1041c66b8046SYuri Pankov cur->font = EQNFONT_ITALIC;
1042c66b8046SYuri Pankov break;
1043c66b8046SYuri Pankov case EQN_TOK_SYM:
1044c66b8046SYuri Pankov break;
1045c66b8046SYuri Pankov default:
1046c66b8046SYuri Pankov if (cur->font != EQNFONT_NONE || *p == '\0')
1047c66b8046SYuri Pankov break;
1048c66b8046SYuri Pankov cpn = p - 1;
1049c66b8046SYuri Pankov ccln = CCL_LET;
1050c66b8046SYuri Pankov split = NULL;
1051c66b8046SYuri Pankov for (;;) {
1052c66b8046SYuri Pankov /* Advance to next character. */
1053c66b8046SYuri Pankov cp = cpn++;
1054c66b8046SYuri Pankov ccl = ccln;
1055c66b8046SYuri Pankov ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
1056c66b8046SYuri Pankov isdigit((unsigned char)*cpn) ||
1057c66b8046SYuri Pankov (*cpn == '.' && (ccl == CCL_DIG ||
1058c66b8046SYuri Pankov isdigit((unsigned char)cpn[1]))) ?
1059c66b8046SYuri Pankov CCL_DIG : CCL_PUN;
1060c66b8046SYuri Pankov /* No boundary before first character. */
1061c66b8046SYuri Pankov if (cp < p)
1062c66b8046SYuri Pankov continue;
1063c66b8046SYuri Pankov cur->font = ccl == CCL_LET ?
1064c66b8046SYuri Pankov EQNFONT_ITALIC : EQNFONT_ROMAN;
1065c66b8046SYuri Pankov if (*cp == '\\')
1066c66b8046SYuri Pankov mandoc_escape(&cpn, NULL, NULL);
1067c66b8046SYuri Pankov /* No boundary after last character. */
1068c66b8046SYuri Pankov if (*cpn == '\0')
1069c66b8046SYuri Pankov break;
1070c66b8046SYuri Pankov if (ccln == ccl && *cp != ',' && *cpn != ',')
1071c66b8046SYuri Pankov continue;
1072c66b8046SYuri Pankov /* Boundary found, split the text. */
1073c66b8046SYuri Pankov if (parent->args == parent->expectargs) {
1074c66b8046SYuri Pankov /* Remove the text from the tree. */
1075c66b8046SYuri Pankov if (cur->prev == NULL)
1076c66b8046SYuri Pankov parent->first = cur->next;
1077c66b8046SYuri Pankov else
1078c66b8046SYuri Pankov cur->prev->next = NULL;
1079c66b8046SYuri Pankov parent->last = cur->prev;
1080c66b8046SYuri Pankov parent->args--;
1081c66b8046SYuri Pankov /* Set up a list instead. */
1082c66b8046SYuri Pankov split = eqn_box_alloc(ep, parent);
1083c66b8046SYuri Pankov split->type = EQN_LIST;
1084c66b8046SYuri Pankov /* Insert the word into the list. */
1085c66b8046SYuri Pankov split->first = split->last = cur;
1086c66b8046SYuri Pankov cur->parent = split;
1087c66b8046SYuri Pankov cur->prev = NULL;
1088c66b8046SYuri Pankov parent = split;
1089c66b8046SYuri Pankov }
1090c66b8046SYuri Pankov /* Append a new text box. */
1091c66b8046SYuri Pankov nbox = eqn_box_alloc(ep, parent);
1092c66b8046SYuri Pankov nbox->type = EQN_TEXT;
1093c66b8046SYuri Pankov nbox->text = mandoc_strdup(cpn);
1094c66b8046SYuri Pankov /* Truncate the old box. */
1095c66b8046SYuri Pankov p = mandoc_strndup(cur->text,
1096c66b8046SYuri Pankov cpn - cur->text);
1097c66b8046SYuri Pankov free(cur->text);
1098c66b8046SYuri Pankov cur->text = p;
1099c66b8046SYuri Pankov /* Setup to process the new box. */
1100c66b8046SYuri Pankov cur = nbox;
1101c66b8046SYuri Pankov p = nbox->text;
1102c66b8046SYuri Pankov cpn = p - 1;
1103c66b8046SYuri Pankov ccln = CCL_LET;
1104c66b8046SYuri Pankov }
1105c66b8046SYuri Pankov if (split != NULL)
1106c66b8046SYuri Pankov parent = split->parent;
1107260e9a87SYuri Pankov break;
1108260e9a87SYuri Pankov }
1109260e9a87SYuri Pankov break;
1110c66b8046SYuri Pankov default:
1111c66b8046SYuri Pankov abort();
1112260e9a87SYuri Pankov }
1113260e9a87SYuri Pankov goto next_tok;
1114260e9a87SYuri Pankov }
1115260e9a87SYuri Pankov
1116260e9a87SYuri Pankov void
eqn_free(struct eqn_node * p)1117260e9a87SYuri Pankov eqn_free(struct eqn_node *p)
111895c635efSGarrett D'Amore {
111995c635efSGarrett D'Amore int i;
112095c635efSGarrett D'Amore
1121cec8643bSMichal Nowak if (p == NULL)
1122cec8643bSMichal Nowak return;
1123cec8643bSMichal Nowak
1124260e9a87SYuri Pankov for (i = 0; i < (int)p->defsz; i++) {
1125260e9a87SYuri Pankov free(p->defs[i].key);
1126260e9a87SYuri Pankov free(p->defs[i].val);
1127260e9a87SYuri Pankov }
1128260e9a87SYuri Pankov
1129260e9a87SYuri Pankov free(p->data);
1130260e9a87SYuri Pankov free(p->defs);
1131260e9a87SYuri Pankov free(p);
113295c635efSGarrett D'Amore }
1133