xref: /illumos-gate/usr/src/cmd/mandoc/eqn.c (revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f)
1*260e9a87SYuri Pankov /*	$Id: eqn.c,v 1.58 2015/03/04 12:19:49 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*260e9a87SYuri Pankov  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*260e9a87SYuri Pankov  * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
19*260e9a87SYuri Pankov 
20*260e9a87SYuri Pankov #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <limits.h>
2495c635efSGarrett D'Amore #include <stdio.h>
2595c635efSGarrett D'Amore #include <stdlib.h>
2695c635efSGarrett D'Amore #include <string.h>
2795c635efSGarrett D'Amore #include <time.h>
2895c635efSGarrett D'Amore 
2995c635efSGarrett D'Amore #include "mandoc.h"
30*260e9a87SYuri Pankov #include "mandoc_aux.h"
3195c635efSGarrett D'Amore #include "libmandoc.h"
3295c635efSGarrett D'Amore #include "libroff.h"
3395c635efSGarrett D'Amore 
3495c635efSGarrett D'Amore #define	EQN_NEST_MAX	 128 /* maximum nesting of defines */
35*260e9a87SYuri Pankov #define	STRNEQ(p1, sz1, p2, sz2) \
36*260e9a87SYuri Pankov 	((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
3795c635efSGarrett D'Amore 
38*260e9a87SYuri Pankov enum	eqn_tok {
39*260e9a87SYuri Pankov 	EQN_TOK_DYAD = 0,
40*260e9a87SYuri Pankov 	EQN_TOK_VEC,
41*260e9a87SYuri Pankov 	EQN_TOK_UNDER,
42*260e9a87SYuri Pankov 	EQN_TOK_BAR,
43*260e9a87SYuri Pankov 	EQN_TOK_TILDE,
44*260e9a87SYuri Pankov 	EQN_TOK_HAT,
45*260e9a87SYuri Pankov 	EQN_TOK_DOT,
46*260e9a87SYuri Pankov 	EQN_TOK_DOTDOT,
47*260e9a87SYuri Pankov 	EQN_TOK_FWD,
48*260e9a87SYuri Pankov 	EQN_TOK_BACK,
49*260e9a87SYuri Pankov 	EQN_TOK_DOWN,
50*260e9a87SYuri Pankov 	EQN_TOK_UP,
51*260e9a87SYuri Pankov 	EQN_TOK_FAT,
52*260e9a87SYuri Pankov 	EQN_TOK_ROMAN,
53*260e9a87SYuri Pankov 	EQN_TOK_ITALIC,
54*260e9a87SYuri Pankov 	EQN_TOK_BOLD,
55*260e9a87SYuri Pankov 	EQN_TOK_SIZE,
56*260e9a87SYuri Pankov 	EQN_TOK_SUB,
57*260e9a87SYuri Pankov 	EQN_TOK_SUP,
58*260e9a87SYuri Pankov 	EQN_TOK_SQRT,
59*260e9a87SYuri Pankov 	EQN_TOK_OVER,
60*260e9a87SYuri Pankov 	EQN_TOK_FROM,
61*260e9a87SYuri Pankov 	EQN_TOK_TO,
62*260e9a87SYuri Pankov 	EQN_TOK_BRACE_OPEN,
63*260e9a87SYuri Pankov 	EQN_TOK_BRACE_CLOSE,
64*260e9a87SYuri Pankov 	EQN_TOK_GSIZE,
65*260e9a87SYuri Pankov 	EQN_TOK_GFONT,
66*260e9a87SYuri Pankov 	EQN_TOK_MARK,
67*260e9a87SYuri Pankov 	EQN_TOK_LINEUP,
68*260e9a87SYuri Pankov 	EQN_TOK_LEFT,
69*260e9a87SYuri Pankov 	EQN_TOK_RIGHT,
70*260e9a87SYuri Pankov 	EQN_TOK_PILE,
71*260e9a87SYuri Pankov 	EQN_TOK_LPILE,
72*260e9a87SYuri Pankov 	EQN_TOK_RPILE,
73*260e9a87SYuri Pankov 	EQN_TOK_CPILE,
74*260e9a87SYuri Pankov 	EQN_TOK_MATRIX,
75*260e9a87SYuri Pankov 	EQN_TOK_CCOL,
76*260e9a87SYuri Pankov 	EQN_TOK_LCOL,
77*260e9a87SYuri Pankov 	EQN_TOK_RCOL,
78*260e9a87SYuri Pankov 	EQN_TOK_DELIM,
79*260e9a87SYuri Pankov 	EQN_TOK_DEFINE,
80*260e9a87SYuri Pankov 	EQN_TOK_TDEFINE,
81*260e9a87SYuri Pankov 	EQN_TOK_NDEFINE,
82*260e9a87SYuri Pankov 	EQN_TOK_UNDEF,
83*260e9a87SYuri Pankov 	EQN_TOK_EOF,
84*260e9a87SYuri Pankov 	EQN_TOK_ABOVE,
85*260e9a87SYuri Pankov 	EQN_TOK__MAX
86*260e9a87SYuri Pankov };
87*260e9a87SYuri Pankov 
88*260e9a87SYuri Pankov static	const char *eqn_toks[EQN_TOK__MAX] = {
89*260e9a87SYuri Pankov 	"dyad", /* EQN_TOK_DYAD */
90*260e9a87SYuri Pankov 	"vec", /* EQN_TOK_VEC */
91*260e9a87SYuri Pankov 	"under", /* EQN_TOK_UNDER */
92*260e9a87SYuri Pankov 	"bar", /* EQN_TOK_BAR */
93*260e9a87SYuri Pankov 	"tilde", /* EQN_TOK_TILDE */
94*260e9a87SYuri Pankov 	"hat", /* EQN_TOK_HAT */
95*260e9a87SYuri Pankov 	"dot", /* EQN_TOK_DOT */
96*260e9a87SYuri Pankov 	"dotdot", /* EQN_TOK_DOTDOT */
97*260e9a87SYuri Pankov 	"fwd", /* EQN_TOK_FWD * */
98*260e9a87SYuri Pankov 	"back", /* EQN_TOK_BACK */
99*260e9a87SYuri Pankov 	"down", /* EQN_TOK_DOWN */
100*260e9a87SYuri Pankov 	"up", /* EQN_TOK_UP */
101*260e9a87SYuri Pankov 	"fat", /* EQN_TOK_FAT */
102*260e9a87SYuri Pankov 	"roman", /* EQN_TOK_ROMAN */
103*260e9a87SYuri Pankov 	"italic", /* EQN_TOK_ITALIC */
104*260e9a87SYuri Pankov 	"bold", /* EQN_TOK_BOLD */
105*260e9a87SYuri Pankov 	"size", /* EQN_TOK_SIZE */
106*260e9a87SYuri Pankov 	"sub", /* EQN_TOK_SUB */
107*260e9a87SYuri Pankov 	"sup", /* EQN_TOK_SUP */
108*260e9a87SYuri Pankov 	"sqrt", /* EQN_TOK_SQRT */
109*260e9a87SYuri Pankov 	"over", /* EQN_TOK_OVER */
110*260e9a87SYuri Pankov 	"from", /* EQN_TOK_FROM */
111*260e9a87SYuri Pankov 	"to", /* EQN_TOK_TO */
112*260e9a87SYuri Pankov 	"{", /* EQN_TOK_BRACE_OPEN */
113*260e9a87SYuri Pankov 	"}", /* EQN_TOK_BRACE_CLOSE */
114*260e9a87SYuri Pankov 	"gsize", /* EQN_TOK_GSIZE */
115*260e9a87SYuri Pankov 	"gfont", /* EQN_TOK_GFONT */
116*260e9a87SYuri Pankov 	"mark", /* EQN_TOK_MARK */
117*260e9a87SYuri Pankov 	"lineup", /* EQN_TOK_LINEUP */
118*260e9a87SYuri Pankov 	"left", /* EQN_TOK_LEFT */
119*260e9a87SYuri Pankov 	"right", /* EQN_TOK_RIGHT */
120*260e9a87SYuri Pankov 	"pile", /* EQN_TOK_PILE */
121*260e9a87SYuri Pankov 	"lpile", /* EQN_TOK_LPILE */
122*260e9a87SYuri Pankov 	"rpile", /* EQN_TOK_RPILE */
123*260e9a87SYuri Pankov 	"cpile", /* EQN_TOK_CPILE */
124*260e9a87SYuri Pankov 	"matrix", /* EQN_TOK_MATRIX */
125*260e9a87SYuri Pankov 	"ccol", /* EQN_TOK_CCOL */
126*260e9a87SYuri Pankov 	"lcol", /* EQN_TOK_LCOL */
127*260e9a87SYuri Pankov 	"rcol", /* EQN_TOK_RCOL */
128*260e9a87SYuri Pankov 	"delim", /* EQN_TOK_DELIM */
129*260e9a87SYuri Pankov 	"define", /* EQN_TOK_DEFINE */
130*260e9a87SYuri Pankov 	"tdefine", /* EQN_TOK_TDEFINE */
131*260e9a87SYuri Pankov 	"ndefine", /* EQN_TOK_NDEFINE */
132*260e9a87SYuri Pankov 	"undef", /* EQN_TOK_UNDEF */
133*260e9a87SYuri Pankov 	NULL, /* EQN_TOK_EOF */
134*260e9a87SYuri Pankov 	"above", /* EQN_TOK_ABOVE */
13595c635efSGarrett D'Amore };
13695c635efSGarrett D'Amore 
13795c635efSGarrett D'Amore enum	eqn_symt {
13895c635efSGarrett D'Amore 	EQNSYM_alpha,
13995c635efSGarrett D'Amore 	EQNSYM_beta,
14095c635efSGarrett D'Amore 	EQNSYM_chi,
14195c635efSGarrett D'Amore 	EQNSYM_delta,
14295c635efSGarrett D'Amore 	EQNSYM_epsilon,
14395c635efSGarrett D'Amore 	EQNSYM_eta,
14495c635efSGarrett D'Amore 	EQNSYM_gamma,
14595c635efSGarrett D'Amore 	EQNSYM_iota,
14695c635efSGarrett D'Amore 	EQNSYM_kappa,
14795c635efSGarrett D'Amore 	EQNSYM_lambda,
14895c635efSGarrett D'Amore 	EQNSYM_mu,
14995c635efSGarrett D'Amore 	EQNSYM_nu,
15095c635efSGarrett D'Amore 	EQNSYM_omega,
15195c635efSGarrett D'Amore 	EQNSYM_omicron,
15295c635efSGarrett D'Amore 	EQNSYM_phi,
15395c635efSGarrett D'Amore 	EQNSYM_pi,
15495c635efSGarrett D'Amore 	EQNSYM_ps,
15595c635efSGarrett D'Amore 	EQNSYM_rho,
15695c635efSGarrett D'Amore 	EQNSYM_sigma,
15795c635efSGarrett D'Amore 	EQNSYM_tau,
15895c635efSGarrett D'Amore 	EQNSYM_theta,
15995c635efSGarrett D'Amore 	EQNSYM_upsilon,
16095c635efSGarrett D'Amore 	EQNSYM_xi,
16195c635efSGarrett D'Amore 	EQNSYM_zeta,
16295c635efSGarrett D'Amore 	EQNSYM_DELTA,
16395c635efSGarrett D'Amore 	EQNSYM_GAMMA,
16495c635efSGarrett D'Amore 	EQNSYM_LAMBDA,
16595c635efSGarrett D'Amore 	EQNSYM_OMEGA,
16695c635efSGarrett D'Amore 	EQNSYM_PHI,
16795c635efSGarrett D'Amore 	EQNSYM_PI,
16895c635efSGarrett D'Amore 	EQNSYM_PSI,
16995c635efSGarrett D'Amore 	EQNSYM_SIGMA,
17095c635efSGarrett D'Amore 	EQNSYM_THETA,
17195c635efSGarrett D'Amore 	EQNSYM_UPSILON,
17295c635efSGarrett D'Amore 	EQNSYM_XI,
17395c635efSGarrett D'Amore 	EQNSYM_inter,
17495c635efSGarrett D'Amore 	EQNSYM_union,
17595c635efSGarrett D'Amore 	EQNSYM_prod,
17695c635efSGarrett D'Amore 	EQNSYM_int,
17795c635efSGarrett D'Amore 	EQNSYM_sum,
17895c635efSGarrett D'Amore 	EQNSYM_grad,
17995c635efSGarrett D'Amore 	EQNSYM_del,
18095c635efSGarrett D'Amore 	EQNSYM_times,
18195c635efSGarrett D'Amore 	EQNSYM_cdot,
18295c635efSGarrett D'Amore 	EQNSYM_nothing,
18395c635efSGarrett D'Amore 	EQNSYM_approx,
18495c635efSGarrett D'Amore 	EQNSYM_prime,
18595c635efSGarrett D'Amore 	EQNSYM_half,
18695c635efSGarrett D'Amore 	EQNSYM_partial,
18795c635efSGarrett D'Amore 	EQNSYM_inf,
18895c635efSGarrett D'Amore 	EQNSYM_muchgreat,
18995c635efSGarrett D'Amore 	EQNSYM_muchless,
19095c635efSGarrett D'Amore 	EQNSYM_larrow,
19195c635efSGarrett D'Amore 	EQNSYM_rarrow,
19295c635efSGarrett D'Amore 	EQNSYM_pm,
19395c635efSGarrett D'Amore 	EQNSYM_nequal,
19495c635efSGarrett D'Amore 	EQNSYM_equiv,
19595c635efSGarrett D'Amore 	EQNSYM_lessequal,
19695c635efSGarrett D'Amore 	EQNSYM_moreequal,
197*260e9a87SYuri Pankov 	EQNSYM_minus,
19895c635efSGarrett D'Amore 	EQNSYM__MAX
19995c635efSGarrett D'Amore };
20095c635efSGarrett D'Amore 
20195c635efSGarrett D'Amore struct	eqnsym {
202*260e9a87SYuri Pankov 	const char	*str;
20395c635efSGarrett D'Amore 	const char	*sym;
20495c635efSGarrett D'Amore };
20595c635efSGarrett D'Amore 
20695c635efSGarrett D'Amore static	const struct eqnsym eqnsyms[EQNSYM__MAX] = {
207*260e9a87SYuri Pankov 	{ "alpha", "*a" }, /* EQNSYM_alpha */
208*260e9a87SYuri Pankov 	{ "beta", "*b" }, /* EQNSYM_beta */
209*260e9a87SYuri Pankov 	{ "chi", "*x" }, /* EQNSYM_chi */
210*260e9a87SYuri Pankov 	{ "delta", "*d" }, /* EQNSYM_delta */
211*260e9a87SYuri Pankov 	{ "epsilon", "*e" }, /* EQNSYM_epsilon */
212*260e9a87SYuri Pankov 	{ "eta", "*y" }, /* EQNSYM_eta */
213*260e9a87SYuri Pankov 	{ "gamma", "*g" }, /* EQNSYM_gamma */
214*260e9a87SYuri Pankov 	{ "iota", "*i" }, /* EQNSYM_iota */
215*260e9a87SYuri Pankov 	{ "kappa", "*k" }, /* EQNSYM_kappa */
216*260e9a87SYuri Pankov 	{ "lambda", "*l" }, /* EQNSYM_lambda */
217*260e9a87SYuri Pankov 	{ "mu", "*m" }, /* EQNSYM_mu */
218*260e9a87SYuri Pankov 	{ "nu", "*n" }, /* EQNSYM_nu */
219*260e9a87SYuri Pankov 	{ "omega", "*w" }, /* EQNSYM_omega */
220*260e9a87SYuri Pankov 	{ "omicron", "*o" }, /* EQNSYM_omicron */
221*260e9a87SYuri Pankov 	{ "phi", "*f" }, /* EQNSYM_phi */
222*260e9a87SYuri Pankov 	{ "pi", "*p" }, /* EQNSYM_pi */
223*260e9a87SYuri Pankov 	{ "psi", "*q" }, /* EQNSYM_psi */
224*260e9a87SYuri Pankov 	{ "rho", "*r" }, /* EQNSYM_rho */
225*260e9a87SYuri Pankov 	{ "sigma", "*s" }, /* EQNSYM_sigma */
226*260e9a87SYuri Pankov 	{ "tau", "*t" }, /* EQNSYM_tau */
227*260e9a87SYuri Pankov 	{ "theta", "*h" }, /* EQNSYM_theta */
228*260e9a87SYuri Pankov 	{ "upsilon", "*u" }, /* EQNSYM_upsilon */
229*260e9a87SYuri Pankov 	{ "xi", "*c" }, /* EQNSYM_xi */
230*260e9a87SYuri Pankov 	{ "zeta", "*z" }, /* EQNSYM_zeta */
231*260e9a87SYuri Pankov 	{ "DELTA", "*D" }, /* EQNSYM_DELTA */
232*260e9a87SYuri Pankov 	{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */
233*260e9a87SYuri Pankov 	{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
234*260e9a87SYuri Pankov 	{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */
235*260e9a87SYuri Pankov 	{ "PHI", "*F" }, /* EQNSYM_PHI */
236*260e9a87SYuri Pankov 	{ "PI", "*P" }, /* EQNSYM_PI */
237*260e9a87SYuri Pankov 	{ "PSI", "*Q" }, /* EQNSYM_PSI */
238*260e9a87SYuri Pankov 	{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */
239*260e9a87SYuri Pankov 	{ "THETA", "*H" }, /* EQNSYM_THETA */
240*260e9a87SYuri Pankov 	{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */
241*260e9a87SYuri Pankov 	{ "XI", "*C" }, /* EQNSYM_XI */
242*260e9a87SYuri Pankov 	{ "inter", "ca" }, /* EQNSYM_inter */
243*260e9a87SYuri Pankov 	{ "union", "cu" }, /* EQNSYM_union */
244*260e9a87SYuri Pankov 	{ "prod", "product" }, /* EQNSYM_prod */
245*260e9a87SYuri Pankov 	{ "int", "integral" }, /* EQNSYM_int */
246*260e9a87SYuri Pankov 	{ "sum", "sum" }, /* EQNSYM_sum */
247*260e9a87SYuri Pankov 	{ "grad", "gr" }, /* EQNSYM_grad */
248*260e9a87SYuri Pankov 	{ "del", "gr" }, /* EQNSYM_del */
249*260e9a87SYuri Pankov 	{ "times", "mu" }, /* EQNSYM_times */
250*260e9a87SYuri Pankov 	{ "cdot", "pc" }, /* EQNSYM_cdot */
251*260e9a87SYuri Pankov 	{ "nothing", "&" }, /* EQNSYM_nothing */
252*260e9a87SYuri Pankov 	{ "approx", "~~" }, /* EQNSYM_approx */
253*260e9a87SYuri Pankov 	{ "prime", "fm" }, /* EQNSYM_prime */
254*260e9a87SYuri Pankov 	{ "half", "12" }, /* EQNSYM_half */
255*260e9a87SYuri Pankov 	{ "partial", "pd" }, /* EQNSYM_partial */
256*260e9a87SYuri Pankov 	{ "inf", "if" }, /* EQNSYM_inf */
257*260e9a87SYuri Pankov 	{ ">>", ">>" }, /* EQNSYM_muchgreat */
258*260e9a87SYuri Pankov 	{ "<<", "<<" }, /* EQNSYM_muchless */
259*260e9a87SYuri Pankov 	{ "<-", "<-" }, /* EQNSYM_larrow */
260*260e9a87SYuri Pankov 	{ "->", "->" }, /* EQNSYM_rarrow */
261*260e9a87SYuri Pankov 	{ "+-", "+-" }, /* EQNSYM_pm */
262*260e9a87SYuri Pankov 	{ "!=", "!=" }, /* EQNSYM_nequal */
263*260e9a87SYuri Pankov 	{ "==", "==" }, /* EQNSYM_equiv */
264*260e9a87SYuri Pankov 	{ "<=", "<=" }, /* EQNSYM_lessequal */
265*260e9a87SYuri Pankov 	{ ">=", ">=" }, /* EQNSYM_moreequal */
266*260e9a87SYuri Pankov 	{ "-", "mi" }, /* EQNSYM_minus */
26795c635efSGarrett D'Amore };
26895c635efSGarrett D'Amore 
269*260e9a87SYuri Pankov static	struct eqn_box	*eqn_box_alloc(struct eqn_node *, struct eqn_box *);
270*260e9a87SYuri Pankov static	void		 eqn_box_free(struct eqn_box *);
271*260e9a87SYuri Pankov static	struct eqn_box	*eqn_box_makebinary(struct eqn_node *,
272*260e9a87SYuri Pankov 				enum eqn_post, struct eqn_box *);
273*260e9a87SYuri Pankov static	void		 eqn_def(struct eqn_node *);
274*260e9a87SYuri Pankov static	struct eqn_def	*eqn_def_find(struct eqn_node *, const char *, size_t);
275*260e9a87SYuri Pankov static	void		 eqn_delim(struct eqn_node *);
276*260e9a87SYuri Pankov static	const char	*eqn_next(struct eqn_node *, char, size_t *, int);
277*260e9a87SYuri Pankov static	const char	*eqn_nextrawtok(struct eqn_node *, size_t *);
278*260e9a87SYuri Pankov static	const char	*eqn_nexttok(struct eqn_node *, size_t *);
279*260e9a87SYuri Pankov static	enum rofferr	 eqn_parse(struct eqn_node *, struct eqn_box *);
280*260e9a87SYuri Pankov static	enum eqn_tok	 eqn_tok_parse(struct eqn_node *, char **);
281*260e9a87SYuri Pankov static	void		 eqn_undef(struct eqn_node *);
282*260e9a87SYuri Pankov 
283*260e9a87SYuri Pankov 
28495c635efSGarrett D'Amore enum rofferr
28595c635efSGarrett D'Amore eqn_read(struct eqn_node **epp, int ln,
28695c635efSGarrett D'Amore 		const char *p, int pos, int *offs)
28795c635efSGarrett D'Amore {
28895c635efSGarrett D'Amore 	size_t		 sz;
28995c635efSGarrett D'Amore 	struct eqn_node	*ep;
29095c635efSGarrett D'Amore 	enum rofferr	 er;
29195c635efSGarrett D'Amore 
29295c635efSGarrett D'Amore 	ep = *epp;
29395c635efSGarrett D'Amore 
29495c635efSGarrett D'Amore 	/*
29595c635efSGarrett D'Amore 	 * If we're the terminating mark, unset our equation status and
29695c635efSGarrett D'Amore 	 * validate the full equation.
29795c635efSGarrett D'Amore 	 */
29895c635efSGarrett D'Amore 
29995c635efSGarrett D'Amore 	if (0 == strncmp(p, ".EN", 3)) {
30095c635efSGarrett D'Amore 		er = eqn_end(epp);
30195c635efSGarrett D'Amore 		p += 3;
30295c635efSGarrett D'Amore 		while (' ' == *p || '\t' == *p)
30395c635efSGarrett D'Amore 			p++;
30495c635efSGarrett D'Amore 		if ('\0' == *p)
30595c635efSGarrett D'Amore 			return(er);
306*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse,
307*260e9a87SYuri Pankov 		    ln, pos, "EN %s", p);
30895c635efSGarrett D'Amore 		return(er);
30995c635efSGarrett D'Amore 	}
31095c635efSGarrett D'Amore 
31195c635efSGarrett D'Amore 	/*
31295c635efSGarrett D'Amore 	 * Build up the full string, replacing all newlines with regular
31395c635efSGarrett D'Amore 	 * whitespace.
31495c635efSGarrett D'Amore 	 */
31595c635efSGarrett D'Amore 
31695c635efSGarrett D'Amore 	sz = strlen(p + pos) + 1;
31795c635efSGarrett D'Amore 	ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
31895c635efSGarrett D'Amore 
31995c635efSGarrett D'Amore 	/* First invocation: nil terminate the string. */
32095c635efSGarrett D'Amore 
32195c635efSGarrett D'Amore 	if (0 == ep->sz)
32295c635efSGarrett D'Amore 		*ep->data = '\0';
32395c635efSGarrett D'Amore 
32495c635efSGarrett D'Amore 	ep->sz += sz;
32595c635efSGarrett D'Amore 	strlcat(ep->data, p + pos, ep->sz + 1);
32695c635efSGarrett D'Amore 	strlcat(ep->data, " ", ep->sz + 1);
32795c635efSGarrett D'Amore 	return(ROFF_IGN);
32895c635efSGarrett D'Amore }
32995c635efSGarrett D'Amore 
33095c635efSGarrett D'Amore struct eqn_node *
331*260e9a87SYuri Pankov eqn_alloc(int pos, int line, struct mparse *parse)
33295c635efSGarrett D'Amore {
33395c635efSGarrett D'Amore 	struct eqn_node	*p;
33495c635efSGarrett D'Amore 
33595c635efSGarrett D'Amore 	p = mandoc_calloc(1, sizeof(struct eqn_node));
33695c635efSGarrett D'Amore 
33795c635efSGarrett D'Amore 	p->parse = parse;
33895c635efSGarrett D'Amore 	p->eqn.ln = line;
33995c635efSGarrett D'Amore 	p->eqn.pos = pos;
34095c635efSGarrett D'Amore 	p->gsize = EQN_DEFSIZE;
34195c635efSGarrett D'Amore 
34295c635efSGarrett D'Amore 	return(p);
34395c635efSGarrett D'Amore }
34495c635efSGarrett D'Amore 
345*260e9a87SYuri Pankov /*
346*260e9a87SYuri Pankov  * Find the key "key" of the give size within our eqn-defined values.
347*260e9a87SYuri Pankov  */
348*260e9a87SYuri Pankov static struct eqn_def *
349*260e9a87SYuri Pankov eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
35095c635efSGarrett D'Amore {
35195c635efSGarrett D'Amore 	int		 i;
35295c635efSGarrett D'Amore 
353*260e9a87SYuri Pankov 	for (i = 0; i < (int)ep->defsz; i++)
354*260e9a87SYuri Pankov 		if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
355*260e9a87SYuri Pankov 		    ep->defs[i].keysz, key, sz))
356*260e9a87SYuri Pankov 			return(&ep->defs[i]);
35795c635efSGarrett D'Amore 
358*260e9a87SYuri Pankov 	return(NULL);
35995c635efSGarrett D'Amore }
36095c635efSGarrett D'Amore 
361*260e9a87SYuri Pankov /*
362*260e9a87SYuri Pankov  * Get the next token from the input stream using the given quote
363*260e9a87SYuri Pankov  * character.
364*260e9a87SYuri Pankov  * Optionally make any replacements.
365*260e9a87SYuri Pankov  */
36695c635efSGarrett D'Amore static const char *
36795c635efSGarrett D'Amore eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
36895c635efSGarrett D'Amore {
36995c635efSGarrett D'Amore 	char		*start, *next;
37095c635efSGarrett D'Amore 	int		 q, diff, lim;
37195c635efSGarrett D'Amore 	size_t		 ssz, dummy;
37295c635efSGarrett D'Amore 	struct eqn_def	*def;
37395c635efSGarrett D'Amore 
37495c635efSGarrett D'Amore 	if (NULL == sz)
37595c635efSGarrett D'Amore 		sz = &dummy;
37695c635efSGarrett D'Amore 
37795c635efSGarrett D'Amore 	lim = 0;
37895c635efSGarrett D'Amore 	ep->rew = ep->cur;
37995c635efSGarrett D'Amore again:
38095c635efSGarrett D'Amore 	/* Prevent self-definitions. */
38195c635efSGarrett D'Amore 
38295c635efSGarrett D'Amore 	if (lim >= EQN_NEST_MAX) {
383*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
384*260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, NULL);
38595c635efSGarrett D'Amore 		return(NULL);
38695c635efSGarrett D'Amore 	}
38795c635efSGarrett D'Amore 
38895c635efSGarrett D'Amore 	ep->cur = ep->rew;
38995c635efSGarrett D'Amore 	start = &ep->data[(int)ep->cur];
39095c635efSGarrett D'Amore 	q = 0;
39195c635efSGarrett D'Amore 
39295c635efSGarrett D'Amore 	if ('\0' == *start)
39395c635efSGarrett D'Amore 		return(NULL);
39495c635efSGarrett D'Amore 
39595c635efSGarrett D'Amore 	if (quote == *start) {
39695c635efSGarrett D'Amore 		ep->cur++;
39795c635efSGarrett D'Amore 		q = 1;
39895c635efSGarrett D'Amore 	}
39995c635efSGarrett D'Amore 
40095c635efSGarrett D'Amore 	start = &ep->data[(int)ep->cur];
40195c635efSGarrett D'Amore 
40295c635efSGarrett D'Amore 	if ( ! q) {
40395c635efSGarrett D'Amore 		if ('{' == *start || '}' == *start)
40495c635efSGarrett D'Amore 			ssz = 1;
40595c635efSGarrett D'Amore 		else
40695c635efSGarrett D'Amore 			ssz = strcspn(start + 1, " ^~\"{}\t") + 1;
40795c635efSGarrett D'Amore 		next = start + (int)ssz;
40895c635efSGarrett D'Amore 		if ('\0' == *next)
40995c635efSGarrett D'Amore 			next = NULL;
41095c635efSGarrett D'Amore 	} else
41195c635efSGarrett D'Amore 		next = strchr(start, quote);
41295c635efSGarrett D'Amore 
41395c635efSGarrett D'Amore 	if (NULL != next) {
41495c635efSGarrett D'Amore 		*sz = (size_t)(next - start);
41595c635efSGarrett D'Amore 		ep->cur += *sz;
41695c635efSGarrett D'Amore 		if (q)
41795c635efSGarrett D'Amore 			ep->cur++;
41895c635efSGarrett D'Amore 		while (' ' == ep->data[(int)ep->cur] ||
41995c635efSGarrett D'Amore 		    '\t' == ep->data[(int)ep->cur] ||
42095c635efSGarrett D'Amore 		    '^' == ep->data[(int)ep->cur] ||
42195c635efSGarrett D'Amore 		    '~' == ep->data[(int)ep->cur])
42295c635efSGarrett D'Amore 			ep->cur++;
42395c635efSGarrett D'Amore 	} else {
42495c635efSGarrett D'Amore 		if (q)
425*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
426*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, NULL);
42795c635efSGarrett D'Amore 		next = strchr(start, '\0');
42895c635efSGarrett D'Amore 		*sz = (size_t)(next - start);
42995c635efSGarrett D'Amore 		ep->cur += *sz;
43095c635efSGarrett D'Amore 	}
43195c635efSGarrett D'Amore 
43295c635efSGarrett D'Amore 	/* Quotes aren't expanded for values. */
43395c635efSGarrett D'Amore 
43495c635efSGarrett D'Amore 	if (q || ! repl)
43595c635efSGarrett D'Amore 		return(start);
43695c635efSGarrett D'Amore 
43795c635efSGarrett D'Amore 	if (NULL != (def = eqn_def_find(ep, start, *sz))) {
43895c635efSGarrett D'Amore 		diff = def->valsz - *sz;
43995c635efSGarrett D'Amore 
44095c635efSGarrett D'Amore 		if (def->valsz > *sz) {
44195c635efSGarrett D'Amore 			ep->sz += diff;
44295c635efSGarrett D'Amore 			ep->data = mandoc_realloc(ep->data, ep->sz + 1);
44395c635efSGarrett D'Amore 			ep->data[ep->sz] = '\0';
44495c635efSGarrett D'Amore 			start = &ep->data[(int)ep->rew];
44595c635efSGarrett D'Amore 		}
44695c635efSGarrett D'Amore 
44795c635efSGarrett D'Amore 		diff = def->valsz - *sz;
44895c635efSGarrett D'Amore 		memmove(start + *sz + diff, start + *sz,
44995c635efSGarrett D'Amore 		    (strlen(start) - *sz) + 1);
45095c635efSGarrett D'Amore 		memcpy(start, def->val, def->valsz);
45195c635efSGarrett D'Amore 		goto again;
45295c635efSGarrett D'Amore 	}
45395c635efSGarrett D'Amore 
45495c635efSGarrett D'Amore 	return(start);
45595c635efSGarrett D'Amore }
45695c635efSGarrett D'Amore 
457*260e9a87SYuri Pankov /*
458*260e9a87SYuri Pankov  * Get the next delimited token using the default current quote
459*260e9a87SYuri Pankov  * character.
460*260e9a87SYuri Pankov  */
461*260e9a87SYuri Pankov static const char *
462*260e9a87SYuri Pankov eqn_nexttok(struct eqn_node *ep, size_t *sz)
46395c635efSGarrett D'Amore {
46495c635efSGarrett D'Amore 
465*260e9a87SYuri Pankov 	return(eqn_next(ep, '"', sz, 1));
46695c635efSGarrett D'Amore }
46795c635efSGarrett D'Amore 
468*260e9a87SYuri Pankov /*
469*260e9a87SYuri Pankov  * Get next token without replacement.
470*260e9a87SYuri Pankov  */
471*260e9a87SYuri Pankov static const char *
472*260e9a87SYuri Pankov eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
47395c635efSGarrett D'Amore {
47495c635efSGarrett D'Amore 
475*260e9a87SYuri Pankov 	return(eqn_next(ep, '"', sz, 0));
47695c635efSGarrett D'Amore }
47795c635efSGarrett D'Amore 
478*260e9a87SYuri Pankov /*
479*260e9a87SYuri Pankov  * Parse a token from the stream of text.
480*260e9a87SYuri Pankov  * A token consists of one of the recognised eqn(7) strings.
481*260e9a87SYuri Pankov  * Strings are separated by delimiting marks.
482*260e9a87SYuri Pankov  * This returns EQN_TOK_EOF when there are no more tokens.
483*260e9a87SYuri Pankov  * If the token is an unrecognised string literal, then it returns
484*260e9a87SYuri Pankov  * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated
485*260e9a87SYuri Pankov  * string.
486*260e9a87SYuri Pankov  * This must be later freed with free(3).
487*260e9a87SYuri Pankov  */
488*260e9a87SYuri Pankov static enum eqn_tok
489*260e9a87SYuri Pankov eqn_tok_parse(struct eqn_node *ep, char **p)
490*260e9a87SYuri Pankov {
491*260e9a87SYuri Pankov 	const char	*start;
492*260e9a87SYuri Pankov 	size_t		 i, sz;
493*260e9a87SYuri Pankov 	int		 quoted;
494*260e9a87SYuri Pankov 
495*260e9a87SYuri Pankov 	if (NULL != p)
496*260e9a87SYuri Pankov 		*p = NULL;
497*260e9a87SYuri Pankov 
498*260e9a87SYuri Pankov 	quoted = ep->data[ep->cur] == '"';
499*260e9a87SYuri Pankov 
500*260e9a87SYuri Pankov 	if (NULL == (start = eqn_nexttok(ep, &sz)))
501*260e9a87SYuri Pankov 		return(EQN_TOK_EOF);
502*260e9a87SYuri Pankov 
503*260e9a87SYuri Pankov 	if (quoted) {
504*260e9a87SYuri Pankov 		if (p != NULL)
505*260e9a87SYuri Pankov 			*p = mandoc_strndup(start, sz);
506*260e9a87SYuri Pankov 		return(EQN_TOK__MAX);
507*260e9a87SYuri Pankov 	}
508*260e9a87SYuri Pankov 
509*260e9a87SYuri Pankov 	for (i = 0; i < EQN_TOK__MAX; i++) {
510*260e9a87SYuri Pankov 		if (NULL == eqn_toks[i])
511*260e9a87SYuri Pankov 			continue;
512*260e9a87SYuri Pankov 		if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i])))
513*260e9a87SYuri Pankov 			break;
514*260e9a87SYuri Pankov 	}
515*260e9a87SYuri Pankov 
516*260e9a87SYuri Pankov 	if (i == EQN_TOK__MAX && NULL != p)
517*260e9a87SYuri Pankov 		*p = mandoc_strndup(start, sz);
518*260e9a87SYuri Pankov 
519*260e9a87SYuri Pankov 	return(i);
520*260e9a87SYuri Pankov }
521*260e9a87SYuri Pankov 
522*260e9a87SYuri Pankov static void
523*260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp)
52495c635efSGarrett D'Amore {
52595c635efSGarrett D'Amore 
526*260e9a87SYuri Pankov 	if (bp->first)
527*260e9a87SYuri Pankov 		eqn_box_free(bp->first);
528*260e9a87SYuri Pankov 	if (bp->next)
529*260e9a87SYuri Pankov 		eqn_box_free(bp->next);
53095c635efSGarrett D'Amore 
531*260e9a87SYuri Pankov 	free(bp->text);
532*260e9a87SYuri Pankov 	free(bp->left);
533*260e9a87SYuri Pankov 	free(bp->right);
534*260e9a87SYuri Pankov 	free(bp->top);
535*260e9a87SYuri Pankov 	free(bp->bottom);
536*260e9a87SYuri Pankov 	free(bp);
53795c635efSGarrett D'Amore }
53895c635efSGarrett D'Amore 
539*260e9a87SYuri Pankov /*
540*260e9a87SYuri Pankov  * Allocate a box as the last child of the parent node.
541*260e9a87SYuri Pankov  */
542*260e9a87SYuri Pankov static struct eqn_box *
543*260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
544*260e9a87SYuri Pankov {
545*260e9a87SYuri Pankov 	struct eqn_box	*bp;
546*260e9a87SYuri Pankov 
547*260e9a87SYuri Pankov 	bp = mandoc_calloc(1, sizeof(struct eqn_box));
548*260e9a87SYuri Pankov 	bp->parent = parent;
549*260e9a87SYuri Pankov 	bp->parent->args++;
550*260e9a87SYuri Pankov 	bp->expectargs = UINT_MAX;
551*260e9a87SYuri Pankov 	bp->size = ep->gsize;
552*260e9a87SYuri Pankov 
553*260e9a87SYuri Pankov 	if (NULL != parent->first) {
554*260e9a87SYuri Pankov 		parent->last->next = bp;
555*260e9a87SYuri Pankov 		bp->prev = parent->last;
556*260e9a87SYuri Pankov 	} else
557*260e9a87SYuri Pankov 		parent->first = bp;
558*260e9a87SYuri Pankov 
559*260e9a87SYuri Pankov 	parent->last = bp;
560*260e9a87SYuri Pankov 	return(bp);
561*260e9a87SYuri Pankov }
562*260e9a87SYuri Pankov 
563*260e9a87SYuri Pankov /*
564*260e9a87SYuri Pankov  * Reparent the current last node (of the current parent) under a new
565*260e9a87SYuri Pankov  * EQN_SUBEXPR as the first element.
566*260e9a87SYuri Pankov  * Then return the new parent.
567*260e9a87SYuri Pankov  * The new EQN_SUBEXPR will have a two-child limit.
568*260e9a87SYuri Pankov  */
569*260e9a87SYuri Pankov static struct eqn_box *
570*260e9a87SYuri Pankov eqn_box_makebinary(struct eqn_node *ep,
571*260e9a87SYuri Pankov 	enum eqn_post pos, struct eqn_box *parent)
572*260e9a87SYuri Pankov {
573*260e9a87SYuri Pankov 	struct eqn_box	*b, *newb;
574*260e9a87SYuri Pankov 
575*260e9a87SYuri Pankov 	assert(NULL != parent->last);
576*260e9a87SYuri Pankov 	b = parent->last;
577*260e9a87SYuri Pankov 	if (parent->last == parent->first)
578*260e9a87SYuri Pankov 		parent->first = NULL;
579*260e9a87SYuri Pankov 	parent->args--;
580*260e9a87SYuri Pankov 	parent->last = b->prev;
581*260e9a87SYuri Pankov 	b->prev = NULL;
582*260e9a87SYuri Pankov 	newb = eqn_box_alloc(ep, parent);
583*260e9a87SYuri Pankov 	newb->pos = pos;
584*260e9a87SYuri Pankov 	newb->type = EQN_SUBEXPR;
585*260e9a87SYuri Pankov 	newb->expectargs = 2;
586*260e9a87SYuri Pankov 	newb->args = 1;
587*260e9a87SYuri Pankov 	newb->first = newb->last = b;
588*260e9a87SYuri Pankov 	newb->first->next = NULL;
589*260e9a87SYuri Pankov 	b->parent = newb;
590*260e9a87SYuri Pankov 	return(newb);
591*260e9a87SYuri Pankov }
592*260e9a87SYuri Pankov 
593*260e9a87SYuri Pankov /*
594*260e9a87SYuri Pankov  * Parse the "delim" control statement.
595*260e9a87SYuri Pankov  */
596*260e9a87SYuri Pankov static void
597*260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep)
598*260e9a87SYuri Pankov {
599*260e9a87SYuri Pankov 	const char	*start;
600*260e9a87SYuri Pankov 	size_t		 sz;
601*260e9a87SYuri Pankov 
602*260e9a87SYuri Pankov 	if ((start = eqn_nextrawtok(ep, &sz)) == NULL)
603*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
604*260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "delim");
605*260e9a87SYuri Pankov 	else if (strncmp(start, "off", 3) == 0)
606*260e9a87SYuri Pankov 		ep->delim = 0;
607*260e9a87SYuri Pankov 	else if (strncmp(start, "on", 2) == 0) {
608*260e9a87SYuri Pankov 		if (ep->odelim && ep->cdelim)
609*260e9a87SYuri Pankov 			ep->delim = 1;
610*260e9a87SYuri Pankov 	} else if (start[1] != '\0') {
611*260e9a87SYuri Pankov 		ep->odelim = start[0];
612*260e9a87SYuri Pankov 		ep->cdelim = start[1];
613*260e9a87SYuri Pankov 		ep->delim = 1;
614*260e9a87SYuri Pankov 	}
615*260e9a87SYuri Pankov }
616*260e9a87SYuri Pankov 
617*260e9a87SYuri Pankov /*
618*260e9a87SYuri Pankov  * Undefine a previously-defined string.
619*260e9a87SYuri Pankov  */
620*260e9a87SYuri Pankov static void
621*260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep)
622*260e9a87SYuri Pankov {
623*260e9a87SYuri Pankov 	const char	*start;
624*260e9a87SYuri Pankov 	struct eqn_def	*def;
625*260e9a87SYuri Pankov 	size_t		 sz;
626*260e9a87SYuri Pankov 
627*260e9a87SYuri Pankov 	if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
628*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
629*260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "undef");
630*260e9a87SYuri Pankov 		return;
631*260e9a87SYuri Pankov 	}
632*260e9a87SYuri Pankov 	if ((def = eqn_def_find(ep, start, sz)) == NULL)
633*260e9a87SYuri Pankov 		return;
634*260e9a87SYuri Pankov 	free(def->key);
635*260e9a87SYuri Pankov 	free(def->val);
636*260e9a87SYuri Pankov 	def->key = def->val = NULL;
637*260e9a87SYuri Pankov 	def->keysz = def->valsz = 0;
638*260e9a87SYuri Pankov }
639*260e9a87SYuri Pankov 
640*260e9a87SYuri Pankov static void
641*260e9a87SYuri Pankov eqn_def(struct eqn_node *ep)
64295c635efSGarrett D'Amore {
64395c635efSGarrett D'Amore 	const char	*start;
64495c635efSGarrett D'Amore 	size_t		 sz;
64595c635efSGarrett D'Amore 	struct eqn_def	*def;
64695c635efSGarrett D'Amore 	int		 i;
64795c635efSGarrett D'Amore 
648*260e9a87SYuri Pankov 	if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
649*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
650*260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "define");
651*260e9a87SYuri Pankov 		return;
65295c635efSGarrett D'Amore 	}
65395c635efSGarrett D'Amore 
65495c635efSGarrett D'Amore 	/*
65595c635efSGarrett D'Amore 	 * Search for a key that already exists.
65695c635efSGarrett D'Amore 	 * Create a new key if none is found.
65795c635efSGarrett D'Amore 	 */
65895c635efSGarrett D'Amore 	if (NULL == (def = eqn_def_find(ep, start, sz))) {
65995c635efSGarrett D'Amore 		/* Find holes in string array. */
66095c635efSGarrett D'Amore 		for (i = 0; i < (int)ep->defsz; i++)
66195c635efSGarrett D'Amore 			if (0 == ep->defs[i].keysz)
66295c635efSGarrett D'Amore 				break;
66395c635efSGarrett D'Amore 
66495c635efSGarrett D'Amore 		if (i == (int)ep->defsz) {
66595c635efSGarrett D'Amore 			ep->defsz++;
666*260e9a87SYuri Pankov 			ep->defs = mandoc_reallocarray(ep->defs,
667*260e9a87SYuri Pankov 			    ep->defsz, sizeof(struct eqn_def));
66895c635efSGarrett D'Amore 			ep->defs[i].key = ep->defs[i].val = NULL;
66995c635efSGarrett D'Amore 		}
67095c635efSGarrett D'Amore 
671*260e9a87SYuri Pankov 		def = ep->defs + i;
672*260e9a87SYuri Pankov 		free(def->key);
673*260e9a87SYuri Pankov 		def->key = mandoc_strndup(start, sz);
674*260e9a87SYuri Pankov 		def->keysz = sz;
67595c635efSGarrett D'Amore 	}
67695c635efSGarrett D'Amore 
67795c635efSGarrett D'Amore 	start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
678*260e9a87SYuri Pankov 	if (start == NULL) {
679*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
680*260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "define %s", def->key);
681*260e9a87SYuri Pankov 		free(def->key);
682*260e9a87SYuri Pankov 		free(def->val);
683*260e9a87SYuri Pankov 		def->key = def->val = NULL;
684*260e9a87SYuri Pankov 		def->keysz = def->valsz = 0;
685*260e9a87SYuri Pankov 		return;
68695c635efSGarrett D'Amore 	}
687*260e9a87SYuri Pankov 	free(def->val);
688*260e9a87SYuri Pankov 	def->val = mandoc_strndup(start, sz);
68995c635efSGarrett D'Amore 	def->valsz = sz;
69095c635efSGarrett D'Amore }
69195c635efSGarrett D'Amore 
692*260e9a87SYuri Pankov /*
693*260e9a87SYuri Pankov  * Recursively parse an eqn(7) expression.
694*260e9a87SYuri Pankov  */
695*260e9a87SYuri Pankov static enum rofferr
696*260e9a87SYuri Pankov eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
69795c635efSGarrett D'Amore {
698*260e9a87SYuri Pankov 	char		 sym[64];
699*260e9a87SYuri Pankov 	struct eqn_box	*cur;
70095c635efSGarrett D'Amore 	const char	*start;
701*260e9a87SYuri Pankov 	char		*p;
702*260e9a87SYuri Pankov 	size_t		 i, sz;
703*260e9a87SYuri Pankov 	enum eqn_tok	 tok, subtok;
704*260e9a87SYuri Pankov 	enum eqn_post	 pos;
705*260e9a87SYuri Pankov 	int		 size;
70695c635efSGarrett D'Amore 
707*260e9a87SYuri Pankov 	assert(parent != NULL);
708*260e9a87SYuri Pankov 
709*260e9a87SYuri Pankov 	/*
710*260e9a87SYuri Pankov 	 * Empty equation.
711*260e9a87SYuri Pankov 	 * Do not add it to the high-level syntax tree.
712*260e9a87SYuri Pankov 	 */
713*260e9a87SYuri Pankov 
714*260e9a87SYuri Pankov 	if (ep->data == NULL)
715*260e9a87SYuri Pankov 		return(ROFF_IGN);
716*260e9a87SYuri Pankov 
717*260e9a87SYuri Pankov next_tok:
718*260e9a87SYuri Pankov 	tok = eqn_tok_parse(ep, &p);
719*260e9a87SYuri Pankov 
720*260e9a87SYuri Pankov this_tok:
721*260e9a87SYuri Pankov 	switch (tok) {
722*260e9a87SYuri Pankov 	case (EQN_TOK_UNDEF):
723*260e9a87SYuri Pankov 		eqn_undef(ep);
724*260e9a87SYuri Pankov 		break;
725*260e9a87SYuri Pankov 	case (EQN_TOK_NDEFINE):
726*260e9a87SYuri Pankov 	case (EQN_TOK_DEFINE):
727*260e9a87SYuri Pankov 		eqn_def(ep);
728*260e9a87SYuri Pankov 		break;
729*260e9a87SYuri Pankov 	case (EQN_TOK_TDEFINE):
730*260e9a87SYuri Pankov 		if (eqn_nextrawtok(ep, NULL) == NULL ||
731*260e9a87SYuri Pankov 		    eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL)
732*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
733*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, "tdefine");
734*260e9a87SYuri Pankov 		break;
735*260e9a87SYuri Pankov 	case (EQN_TOK_DELIM):
736*260e9a87SYuri Pankov 		eqn_delim(ep);
737*260e9a87SYuri Pankov 		break;
738*260e9a87SYuri Pankov 	case (EQN_TOK_GFONT):
739*260e9a87SYuri Pankov 		if (eqn_nextrawtok(ep, NULL) == NULL)
740*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
741*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
742*260e9a87SYuri Pankov 		break;
743*260e9a87SYuri Pankov 	case (EQN_TOK_MARK):
744*260e9a87SYuri Pankov 	case (EQN_TOK_LINEUP):
745*260e9a87SYuri Pankov 		/* Ignore these. */
746*260e9a87SYuri Pankov 		break;
747*260e9a87SYuri Pankov 	case (EQN_TOK_DYAD):
748*260e9a87SYuri Pankov 	case (EQN_TOK_VEC):
749*260e9a87SYuri Pankov 	case (EQN_TOK_UNDER):
750*260e9a87SYuri Pankov 	case (EQN_TOK_BAR):
751*260e9a87SYuri Pankov 	case (EQN_TOK_TILDE):
752*260e9a87SYuri Pankov 	case (EQN_TOK_HAT):
753*260e9a87SYuri Pankov 	case (EQN_TOK_DOT):
754*260e9a87SYuri Pankov 	case (EQN_TOK_DOTDOT):
755*260e9a87SYuri Pankov 		if (parent->last == NULL) {
756*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
757*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
758*260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
759*260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
760*260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
76195c635efSGarrett D'Amore 		}
762*260e9a87SYuri Pankov 		parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent);
763*260e9a87SYuri Pankov 		parent->type = EQN_LISTONE;
764*260e9a87SYuri Pankov 		parent->expectargs = 1;
765*260e9a87SYuri Pankov 		switch (tok) {
766*260e9a87SYuri Pankov 		case (EQN_TOK_DOTDOT):
767*260e9a87SYuri Pankov 			strlcpy(sym, "\\[ad]", sizeof(sym));
768*260e9a87SYuri Pankov 			break;
769*260e9a87SYuri Pankov 		case (EQN_TOK_VEC):
770*260e9a87SYuri Pankov 			strlcpy(sym, "\\[->]", sizeof(sym));
771*260e9a87SYuri Pankov 			break;
772*260e9a87SYuri Pankov 		case (EQN_TOK_DYAD):
773*260e9a87SYuri Pankov 			strlcpy(sym, "\\[<>]", sizeof(sym));
774*260e9a87SYuri Pankov 			break;
775*260e9a87SYuri Pankov 		case (EQN_TOK_TILDE):
776*260e9a87SYuri Pankov 			strlcpy(sym, "\\[a~]", sizeof(sym));
777*260e9a87SYuri Pankov 			break;
778*260e9a87SYuri Pankov 		case (EQN_TOK_UNDER):
779*260e9a87SYuri Pankov 			strlcpy(sym, "\\[ul]", sizeof(sym));
780*260e9a87SYuri Pankov 			break;
781*260e9a87SYuri Pankov 		case (EQN_TOK_BAR):
782*260e9a87SYuri Pankov 			strlcpy(sym, "\\[rl]", sizeof(sym));
783*260e9a87SYuri Pankov 			break;
784*260e9a87SYuri Pankov 		case (EQN_TOK_DOT):
785*260e9a87SYuri Pankov 			strlcpy(sym, "\\[a.]", sizeof(sym));
786*260e9a87SYuri Pankov 			break;
787*260e9a87SYuri Pankov 		case (EQN_TOK_HAT):
788*260e9a87SYuri Pankov 			strlcpy(sym, "\\[ha]", sizeof(sym));
789*260e9a87SYuri Pankov 			break;
790*260e9a87SYuri Pankov 		default:
791*260e9a87SYuri Pankov 			abort();
79295c635efSGarrett D'Amore 		}
79395c635efSGarrett D'Amore 
794*260e9a87SYuri Pankov 		switch (tok) {
795*260e9a87SYuri Pankov 		case (EQN_TOK_DOTDOT):
796*260e9a87SYuri Pankov 		case (EQN_TOK_VEC):
797*260e9a87SYuri Pankov 		case (EQN_TOK_DYAD):
798*260e9a87SYuri Pankov 		case (EQN_TOK_TILDE):
799*260e9a87SYuri Pankov 		case (EQN_TOK_BAR):
800*260e9a87SYuri Pankov 		case (EQN_TOK_DOT):
801*260e9a87SYuri Pankov 		case (EQN_TOK_HAT):
802*260e9a87SYuri Pankov 			parent->top = mandoc_strdup(sym);
803*260e9a87SYuri Pankov 			break;
804*260e9a87SYuri Pankov 		case (EQN_TOK_UNDER):
805*260e9a87SYuri Pankov 			parent->bottom = mandoc_strdup(sym);
806*260e9a87SYuri Pankov 			break;
807*260e9a87SYuri Pankov 		default:
808*260e9a87SYuri Pankov 			abort();
809*260e9a87SYuri Pankov 		}
810*260e9a87SYuri Pankov 		parent = parent->parent;
811*260e9a87SYuri Pankov 		break;
812*260e9a87SYuri Pankov 	case (EQN_TOK_FWD):
813*260e9a87SYuri Pankov 	case (EQN_TOK_BACK):
814*260e9a87SYuri Pankov 	case (EQN_TOK_DOWN):
815*260e9a87SYuri Pankov 	case (EQN_TOK_UP):
816*260e9a87SYuri Pankov 		subtok = eqn_tok_parse(ep, NULL);
817*260e9a87SYuri Pankov 		if (subtok != EQN_TOK__MAX) {
818*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
819*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
820*260e9a87SYuri Pankov 			tok = subtok;
821*260e9a87SYuri Pankov 			goto this_tok;
822*260e9a87SYuri Pankov 		}
823*260e9a87SYuri Pankov 		break;
824*260e9a87SYuri Pankov 	case (EQN_TOK_FAT):
825*260e9a87SYuri Pankov 	case (EQN_TOK_ROMAN):
826*260e9a87SYuri Pankov 	case (EQN_TOK_ITALIC):
827*260e9a87SYuri Pankov 	case (EQN_TOK_BOLD):
828*260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
829*260e9a87SYuri Pankov 			parent = parent->parent;
830*260e9a87SYuri Pankov 		/*
831*260e9a87SYuri Pankov 		 * These values apply to the next word or sequence of
832*260e9a87SYuri Pankov 		 * words; thus, we mark that we'll have a child with
833*260e9a87SYuri Pankov 		 * exactly one of those.
834*260e9a87SYuri Pankov 		 */
835*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
836*260e9a87SYuri Pankov 		parent->type = EQN_LISTONE;
837*260e9a87SYuri Pankov 		parent->expectargs = 1;
838*260e9a87SYuri Pankov 		switch (tok) {
839*260e9a87SYuri Pankov 		case (EQN_TOK_FAT):
840*260e9a87SYuri Pankov 			parent->font = EQNFONT_FAT;
841*260e9a87SYuri Pankov 			break;
842*260e9a87SYuri Pankov 		case (EQN_TOK_ROMAN):
843*260e9a87SYuri Pankov 			parent->font = EQNFONT_ROMAN;
844*260e9a87SYuri Pankov 			break;
845*260e9a87SYuri Pankov 		case (EQN_TOK_ITALIC):
846*260e9a87SYuri Pankov 			parent->font = EQNFONT_ITALIC;
847*260e9a87SYuri Pankov 			break;
848*260e9a87SYuri Pankov 		case (EQN_TOK_BOLD):
849*260e9a87SYuri Pankov 			parent->font = EQNFONT_BOLD;
850*260e9a87SYuri Pankov 			break;
851*260e9a87SYuri Pankov 		default:
852*260e9a87SYuri Pankov 			abort();
853*260e9a87SYuri Pankov 		}
854*260e9a87SYuri Pankov 		break;
855*260e9a87SYuri Pankov 	case (EQN_TOK_SIZE):
856*260e9a87SYuri Pankov 	case (EQN_TOK_GSIZE):
857*260e9a87SYuri Pankov 		/* Accept two values: integral size and a single. */
858*260e9a87SYuri Pankov 		if (NULL == (start = eqn_nexttok(ep, &sz))) {
859*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
860*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
861*260e9a87SYuri Pankov 			break;
862*260e9a87SYuri Pankov 		}
863*260e9a87SYuri Pankov 		size = mandoc_strntoi(start, sz, 10);
864*260e9a87SYuri Pankov 		if (-1 == size) {
865*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
866*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
867*260e9a87SYuri Pankov 			break;
868*260e9a87SYuri Pankov 		}
869*260e9a87SYuri Pankov 		if (EQN_TOK_GSIZE == tok) {
870*260e9a87SYuri Pankov 			ep->gsize = size;
871*260e9a87SYuri Pankov 			break;
872*260e9a87SYuri Pankov 		}
873*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
874*260e9a87SYuri Pankov 		parent->type = EQN_LISTONE;
875*260e9a87SYuri Pankov 		parent->expectargs = 1;
876*260e9a87SYuri Pankov 		parent->size = size;
877*260e9a87SYuri Pankov 		break;
878*260e9a87SYuri Pankov 	case (EQN_TOK_FROM):
879*260e9a87SYuri Pankov 	case (EQN_TOK_TO):
880*260e9a87SYuri Pankov 	case (EQN_TOK_SUB):
881*260e9a87SYuri Pankov 	case (EQN_TOK_SUP):
882*260e9a87SYuri Pankov 		/*
883*260e9a87SYuri Pankov 		 * We have a left-right-associative expression.
884*260e9a87SYuri Pankov 		 * Repivot under a positional node, open a child scope
885*260e9a87SYuri Pankov 		 * and keep on reading.
886*260e9a87SYuri Pankov 		 */
887*260e9a87SYuri Pankov 		if (parent->last == NULL) {
888*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
889*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
890*260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
891*260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
892*260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
893*260e9a87SYuri Pankov 		}
894*260e9a87SYuri Pankov 		/* Handle the "subsup" and "fromto" positions. */
895*260e9a87SYuri Pankov 		if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {
896*260e9a87SYuri Pankov 			parent->expectargs = 3;
897*260e9a87SYuri Pankov 			parent->pos = EQNPOS_SUBSUP;
898*260e9a87SYuri Pankov 			break;
899*260e9a87SYuri Pankov 		}
900*260e9a87SYuri Pankov 		if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) {
901*260e9a87SYuri Pankov 			parent->expectargs = 3;
902*260e9a87SYuri Pankov 			parent->pos = EQNPOS_FROMTO;
903*260e9a87SYuri Pankov 			break;
904*260e9a87SYuri Pankov 		}
905*260e9a87SYuri Pankov 		switch (tok) {
906*260e9a87SYuri Pankov 		case (EQN_TOK_FROM):
907*260e9a87SYuri Pankov 			pos = EQNPOS_FROM;
908*260e9a87SYuri Pankov 			break;
909*260e9a87SYuri Pankov 		case (EQN_TOK_TO):
910*260e9a87SYuri Pankov 			pos = EQNPOS_TO;
911*260e9a87SYuri Pankov 			break;
912*260e9a87SYuri Pankov 		case (EQN_TOK_SUP):
913*260e9a87SYuri Pankov 			pos = EQNPOS_SUP;
914*260e9a87SYuri Pankov 			break;
915*260e9a87SYuri Pankov 		case (EQN_TOK_SUB):
916*260e9a87SYuri Pankov 			pos = EQNPOS_SUB;
917*260e9a87SYuri Pankov 			break;
918*260e9a87SYuri Pankov 		default:
919*260e9a87SYuri Pankov 			abort();
920*260e9a87SYuri Pankov 		}
921*260e9a87SYuri Pankov 		parent = eqn_box_makebinary(ep, pos, parent);
922*260e9a87SYuri Pankov 		break;
923*260e9a87SYuri Pankov 	case (EQN_TOK_SQRT):
924*260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
925*260e9a87SYuri Pankov 			parent = parent->parent;
926*260e9a87SYuri Pankov 		/*
927*260e9a87SYuri Pankov 		 * Accept a left-right-associative set of arguments just
928*260e9a87SYuri Pankov 		 * like sub and sup and friends but without rebalancing
929*260e9a87SYuri Pankov 		 * under a pivot.
930*260e9a87SYuri Pankov 		 */
931*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
932*260e9a87SYuri Pankov 		parent->type = EQN_SUBEXPR;
933*260e9a87SYuri Pankov 		parent->pos = EQNPOS_SQRT;
934*260e9a87SYuri Pankov 		parent->expectargs = 1;
935*260e9a87SYuri Pankov 		break;
936*260e9a87SYuri Pankov 	case (EQN_TOK_OVER):
937*260e9a87SYuri Pankov 		/*
938*260e9a87SYuri Pankov 		 * We have a right-left-associative fraction.
939*260e9a87SYuri Pankov 		 * Close out anything that's currently open, then
940*260e9a87SYuri Pankov 		 * rebalance and continue reading.
941*260e9a87SYuri Pankov 		 */
942*260e9a87SYuri Pankov 		if (parent->last == NULL) {
943*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
944*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
945*260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
946*260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
947*260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
948*260e9a87SYuri Pankov 		}
949*260e9a87SYuri Pankov 		while (EQN_SUBEXPR == parent->type)
950*260e9a87SYuri Pankov 			parent = parent->parent;
951*260e9a87SYuri Pankov 		parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);
952*260e9a87SYuri Pankov 		break;
953*260e9a87SYuri Pankov 	case (EQN_TOK_RIGHT):
954*260e9a87SYuri Pankov 	case (EQN_TOK_BRACE_CLOSE):
955*260e9a87SYuri Pankov 		/*
956*260e9a87SYuri Pankov 		 * Close out the existing brace.
957*260e9a87SYuri Pankov 		 * FIXME: this is a shitty sentinel: we should really
958*260e9a87SYuri Pankov 		 * have a native EQN_BRACE type or whatnot.
959*260e9a87SYuri Pankov 		 */
960*260e9a87SYuri Pankov 		for (cur = parent; cur != NULL; cur = cur->parent)
961*260e9a87SYuri Pankov 			if (cur->type == EQN_LIST &&
962*260e9a87SYuri Pankov 			    (tok == EQN_TOK_BRACE_CLOSE ||
963*260e9a87SYuri Pankov 			     cur->left != NULL))
964*260e9a87SYuri Pankov 				break;
965*260e9a87SYuri Pankov 		if (cur == NULL) {
966*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
967*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
968*260e9a87SYuri Pankov 			break;
969*260e9a87SYuri Pankov 		}
970*260e9a87SYuri Pankov 		parent = cur;
971*260e9a87SYuri Pankov 		if (EQN_TOK_RIGHT == tok) {
972*260e9a87SYuri Pankov 			if (NULL == (start = eqn_nexttok(ep, &sz))) {
973*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_REQ_EMPTY,
974*260e9a87SYuri Pankov 				    ep->parse, ep->eqn.ln,
975*260e9a87SYuri Pankov 				    ep->eqn.pos, eqn_toks[tok]);
976*260e9a87SYuri Pankov 				break;
977*260e9a87SYuri Pankov 			}
978*260e9a87SYuri Pankov 			/* Handling depends on right/left. */
979*260e9a87SYuri Pankov 			if (STRNEQ(start, sz, "ceiling", 7)) {
980*260e9a87SYuri Pankov 				strlcpy(sym, "\\[rc]", sizeof(sym));
981*260e9a87SYuri Pankov 				parent->right = mandoc_strdup(sym);
982*260e9a87SYuri Pankov 			} else if (STRNEQ(start, sz, "floor", 5)) {
983*260e9a87SYuri Pankov 				strlcpy(sym, "\\[rf]", sizeof(sym));
984*260e9a87SYuri Pankov 				parent->right = mandoc_strdup(sym);
985*260e9a87SYuri Pankov 			} else
986*260e9a87SYuri Pankov 				parent->right = mandoc_strndup(start, sz);
987*260e9a87SYuri Pankov 		}
988*260e9a87SYuri Pankov 		parent = parent->parent;
989*260e9a87SYuri Pankov 		if (EQN_TOK_BRACE_CLOSE == tok && parent &&
990*260e9a87SYuri Pankov 		    (parent->type == EQN_PILE ||
991*260e9a87SYuri Pankov 		     parent->type == EQN_MATRIX))
992*260e9a87SYuri Pankov 			parent = parent->parent;
993*260e9a87SYuri Pankov 		/* Close out any "singleton" lists. */
994*260e9a87SYuri Pankov 		while (parent->type == EQN_LISTONE &&
995*260e9a87SYuri Pankov 		    parent->args == parent->expectargs)
996*260e9a87SYuri Pankov 			parent = parent->parent;
997*260e9a87SYuri Pankov 		break;
998*260e9a87SYuri Pankov 	case (EQN_TOK_BRACE_OPEN):
999*260e9a87SYuri Pankov 	case (EQN_TOK_LEFT):
1000*260e9a87SYuri Pankov 		/*
1001*260e9a87SYuri Pankov 		 * If we already have something in the stack and we're
1002*260e9a87SYuri Pankov 		 * in an expression, then rewind til we're not any more
1003*260e9a87SYuri Pankov 		 * (just like with the text node).
1004*260e9a87SYuri Pankov 		 */
1005*260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1006*260e9a87SYuri Pankov 			parent = parent->parent;
1007*260e9a87SYuri Pankov 		if (EQN_TOK_LEFT == tok &&
1008*260e9a87SYuri Pankov 		    (start = eqn_nexttok(ep, &sz)) == NULL) {
1009*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
1010*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
1011*260e9a87SYuri Pankov 			break;
1012*260e9a87SYuri Pankov 		}
1013*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
1014*260e9a87SYuri Pankov 		parent->type = EQN_LIST;
1015*260e9a87SYuri Pankov 		if (EQN_TOK_LEFT == tok) {
1016*260e9a87SYuri Pankov 			if (STRNEQ(start, sz, "ceiling", 7)) {
1017*260e9a87SYuri Pankov 				strlcpy(sym, "\\[lc]", sizeof(sym));
1018*260e9a87SYuri Pankov 				parent->left = mandoc_strdup(sym);
1019*260e9a87SYuri Pankov 			} else if (STRNEQ(start, sz, "floor", 5)) {
1020*260e9a87SYuri Pankov 				strlcpy(sym, "\\[lf]", sizeof(sym));
1021*260e9a87SYuri Pankov 				parent->left = mandoc_strdup(sym);
1022*260e9a87SYuri Pankov 			} else
1023*260e9a87SYuri Pankov 				parent->left = mandoc_strndup(start, sz);
1024*260e9a87SYuri Pankov 		}
1025*260e9a87SYuri Pankov 		break;
1026*260e9a87SYuri Pankov 	case (EQN_TOK_PILE):
1027*260e9a87SYuri Pankov 	case (EQN_TOK_LPILE):
1028*260e9a87SYuri Pankov 	case (EQN_TOK_RPILE):
1029*260e9a87SYuri Pankov 	case (EQN_TOK_CPILE):
1030*260e9a87SYuri Pankov 	case (EQN_TOK_CCOL):
1031*260e9a87SYuri Pankov 	case (EQN_TOK_LCOL):
1032*260e9a87SYuri Pankov 	case (EQN_TOK_RCOL):
1033*260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1034*260e9a87SYuri Pankov 			parent = parent->parent;
1035*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
1036*260e9a87SYuri Pankov 		parent->type = EQN_PILE;
1037*260e9a87SYuri Pankov 		parent->expectargs = 1;
1038*260e9a87SYuri Pankov 		break;
1039*260e9a87SYuri Pankov 	case (EQN_TOK_ABOVE):
1040*260e9a87SYuri Pankov 		for (cur = parent; cur != NULL; cur = cur->parent)
1041*260e9a87SYuri Pankov 			if (cur->type == EQN_PILE)
1042*260e9a87SYuri Pankov 				break;
1043*260e9a87SYuri Pankov 		if (cur == NULL) {
1044*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
1045*260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
1046*260e9a87SYuri Pankov 			break;
1047*260e9a87SYuri Pankov 		}
1048*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, cur);
1049*260e9a87SYuri Pankov 		parent->type = EQN_LIST;
1050*260e9a87SYuri Pankov 		break;
1051*260e9a87SYuri Pankov 	case (EQN_TOK_MATRIX):
1052*260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1053*260e9a87SYuri Pankov 			parent = parent->parent;
1054*260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
1055*260e9a87SYuri Pankov 		parent->type = EQN_MATRIX;
1056*260e9a87SYuri Pankov 		parent->expectargs = 1;
1057*260e9a87SYuri Pankov 		break;
1058*260e9a87SYuri Pankov 	case (EQN_TOK_EOF):
1059*260e9a87SYuri Pankov 		/*
1060*260e9a87SYuri Pankov 		 * End of file!
1061*260e9a87SYuri Pankov 		 * TODO: make sure we're not in an open subexpression.
1062*260e9a87SYuri Pankov 		 */
1063*260e9a87SYuri Pankov 		return(ROFF_EQN);
1064*260e9a87SYuri Pankov 	default:
1065*260e9a87SYuri Pankov 		assert(tok == EQN_TOK__MAX);
1066*260e9a87SYuri Pankov 		assert(NULL != p);
1067*260e9a87SYuri Pankov 		/*
1068*260e9a87SYuri Pankov 		 * If we already have something in the stack and we're
1069*260e9a87SYuri Pankov 		 * in an expression, then rewind til we're not any more.
1070*260e9a87SYuri Pankov 		 */
1071*260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1072*260e9a87SYuri Pankov 			parent = parent->parent;
1073*260e9a87SYuri Pankov 		cur = eqn_box_alloc(ep, parent);
1074*260e9a87SYuri Pankov 		cur->type = EQN_TEXT;
1075*260e9a87SYuri Pankov 		for (i = 0; i < EQNSYM__MAX; i++)
1076*260e9a87SYuri Pankov 			if (0 == strcmp(eqnsyms[i].str, p)) {
1077*260e9a87SYuri Pankov 				(void)snprintf(sym, sizeof(sym),
1078*260e9a87SYuri Pankov 					"\\[%s]", eqnsyms[i].sym);
1079*260e9a87SYuri Pankov 				cur->text = mandoc_strdup(sym);
1080*260e9a87SYuri Pankov 				free(p);
1081*260e9a87SYuri Pankov 				break;
1082*260e9a87SYuri Pankov 			}
1083*260e9a87SYuri Pankov 
1084*260e9a87SYuri Pankov 		if (i == EQNSYM__MAX)
1085*260e9a87SYuri Pankov 			cur->text = p;
1086*260e9a87SYuri Pankov 		/*
1087*260e9a87SYuri Pankov 		 * Post-process list status.
1088*260e9a87SYuri Pankov 		 */
1089*260e9a87SYuri Pankov 		while (parent->type == EQN_LISTONE &&
1090*260e9a87SYuri Pankov 		    parent->args == parent->expectargs)
1091*260e9a87SYuri Pankov 			parent = parent->parent;
1092*260e9a87SYuri Pankov 		break;
1093*260e9a87SYuri Pankov 	}
1094*260e9a87SYuri Pankov 	goto next_tok;
1095*260e9a87SYuri Pankov }
1096*260e9a87SYuri Pankov 
1097*260e9a87SYuri Pankov enum rofferr
1098*260e9a87SYuri Pankov eqn_end(struct eqn_node **epp)
109995c635efSGarrett D'Amore {
1100*260e9a87SYuri Pankov 	struct eqn_node	*ep;
110195c635efSGarrett D'Amore 
1102*260e9a87SYuri Pankov 	ep = *epp;
1103*260e9a87SYuri Pankov 	*epp = NULL;
110495c635efSGarrett D'Amore 
1105*260e9a87SYuri Pankov 	ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
1106*260e9a87SYuri Pankov 	ep->eqn.root->expectargs = UINT_MAX;
1107*260e9a87SYuri Pankov 	return(eqn_parse(ep, ep->eqn.root));
110895c635efSGarrett D'Amore }
110995c635efSGarrett D'Amore 
1110*260e9a87SYuri Pankov void
1111*260e9a87SYuri Pankov eqn_free(struct eqn_node *p)
111295c635efSGarrett D'Amore {
111395c635efSGarrett D'Amore 	int		 i;
111495c635efSGarrett D'Amore 
1115*260e9a87SYuri Pankov 	eqn_box_free(p->eqn.root);
111695c635efSGarrett D'Amore 
1117*260e9a87SYuri Pankov 	for (i = 0; i < (int)p->defsz; i++) {
1118*260e9a87SYuri Pankov 		free(p->defs[i].key);
1119*260e9a87SYuri Pankov 		free(p->defs[i].val);
1120*260e9a87SYuri Pankov 	}
1121*260e9a87SYuri Pankov 
1122*260e9a87SYuri Pankov 	free(p->data);
1123*260e9a87SYuri Pankov 	free(p->defs);
1124*260e9a87SYuri Pankov 	free(p);
112595c635efSGarrett D'Amore }
1126