eqn.c revision 1.7 1 1.7 christos /* Id: eqn.c,v 1.78 2017/07/15 16:26:17 schwarze Exp */
2 1.1 joerg /*
3 1.2 christos * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.7 christos * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze (at) openbsd.org>
5 1.1 joerg *
6 1.1 joerg * Permission to use, copy, modify, and distribute this software for any
7 1.1 joerg * purpose with or without fee is hereby granted, provided that the above
8 1.1 joerg * copyright notice and this permission notice appear in all copies.
9 1.1 joerg *
10 1.1 joerg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 joerg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 joerg */
18 1.1 joerg #include "config.h"
19 1.2 christos
20 1.2 christos #include <sys/types.h>
21 1.1 joerg
22 1.1 joerg #include <assert.h>
23 1.7 christos #include <ctype.h>
24 1.2 christos #include <limits.h>
25 1.1 joerg #include <stdio.h>
26 1.1 joerg #include <stdlib.h>
27 1.1 joerg #include <string.h>
28 1.1 joerg #include <time.h>
29 1.1 joerg
30 1.7 christos #include "mandoc_aux.h"
31 1.1 joerg #include "mandoc.h"
32 1.7 christos #include "roff.h"
33 1.1 joerg #include "libmandoc.h"
34 1.1 joerg #include "libroff.h"
35 1.1 joerg
36 1.2 christos #define EQN_NEST_MAX 128 /* maximum nesting of defines */
37 1.2 christos #define STRNEQ(p1, sz1, p2, sz2) \
38 1.2 christos ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
39 1.2 christos
40 1.2 christos enum eqn_tok {
41 1.2 christos EQN_TOK_DYAD = 0,
42 1.2 christos EQN_TOK_VEC,
43 1.2 christos EQN_TOK_UNDER,
44 1.2 christos EQN_TOK_BAR,
45 1.2 christos EQN_TOK_TILDE,
46 1.2 christos EQN_TOK_HAT,
47 1.2 christos EQN_TOK_DOT,
48 1.2 christos EQN_TOK_DOTDOT,
49 1.2 christos EQN_TOK_FWD,
50 1.2 christos EQN_TOK_BACK,
51 1.2 christos EQN_TOK_DOWN,
52 1.2 christos EQN_TOK_UP,
53 1.2 christos EQN_TOK_FAT,
54 1.2 christos EQN_TOK_ROMAN,
55 1.2 christos EQN_TOK_ITALIC,
56 1.2 christos EQN_TOK_BOLD,
57 1.2 christos EQN_TOK_SIZE,
58 1.2 christos EQN_TOK_SUB,
59 1.2 christos EQN_TOK_SUP,
60 1.2 christos EQN_TOK_SQRT,
61 1.2 christos EQN_TOK_OVER,
62 1.2 christos EQN_TOK_FROM,
63 1.2 christos EQN_TOK_TO,
64 1.2 christos EQN_TOK_BRACE_OPEN,
65 1.2 christos EQN_TOK_BRACE_CLOSE,
66 1.2 christos EQN_TOK_GSIZE,
67 1.2 christos EQN_TOK_GFONT,
68 1.2 christos EQN_TOK_MARK,
69 1.2 christos EQN_TOK_LINEUP,
70 1.2 christos EQN_TOK_LEFT,
71 1.2 christos EQN_TOK_RIGHT,
72 1.2 christos EQN_TOK_PILE,
73 1.2 christos EQN_TOK_LPILE,
74 1.2 christos EQN_TOK_RPILE,
75 1.2 christos EQN_TOK_CPILE,
76 1.2 christos EQN_TOK_MATRIX,
77 1.2 christos EQN_TOK_CCOL,
78 1.2 christos EQN_TOK_LCOL,
79 1.2 christos EQN_TOK_RCOL,
80 1.2 christos EQN_TOK_DELIM,
81 1.2 christos EQN_TOK_DEFINE,
82 1.2 christos EQN_TOK_TDEFINE,
83 1.2 christos EQN_TOK_NDEFINE,
84 1.2 christos EQN_TOK_UNDEF,
85 1.2 christos EQN_TOK_ABOVE,
86 1.7 christos EQN_TOK__MAX,
87 1.7 christos EQN_TOK_FUNC,
88 1.7 christos EQN_TOK_QUOTED,
89 1.7 christos EQN_TOK_SYM,
90 1.7 christos EQN_TOK_EOF
91 1.2 christos };
92 1.2 christos
93 1.2 christos static const char *eqn_toks[EQN_TOK__MAX] = {
94 1.2 christos "dyad", /* EQN_TOK_DYAD */
95 1.2 christos "vec", /* EQN_TOK_VEC */
96 1.2 christos "under", /* EQN_TOK_UNDER */
97 1.2 christos "bar", /* EQN_TOK_BAR */
98 1.2 christos "tilde", /* EQN_TOK_TILDE */
99 1.2 christos "hat", /* EQN_TOK_HAT */
100 1.2 christos "dot", /* EQN_TOK_DOT */
101 1.2 christos "dotdot", /* EQN_TOK_DOTDOT */
102 1.2 christos "fwd", /* EQN_TOK_FWD * */
103 1.2 christos "back", /* EQN_TOK_BACK */
104 1.2 christos "down", /* EQN_TOK_DOWN */
105 1.2 christos "up", /* EQN_TOK_UP */
106 1.2 christos "fat", /* EQN_TOK_FAT */
107 1.2 christos "roman", /* EQN_TOK_ROMAN */
108 1.2 christos "italic", /* EQN_TOK_ITALIC */
109 1.2 christos "bold", /* EQN_TOK_BOLD */
110 1.2 christos "size", /* EQN_TOK_SIZE */
111 1.2 christos "sub", /* EQN_TOK_SUB */
112 1.2 christos "sup", /* EQN_TOK_SUP */
113 1.2 christos "sqrt", /* EQN_TOK_SQRT */
114 1.2 christos "over", /* EQN_TOK_OVER */
115 1.2 christos "from", /* EQN_TOK_FROM */
116 1.2 christos "to", /* EQN_TOK_TO */
117 1.2 christos "{", /* EQN_TOK_BRACE_OPEN */
118 1.2 christos "}", /* EQN_TOK_BRACE_CLOSE */
119 1.2 christos "gsize", /* EQN_TOK_GSIZE */
120 1.2 christos "gfont", /* EQN_TOK_GFONT */
121 1.2 christos "mark", /* EQN_TOK_MARK */
122 1.2 christos "lineup", /* EQN_TOK_LINEUP */
123 1.2 christos "left", /* EQN_TOK_LEFT */
124 1.2 christos "right", /* EQN_TOK_RIGHT */
125 1.2 christos "pile", /* EQN_TOK_PILE */
126 1.2 christos "lpile", /* EQN_TOK_LPILE */
127 1.2 christos "rpile", /* EQN_TOK_RPILE */
128 1.2 christos "cpile", /* EQN_TOK_CPILE */
129 1.2 christos "matrix", /* EQN_TOK_MATRIX */
130 1.2 christos "ccol", /* EQN_TOK_CCOL */
131 1.2 christos "lcol", /* EQN_TOK_LCOL */
132 1.2 christos "rcol", /* EQN_TOK_RCOL */
133 1.2 christos "delim", /* EQN_TOK_DELIM */
134 1.2 christos "define", /* EQN_TOK_DEFINE */
135 1.2 christos "tdefine", /* EQN_TOK_TDEFINE */
136 1.2 christos "ndefine", /* EQN_TOK_NDEFINE */
137 1.2 christos "undef", /* EQN_TOK_UNDEF */
138 1.2 christos "above", /* EQN_TOK_ABOVE */
139 1.2 christos };
140 1.2 christos
141 1.7 christos static const char *const eqn_func[] = {
142 1.7 christos "acos", "acsc", "and", "arc", "asec", "asin", "atan",
143 1.7 christos "cos", "cosh", "coth", "csc", "det", "exp", "for",
144 1.7 christos "if", "lim", "ln", "log", "max", "min",
145 1.7 christos "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
146 1.7 christos };
147 1.7 christos
148 1.2 christos enum eqn_symt {
149 1.7 christos EQNSYM_alpha = 0,
150 1.2 christos EQNSYM_beta,
151 1.2 christos EQNSYM_chi,
152 1.2 christos EQNSYM_delta,
153 1.2 christos EQNSYM_epsilon,
154 1.2 christos EQNSYM_eta,
155 1.2 christos EQNSYM_gamma,
156 1.2 christos EQNSYM_iota,
157 1.2 christos EQNSYM_kappa,
158 1.2 christos EQNSYM_lambda,
159 1.2 christos EQNSYM_mu,
160 1.2 christos EQNSYM_nu,
161 1.2 christos EQNSYM_omega,
162 1.2 christos EQNSYM_omicron,
163 1.2 christos EQNSYM_phi,
164 1.2 christos EQNSYM_pi,
165 1.2 christos EQNSYM_ps,
166 1.2 christos EQNSYM_rho,
167 1.2 christos EQNSYM_sigma,
168 1.2 christos EQNSYM_tau,
169 1.2 christos EQNSYM_theta,
170 1.2 christos EQNSYM_upsilon,
171 1.2 christos EQNSYM_xi,
172 1.2 christos EQNSYM_zeta,
173 1.2 christos EQNSYM_DELTA,
174 1.2 christos EQNSYM_GAMMA,
175 1.2 christos EQNSYM_LAMBDA,
176 1.2 christos EQNSYM_OMEGA,
177 1.2 christos EQNSYM_PHI,
178 1.2 christos EQNSYM_PI,
179 1.2 christos EQNSYM_PSI,
180 1.2 christos EQNSYM_SIGMA,
181 1.2 christos EQNSYM_THETA,
182 1.2 christos EQNSYM_UPSILON,
183 1.2 christos EQNSYM_XI,
184 1.2 christos EQNSYM_inter,
185 1.2 christos EQNSYM_union,
186 1.2 christos EQNSYM_prod,
187 1.2 christos EQNSYM_int,
188 1.2 christos EQNSYM_sum,
189 1.2 christos EQNSYM_grad,
190 1.2 christos EQNSYM_del,
191 1.2 christos EQNSYM_times,
192 1.2 christos EQNSYM_cdot,
193 1.2 christos EQNSYM_nothing,
194 1.2 christos EQNSYM_approx,
195 1.2 christos EQNSYM_prime,
196 1.2 christos EQNSYM_half,
197 1.2 christos EQNSYM_partial,
198 1.2 christos EQNSYM_inf,
199 1.2 christos EQNSYM_muchgreat,
200 1.2 christos EQNSYM_muchless,
201 1.2 christos EQNSYM_larrow,
202 1.2 christos EQNSYM_rarrow,
203 1.2 christos EQNSYM_pm,
204 1.2 christos EQNSYM_nequal,
205 1.2 christos EQNSYM_equiv,
206 1.2 christos EQNSYM_lessequal,
207 1.2 christos EQNSYM_moreequal,
208 1.2 christos EQNSYM_minus,
209 1.2 christos EQNSYM__MAX
210 1.2 christos };
211 1.2 christos
212 1.2 christos struct eqnsym {
213 1.2 christos const char *str;
214 1.2 christos const char *sym;
215 1.2 christos };
216 1.2 christos
217 1.2 christos static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
218 1.2 christos { "alpha", "*a" }, /* EQNSYM_alpha */
219 1.2 christos { "beta", "*b" }, /* EQNSYM_beta */
220 1.2 christos { "chi", "*x" }, /* EQNSYM_chi */
221 1.2 christos { "delta", "*d" }, /* EQNSYM_delta */
222 1.2 christos { "epsilon", "*e" }, /* EQNSYM_epsilon */
223 1.2 christos { "eta", "*y" }, /* EQNSYM_eta */
224 1.2 christos { "gamma", "*g" }, /* EQNSYM_gamma */
225 1.2 christos { "iota", "*i" }, /* EQNSYM_iota */
226 1.2 christos { "kappa", "*k" }, /* EQNSYM_kappa */
227 1.2 christos { "lambda", "*l" }, /* EQNSYM_lambda */
228 1.2 christos { "mu", "*m" }, /* EQNSYM_mu */
229 1.2 christos { "nu", "*n" }, /* EQNSYM_nu */
230 1.2 christos { "omega", "*w" }, /* EQNSYM_omega */
231 1.2 christos { "omicron", "*o" }, /* EQNSYM_omicron */
232 1.2 christos { "phi", "*f" }, /* EQNSYM_phi */
233 1.2 christos { "pi", "*p" }, /* EQNSYM_pi */
234 1.2 christos { "psi", "*q" }, /* EQNSYM_psi */
235 1.2 christos { "rho", "*r" }, /* EQNSYM_rho */
236 1.2 christos { "sigma", "*s" }, /* EQNSYM_sigma */
237 1.2 christos { "tau", "*t" }, /* EQNSYM_tau */
238 1.2 christos { "theta", "*h" }, /* EQNSYM_theta */
239 1.2 christos { "upsilon", "*u" }, /* EQNSYM_upsilon */
240 1.2 christos { "xi", "*c" }, /* EQNSYM_xi */
241 1.2 christos { "zeta", "*z" }, /* EQNSYM_zeta */
242 1.2 christos { "DELTA", "*D" }, /* EQNSYM_DELTA */
243 1.2 christos { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
244 1.2 christos { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
245 1.2 christos { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
246 1.2 christos { "PHI", "*F" }, /* EQNSYM_PHI */
247 1.2 christos { "PI", "*P" }, /* EQNSYM_PI */
248 1.2 christos { "PSI", "*Q" }, /* EQNSYM_PSI */
249 1.2 christos { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
250 1.2 christos { "THETA", "*H" }, /* EQNSYM_THETA */
251 1.2 christos { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
252 1.2 christos { "XI", "*C" }, /* EQNSYM_XI */
253 1.2 christos { "inter", "ca" }, /* EQNSYM_inter */
254 1.2 christos { "union", "cu" }, /* EQNSYM_union */
255 1.2 christos { "prod", "product" }, /* EQNSYM_prod */
256 1.2 christos { "int", "integral" }, /* EQNSYM_int */
257 1.2 christos { "sum", "sum" }, /* EQNSYM_sum */
258 1.2 christos { "grad", "gr" }, /* EQNSYM_grad */
259 1.2 christos { "del", "gr" }, /* EQNSYM_del */
260 1.2 christos { "times", "mu" }, /* EQNSYM_times */
261 1.2 christos { "cdot", "pc" }, /* EQNSYM_cdot */
262 1.2 christos { "nothing", "&" }, /* EQNSYM_nothing */
263 1.2 christos { "approx", "~~" }, /* EQNSYM_approx */
264 1.2 christos { "prime", "fm" }, /* EQNSYM_prime */
265 1.2 christos { "half", "12" }, /* EQNSYM_half */
266 1.2 christos { "partial", "pd" }, /* EQNSYM_partial */
267 1.2 christos { "inf", "if" }, /* EQNSYM_inf */
268 1.2 christos { ">>", ">>" }, /* EQNSYM_muchgreat */
269 1.2 christos { "<<", "<<" }, /* EQNSYM_muchless */
270 1.2 christos { "<-", "<-" }, /* EQNSYM_larrow */
271 1.2 christos { "->", "->" }, /* EQNSYM_rarrow */
272 1.2 christos { "+-", "+-" }, /* EQNSYM_pm */
273 1.2 christos { "!=", "!=" }, /* EQNSYM_nequal */
274 1.2 christos { "==", "==" }, /* EQNSYM_equiv */
275 1.2 christos { "<=", "<=" }, /* EQNSYM_lessequal */
276 1.2 christos { ">=", ">=" }, /* EQNSYM_moreequal */
277 1.2 christos { "-", "mi" }, /* EQNSYM_minus */
278 1.2 christos };
279 1.2 christos
280 1.7 christos enum parse_mode {
281 1.7 christos MODE_QUOTED,
282 1.7 christos MODE_NOSUB,
283 1.7 christos MODE_SUB,
284 1.7 christos MODE_TOK
285 1.7 christos };
286 1.7 christos
287 1.2 christos static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
288 1.2 christos static struct eqn_box *eqn_box_makebinary(struct eqn_node *,
289 1.7 christos struct eqn_box *);
290 1.2 christos static void eqn_def(struct eqn_node *);
291 1.7 christos static struct eqn_def *eqn_def_find(struct eqn_node *);
292 1.2 christos static void eqn_delim(struct eqn_node *);
293 1.7 christos static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode);
294 1.2 christos static void eqn_undef(struct eqn_node *);
295 1.2 christos
296 1.2 christos
297 1.7 christos struct eqn_node *
298 1.7 christos eqn_alloc(struct mparse *parse)
299 1.1 joerg {
300 1.7 christos struct eqn_node *ep;
301 1.1 joerg
302 1.7 christos ep = mandoc_calloc(1, sizeof(*ep));
303 1.7 christos ep->parse = parse;
304 1.7 christos ep->gsize = EQN_DEFSIZE;
305 1.7 christos return ep;
306 1.7 christos }
307 1.2 christos
308 1.7 christos void
309 1.7 christos eqn_reset(struct eqn_node *ep)
310 1.7 christos {
311 1.7 christos free(ep->data);
312 1.7 christos ep->data = ep->start = ep->end = NULL;
313 1.7 christos ep->sz = ep->toksz = 0;
314 1.1 joerg }
315 1.1 joerg
316 1.7 christos void
317 1.7 christos eqn_read(struct eqn_node *ep, const char *p)
318 1.1 joerg {
319 1.7 christos char *cp;
320 1.1 joerg
321 1.7 christos if (ep->data == NULL) {
322 1.7 christos ep->sz = strlen(p);
323 1.7 christos ep->data = mandoc_strdup(p);
324 1.7 christos } else {
325 1.7 christos ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
326 1.7 christos free(ep->data);
327 1.7 christos ep->data = cp;
328 1.7 christos }
329 1.7 christos ep->sz += 1;
330 1.1 joerg }
331 1.1 joerg
332 1.2 christos /*
333 1.2 christos * Find the key "key" of the give size within our eqn-defined values.
334 1.2 christos */
335 1.2 christos static struct eqn_def *
336 1.7 christos eqn_def_find(struct eqn_node *ep)
337 1.2 christos {
338 1.2 christos int i;
339 1.2 christos
340 1.2 christos for (i = 0; i < (int)ep->defsz; i++)
341 1.2 christos if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
342 1.7 christos ep->defs[i].keysz, ep->start, ep->toksz))
343 1.5 christos return &ep->defs[i];
344 1.2 christos
345 1.5 christos return NULL;
346 1.2 christos }
347 1.2 christos
348 1.2 christos /*
349 1.7 christos * Parse a token from the input text. The modes are:
350 1.7 christos * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
351 1.7 christos * before its next occurence. Do not interpret the token in any
352 1.7 christos * way and return EQN_TOK_QUOTED. All other modes behave like
353 1.7 christos * MODE_QUOTED when *ep->start is '"'.
354 1.7 christos * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
355 1.7 christos * otherwise, it ends before the next whitespace or brace.
356 1.7 christos * Do not interpret the token and return EQN_TOK__MAX.
357 1.7 christos * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
358 1.7 christos * alias created with define. If it is an alias, replace it with
359 1.7 christos * its string value and reparse.
360 1.7 christos * MODE_TOK: Like MODE_SUB, but also check the token against the list
361 1.7 christos * of tokens, and if there is a match, return that token. Otherwise,
362 1.7 christos * if the token matches a symbol, return EQN_TOK_SYM; if it matches
363 1.7 christos * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
364 1.7 christos * a token match, *ep->start is set to an allocated string that the
365 1.7 christos * caller is expected to free.
366 1.7 christos * All modes skip whitespace following the end of the token.
367 1.2 christos */
368 1.7 christos static enum eqn_tok
369 1.7 christos eqn_next(struct eqn_node *ep, enum parse_mode mode)
370 1.2 christos {
371 1.7 christos static int last_len, lim;
372 1.7 christos
373 1.2 christos struct eqn_def *def;
374 1.7 christos size_t start;
375 1.7 christos int diff, i, quoted;
376 1.7 christos enum eqn_tok tok;
377 1.2 christos
378 1.7 christos /*
379 1.7 christos * Reset the recursion counter after advancing
380 1.7 christos * beyond the end of the previous substitution.
381 1.7 christos */
382 1.7 christos if (ep->end - ep->data >= last_len)
383 1.7 christos lim = 0;
384 1.2 christos
385 1.7 christos ep->start = ep->end;
386 1.7 christos quoted = mode == MODE_QUOTED;
387 1.7 christos for (;;) {
388 1.7 christos switch (*ep->start) {
389 1.7 christos case '\0':
390 1.7 christos ep->toksz = 0;
391 1.7 christos return EQN_TOK_EOF;
392 1.7 christos case '"':
393 1.7 christos quoted = 1;
394 1.7 christos break;
395 1.7 christos default:
396 1.7 christos break;
397 1.7 christos }
398 1.7 christos if (quoted) {
399 1.7 christos ep->end = strchr(ep->start + 1, *ep->start);
400 1.7 christos ep->start++; /* Skip opening quote. */
401 1.7 christos if (ep->end == NULL) {
402 1.7 christos mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
403 1.7 christos ep->node->line, ep->node->pos, NULL);
404 1.7 christos ep->end = strchr(ep->start, '\0');
405 1.7 christos }
406 1.7 christos } else {
407 1.7 christos ep->end = ep->start + 1;
408 1.7 christos if (*ep->start != '{' && *ep->start != '}')
409 1.7 christos ep->end += strcspn(ep->end, " ^~\"{}\t");
410 1.7 christos }
411 1.7 christos ep->toksz = ep->end - ep->start;
412 1.7 christos if (quoted && *ep->end != '\0')
413 1.7 christos ep->end++; /* Skip closing quote. */
414 1.7 christos while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
415 1.7 christos ep->end++;
416 1.7 christos if (quoted) /* Cannot return, may have to strndup. */
417 1.7 christos break;
418 1.7 christos if (mode == MODE_NOSUB)
419 1.7 christos return EQN_TOK__MAX;
420 1.7 christos if ((def = eqn_def_find(ep)) == NULL)
421 1.7 christos break;
422 1.7 christos if (++lim > EQN_NEST_MAX) {
423 1.7 christos mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
424 1.7 christos ep->node->line, ep->node->pos, NULL);
425 1.7 christos return EQN_TOK_EOF;
426 1.7 christos }
427 1.2 christos
428 1.7 christos /* Replace a defined name with its string value. */
429 1.7 christos if ((diff = def->valsz - ep->toksz) > 0) {
430 1.7 christos start = ep->start - ep->data;
431 1.2 christos ep->sz += diff;
432 1.2 christos ep->data = mandoc_realloc(ep->data, ep->sz + 1);
433 1.7 christos ep->start = ep->data + start;
434 1.2 christos }
435 1.7 christos if (diff)
436 1.7 christos memmove(ep->start + def->valsz, ep->start + ep->toksz,
437 1.7 christos strlen(ep->start + ep->toksz) + 1);
438 1.7 christos memcpy(ep->start, def->val, def->valsz);
439 1.7 christos last_len = ep->start - ep->data + def->valsz;
440 1.2 christos }
441 1.7 christos if (mode != MODE_TOK)
442 1.7 christos return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
443 1.2 christos if (quoted) {
444 1.7 christos ep->start = mandoc_strndup(ep->start, ep->toksz);
445 1.7 christos return EQN_TOK_QUOTED;
446 1.2 christos }
447 1.7 christos for (tok = 0; tok < EQN_TOK__MAX; tok++)
448 1.7 christos if (STRNEQ(ep->start, ep->toksz,
449 1.7 christos eqn_toks[tok], strlen(eqn_toks[tok])))
450 1.7 christos return tok;
451 1.7 christos
452 1.7 christos for (i = 0; i < EQNSYM__MAX; i++) {
453 1.7 christos if (STRNEQ(ep->start, ep->toksz,
454 1.7 christos eqnsyms[i].str, strlen(eqnsyms[i].str))) {
455 1.7 christos mandoc_asprintf(&ep->start,
456 1.7 christos "\\[%s]", eqnsyms[i].sym);
457 1.7 christos return EQN_TOK_SYM;
458 1.7 christos }
459 1.2 christos }
460 1.7 christos ep->start = mandoc_strndup(ep->start, ep->toksz);
461 1.7 christos for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
462 1.7 christos if (STRNEQ(ep->start, ep->toksz,
463 1.7 christos eqn_func[i], strlen(eqn_func[i])))
464 1.7 christos return EQN_TOK_FUNC;
465 1.7 christos return EQN_TOK__MAX;
466 1.2 christos }
467 1.2 christos
468 1.7 christos void
469 1.2 christos eqn_box_free(struct eqn_box *bp)
470 1.2 christos {
471 1.2 christos
472 1.2 christos if (bp->first)
473 1.2 christos eqn_box_free(bp->first);
474 1.2 christos if (bp->next)
475 1.2 christos eqn_box_free(bp->next);
476 1.2 christos
477 1.2 christos free(bp->text);
478 1.2 christos free(bp->left);
479 1.2 christos free(bp->right);
480 1.2 christos free(bp->top);
481 1.2 christos free(bp->bottom);
482 1.2 christos free(bp);
483 1.2 christos }
484 1.2 christos
485 1.2 christos /*
486 1.2 christos * Allocate a box as the last child of the parent node.
487 1.2 christos */
488 1.2 christos static struct eqn_box *
489 1.2 christos eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
490 1.2 christos {
491 1.2 christos struct eqn_box *bp;
492 1.2 christos
493 1.2 christos bp = mandoc_calloc(1, sizeof(struct eqn_box));
494 1.2 christos bp->parent = parent;
495 1.2 christos bp->parent->args++;
496 1.2 christos bp->expectargs = UINT_MAX;
497 1.7 christos bp->font = bp->parent->font;
498 1.2 christos bp->size = ep->gsize;
499 1.2 christos
500 1.2 christos if (NULL != parent->first) {
501 1.2 christos parent->last->next = bp;
502 1.2 christos bp->prev = parent->last;
503 1.2 christos } else
504 1.2 christos parent->first = bp;
505 1.2 christos
506 1.2 christos parent->last = bp;
507 1.5 christos return bp;
508 1.2 christos }
509 1.2 christos
510 1.2 christos /*
511 1.2 christos * Reparent the current last node (of the current parent) under a new
512 1.2 christos * EQN_SUBEXPR as the first element.
513 1.2 christos * Then return the new parent.
514 1.2 christos * The new EQN_SUBEXPR will have a two-child limit.
515 1.2 christos */
516 1.2 christos static struct eqn_box *
517 1.7 christos eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
518 1.2 christos {
519 1.2 christos struct eqn_box *b, *newb;
520 1.2 christos
521 1.2 christos assert(NULL != parent->last);
522 1.2 christos b = parent->last;
523 1.2 christos if (parent->last == parent->first)
524 1.2 christos parent->first = NULL;
525 1.2 christos parent->args--;
526 1.2 christos parent->last = b->prev;
527 1.2 christos b->prev = NULL;
528 1.2 christos newb = eqn_box_alloc(ep, parent);
529 1.2 christos newb->type = EQN_SUBEXPR;
530 1.2 christos newb->expectargs = 2;
531 1.2 christos newb->args = 1;
532 1.2 christos newb->first = newb->last = b;
533 1.2 christos newb->first->next = NULL;
534 1.2 christos b->parent = newb;
535 1.5 christos return newb;
536 1.2 christos }
537 1.2 christos
538 1.2 christos /*
539 1.2 christos * Parse the "delim" control statement.
540 1.2 christos */
541 1.2 christos static void
542 1.2 christos eqn_delim(struct eqn_node *ep)
543 1.2 christos {
544 1.7 christos if (ep->end[0] == '\0' || ep->end[1] == '\0') {
545 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
546 1.7 christos ep->node->line, ep->node->pos, "delim");
547 1.7 christos if (ep->end[0] != '\0')
548 1.7 christos ep->end++;
549 1.7 christos } else if (strncmp(ep->end, "off", 3) == 0) {
550 1.2 christos ep->delim = 0;
551 1.7 christos ep->end += 3;
552 1.7 christos } else if (strncmp(ep->end, "on", 2) == 0) {
553 1.2 christos if (ep->odelim && ep->cdelim)
554 1.2 christos ep->delim = 1;
555 1.7 christos ep->end += 2;
556 1.7 christos } else {
557 1.7 christos ep->odelim = *ep->end++;
558 1.7 christos ep->cdelim = *ep->end++;
559 1.2 christos ep->delim = 1;
560 1.2 christos }
561 1.2 christos }
562 1.2 christos
563 1.2 christos /*
564 1.2 christos * Undefine a previously-defined string.
565 1.2 christos */
566 1.2 christos static void
567 1.2 christos eqn_undef(struct eqn_node *ep)
568 1.2 christos {
569 1.2 christos struct eqn_def *def;
570 1.2 christos
571 1.7 christos if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
572 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
573 1.7 christos ep->node->line, ep->node->pos, "undef");
574 1.2 christos return;
575 1.2 christos }
576 1.7 christos if ((def = eqn_def_find(ep)) == NULL)
577 1.2 christos return;
578 1.2 christos free(def->key);
579 1.2 christos free(def->val);
580 1.2 christos def->key = def->val = NULL;
581 1.2 christos def->keysz = def->valsz = 0;
582 1.2 christos }
583 1.2 christos
584 1.2 christos static void
585 1.2 christos eqn_def(struct eqn_node *ep)
586 1.2 christos {
587 1.2 christos struct eqn_def *def;
588 1.2 christos int i;
589 1.2 christos
590 1.7 christos if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
591 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
592 1.7 christos ep->node->line, ep->node->pos, "define");
593 1.2 christos return;
594 1.2 christos }
595 1.2 christos
596 1.2 christos /*
597 1.2 christos * Search for a key that already exists.
598 1.2 christos * Create a new key if none is found.
599 1.2 christos */
600 1.7 christos if ((def = eqn_def_find(ep)) == NULL) {
601 1.2 christos /* Find holes in string array. */
602 1.2 christos for (i = 0; i < (int)ep->defsz; i++)
603 1.2 christos if (0 == ep->defs[i].keysz)
604 1.2 christos break;
605 1.2 christos
606 1.2 christos if (i == (int)ep->defsz) {
607 1.2 christos ep->defsz++;
608 1.2 christos ep->defs = mandoc_reallocarray(ep->defs,
609 1.2 christos ep->defsz, sizeof(struct eqn_def));
610 1.2 christos ep->defs[i].key = ep->defs[i].val = NULL;
611 1.2 christos }
612 1.2 christos
613 1.2 christos def = ep->defs + i;
614 1.2 christos free(def->key);
615 1.7 christos def->key = mandoc_strndup(ep->start, ep->toksz);
616 1.7 christos def->keysz = ep->toksz;
617 1.2 christos }
618 1.2 christos
619 1.7 christos if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
620 1.2 christos mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
621 1.7 christos ep->node->line, ep->node->pos, "define %s", def->key);
622 1.2 christos free(def->key);
623 1.2 christos free(def->val);
624 1.2 christos def->key = def->val = NULL;
625 1.2 christos def->keysz = def->valsz = 0;
626 1.2 christos return;
627 1.2 christos }
628 1.2 christos free(def->val);
629 1.7 christos def->val = mandoc_strndup(ep->start, ep->toksz);
630 1.7 christos def->valsz = ep->toksz;
631 1.2 christos }
632 1.2 christos
633 1.7 christos void
634 1.7 christos eqn_parse(struct eqn_node *ep)
635 1.2 christos {
636 1.7 christos struct eqn_box *cur, *nbox, *parent, *split;
637 1.7 christos const char *cp, *cpn;
638 1.2 christos char *p;
639 1.7 christos enum eqn_tok tok;
640 1.7 christos enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
641 1.2 christos int size;
642 1.2 christos
643 1.7 christos parent = ep->node->eqn;
644 1.2 christos assert(parent != NULL);
645 1.2 christos
646 1.2 christos /*
647 1.2 christos * Empty equation.
648 1.2 christos * Do not add it to the high-level syntax tree.
649 1.2 christos */
650 1.2 christos
651 1.2 christos if (ep->data == NULL)
652 1.7 christos return;
653 1.7 christos
654 1.7 christos ep->start = ep->end = ep->data + strspn(ep->data, " ^~");
655 1.2 christos
656 1.2 christos next_tok:
657 1.7 christos tok = eqn_next(ep, MODE_TOK);
658 1.2 christos switch (tok) {
659 1.7 christos case EQN_TOK_UNDEF:
660 1.2 christos eqn_undef(ep);
661 1.2 christos break;
662 1.7 christos case EQN_TOK_NDEFINE:
663 1.7 christos case EQN_TOK_DEFINE:
664 1.2 christos eqn_def(ep);
665 1.2 christos break;
666 1.7 christos case EQN_TOK_TDEFINE:
667 1.7 christos if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
668 1.7 christos eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
669 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
670 1.7 christos ep->node->line, ep->node->pos, "tdefine");
671 1.2 christos break;
672 1.7 christos case EQN_TOK_DELIM:
673 1.2 christos eqn_delim(ep);
674 1.2 christos break;
675 1.7 christos case EQN_TOK_GFONT:
676 1.7 christos if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
677 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
678 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
679 1.2 christos break;
680 1.7 christos case EQN_TOK_MARK:
681 1.7 christos case EQN_TOK_LINEUP:
682 1.2 christos /* Ignore these. */
683 1.2 christos break;
684 1.7 christos case EQN_TOK_DYAD:
685 1.7 christos case EQN_TOK_VEC:
686 1.7 christos case EQN_TOK_UNDER:
687 1.7 christos case EQN_TOK_BAR:
688 1.7 christos case EQN_TOK_TILDE:
689 1.7 christos case EQN_TOK_HAT:
690 1.7 christos case EQN_TOK_DOT:
691 1.7 christos case EQN_TOK_DOTDOT:
692 1.2 christos if (parent->last == NULL) {
693 1.2 christos mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
694 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
695 1.2 christos cur = eqn_box_alloc(ep, parent);
696 1.2 christos cur->type = EQN_TEXT;
697 1.2 christos cur->text = mandoc_strdup("");
698 1.2 christos }
699 1.7 christos parent = eqn_box_makebinary(ep, parent);
700 1.7 christos parent->type = EQN_LIST;
701 1.2 christos parent->expectargs = 1;
702 1.7 christos parent->font = EQNFONT_ROMAN;
703 1.2 christos switch (tok) {
704 1.7 christos case EQN_TOK_DOTDOT:
705 1.7 christos parent->top = mandoc_strdup("\\[ad]");
706 1.2 christos break;
707 1.7 christos case EQN_TOK_VEC:
708 1.7 christos parent->top = mandoc_strdup("\\[->]");
709 1.2 christos break;
710 1.7 christos case EQN_TOK_DYAD:
711 1.7 christos parent->top = mandoc_strdup("\\[<>]");
712 1.2 christos break;
713 1.7 christos case EQN_TOK_TILDE:
714 1.7 christos parent->top = mandoc_strdup("\\[a~]");
715 1.2 christos break;
716 1.7 christos case EQN_TOK_UNDER:
717 1.7 christos parent->bottom = mandoc_strdup("\\[ul]");
718 1.2 christos break;
719 1.7 christos case EQN_TOK_BAR:
720 1.7 christos parent->top = mandoc_strdup("\\[rn]");
721 1.2 christos break;
722 1.7 christos case EQN_TOK_DOT:
723 1.7 christos parent->top = mandoc_strdup("\\[a.]");
724 1.2 christos break;
725 1.7 christos case EQN_TOK_HAT:
726 1.7 christos parent->top = mandoc_strdup("\\[ha]");
727 1.2 christos break;
728 1.2 christos default:
729 1.2 christos abort();
730 1.2 christos }
731 1.2 christos parent = parent->parent;
732 1.2 christos break;
733 1.7 christos case EQN_TOK_FWD:
734 1.7 christos case EQN_TOK_BACK:
735 1.7 christos case EQN_TOK_DOWN:
736 1.7 christos case EQN_TOK_UP:
737 1.7 christos if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
738 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
739 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
740 1.2 christos break;
741 1.7 christos case EQN_TOK_FAT:
742 1.7 christos case EQN_TOK_ROMAN:
743 1.7 christos case EQN_TOK_ITALIC:
744 1.7 christos case EQN_TOK_BOLD:
745 1.2 christos while (parent->args == parent->expectargs)
746 1.2 christos parent = parent->parent;
747 1.2 christos /*
748 1.2 christos * These values apply to the next word or sequence of
749 1.2 christos * words; thus, we mark that we'll have a child with
750 1.2 christos * exactly one of those.
751 1.2 christos */
752 1.2 christos parent = eqn_box_alloc(ep, parent);
753 1.7 christos parent->type = EQN_LIST;
754 1.2 christos parent->expectargs = 1;
755 1.2 christos switch (tok) {
756 1.7 christos case EQN_TOK_FAT:
757 1.2 christos parent->font = EQNFONT_FAT;
758 1.2 christos break;
759 1.7 christos case EQN_TOK_ROMAN:
760 1.2 christos parent->font = EQNFONT_ROMAN;
761 1.2 christos break;
762 1.7 christos case EQN_TOK_ITALIC:
763 1.2 christos parent->font = EQNFONT_ITALIC;
764 1.2 christos break;
765 1.7 christos case EQN_TOK_BOLD:
766 1.2 christos parent->font = EQNFONT_BOLD;
767 1.2 christos break;
768 1.2 christos default:
769 1.2 christos abort();
770 1.2 christos }
771 1.2 christos break;
772 1.7 christos case EQN_TOK_SIZE:
773 1.7 christos case EQN_TOK_GSIZE:
774 1.2 christos /* Accept two values: integral size and a single. */
775 1.7 christos if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
776 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
777 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
778 1.2 christos break;
779 1.2 christos }
780 1.7 christos size = mandoc_strntoi(ep->start, ep->toksz, 10);
781 1.2 christos if (-1 == size) {
782 1.2 christos mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
783 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
784 1.2 christos break;
785 1.2 christos }
786 1.2 christos if (EQN_TOK_GSIZE == tok) {
787 1.2 christos ep->gsize = size;
788 1.2 christos break;
789 1.2 christos }
790 1.7 christos while (parent->args == parent->expectargs)
791 1.7 christos parent = parent->parent;
792 1.2 christos parent = eqn_box_alloc(ep, parent);
793 1.7 christos parent->type = EQN_LIST;
794 1.2 christos parent->expectargs = 1;
795 1.2 christos parent->size = size;
796 1.2 christos break;
797 1.7 christos case EQN_TOK_FROM:
798 1.7 christos case EQN_TOK_TO:
799 1.7 christos case EQN_TOK_SUB:
800 1.7 christos case EQN_TOK_SUP:
801 1.2 christos /*
802 1.2 christos * We have a left-right-associative expression.
803 1.2 christos * Repivot under a positional node, open a child scope
804 1.2 christos * and keep on reading.
805 1.2 christos */
806 1.2 christos if (parent->last == NULL) {
807 1.2 christos mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
808 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
809 1.2 christos cur = eqn_box_alloc(ep, parent);
810 1.2 christos cur->type = EQN_TEXT;
811 1.2 christos cur->text = mandoc_strdup("");
812 1.2 christos }
813 1.7 christos while (parent->expectargs == 1 && parent->args == 1)
814 1.7 christos parent = parent->parent;
815 1.7 christos if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) {
816 1.7 christos for (cur = parent; cur != NULL; cur = cur->parent)
817 1.7 christos if (cur->pos == EQNPOS_SUB ||
818 1.7 christos cur->pos == EQNPOS_SUP ||
819 1.7 christos cur->pos == EQNPOS_SUBSUP ||
820 1.7 christos cur->pos == EQNPOS_SQRT ||
821 1.7 christos cur->pos == EQNPOS_OVER)
822 1.7 christos break;
823 1.7 christos if (cur != NULL)
824 1.7 christos parent = cur->parent;
825 1.7 christos }
826 1.7 christos if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
827 1.2 christos parent->expectargs = 3;
828 1.2 christos parent->pos = EQNPOS_SUBSUP;
829 1.2 christos break;
830 1.2 christos }
831 1.7 christos if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
832 1.2 christos parent->expectargs = 3;
833 1.2 christos parent->pos = EQNPOS_FROMTO;
834 1.2 christos break;
835 1.2 christos }
836 1.7 christos parent = eqn_box_makebinary(ep, parent);
837 1.2 christos switch (tok) {
838 1.7 christos case EQN_TOK_FROM:
839 1.7 christos parent->pos = EQNPOS_FROM;
840 1.2 christos break;
841 1.7 christos case EQN_TOK_TO:
842 1.7 christos parent->pos = EQNPOS_TO;
843 1.2 christos break;
844 1.7 christos case EQN_TOK_SUP:
845 1.7 christos parent->pos = EQNPOS_SUP;
846 1.2 christos break;
847 1.7 christos case EQN_TOK_SUB:
848 1.7 christos parent->pos = EQNPOS_SUB;
849 1.2 christos break;
850 1.2 christos default:
851 1.2 christos abort();
852 1.2 christos }
853 1.2 christos break;
854 1.7 christos case EQN_TOK_SQRT:
855 1.2 christos while (parent->args == parent->expectargs)
856 1.2 christos parent = parent->parent;
857 1.2 christos /*
858 1.2 christos * Accept a left-right-associative set of arguments just
859 1.2 christos * like sub and sup and friends but without rebalancing
860 1.2 christos * under a pivot.
861 1.2 christos */
862 1.2 christos parent = eqn_box_alloc(ep, parent);
863 1.2 christos parent->type = EQN_SUBEXPR;
864 1.2 christos parent->pos = EQNPOS_SQRT;
865 1.2 christos parent->expectargs = 1;
866 1.2 christos break;
867 1.7 christos case EQN_TOK_OVER:
868 1.2 christos /*
869 1.2 christos * We have a right-left-associative fraction.
870 1.2 christos * Close out anything that's currently open, then
871 1.2 christos * rebalance and continue reading.
872 1.2 christos */
873 1.2 christos if (parent->last == NULL) {
874 1.2 christos mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
875 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
876 1.2 christos cur = eqn_box_alloc(ep, parent);
877 1.2 christos cur->type = EQN_TEXT;
878 1.2 christos cur->text = mandoc_strdup("");
879 1.2 christos }
880 1.7 christos while (parent->args == parent->expectargs)
881 1.7 christos parent = parent->parent;
882 1.2 christos while (EQN_SUBEXPR == parent->type)
883 1.2 christos parent = parent->parent;
884 1.7 christos parent = eqn_box_makebinary(ep, parent);
885 1.7 christos parent->pos = EQNPOS_OVER;
886 1.2 christos break;
887 1.7 christos case EQN_TOK_RIGHT:
888 1.7 christos case EQN_TOK_BRACE_CLOSE:
889 1.2 christos /*
890 1.2 christos * Close out the existing brace.
891 1.2 christos * FIXME: this is a shitty sentinel: we should really
892 1.2 christos * have a native EQN_BRACE type or whatnot.
893 1.2 christos */
894 1.2 christos for (cur = parent; cur != NULL; cur = cur->parent)
895 1.2 christos if (cur->type == EQN_LIST &&
896 1.7 christos cur->expectargs > 1 &&
897 1.2 christos (tok == EQN_TOK_BRACE_CLOSE ||
898 1.2 christos cur->left != NULL))
899 1.2 christos break;
900 1.2 christos if (cur == NULL) {
901 1.2 christos mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
902 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
903 1.2 christos break;
904 1.2 christos }
905 1.2 christos parent = cur;
906 1.2 christos if (EQN_TOK_RIGHT == tok) {
907 1.7 christos if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
908 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY,
909 1.7 christos ep->parse, ep->node->line,
910 1.7 christos ep->node->pos, eqn_toks[tok]);
911 1.2 christos break;
912 1.2 christos }
913 1.2 christos /* Handling depends on right/left. */
914 1.7 christos if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
915 1.7 christos parent->right = mandoc_strdup("\\[rc]");
916 1.7 christos else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
917 1.7 christos parent->right = mandoc_strdup("\\[rf]");
918 1.7 christos else
919 1.7 christos parent->right =
920 1.7 christos mandoc_strndup(ep->start, ep->toksz);
921 1.2 christos }
922 1.2 christos parent = parent->parent;
923 1.5 christos if (tok == EQN_TOK_BRACE_CLOSE &&
924 1.2 christos (parent->type == EQN_PILE ||
925 1.2 christos parent->type == EQN_MATRIX))
926 1.2 christos parent = parent->parent;
927 1.2 christos /* Close out any "singleton" lists. */
928 1.7 christos while (parent->type == EQN_LIST &&
929 1.7 christos parent->expectargs == 1 &&
930 1.7 christos parent->args == 1)
931 1.2 christos parent = parent->parent;
932 1.2 christos break;
933 1.7 christos case EQN_TOK_BRACE_OPEN:
934 1.7 christos case EQN_TOK_LEFT:
935 1.2 christos /*
936 1.2 christos * If we already have something in the stack and we're
937 1.2 christos * in an expression, then rewind til we're not any more
938 1.2 christos * (just like with the text node).
939 1.2 christos */
940 1.2 christos while (parent->args == parent->expectargs)
941 1.2 christos parent = parent->parent;
942 1.2 christos if (EQN_TOK_LEFT == tok &&
943 1.7 christos eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
944 1.2 christos mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
945 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
946 1.2 christos break;
947 1.2 christos }
948 1.2 christos parent = eqn_box_alloc(ep, parent);
949 1.2 christos parent->type = EQN_LIST;
950 1.2 christos if (EQN_TOK_LEFT == tok) {
951 1.7 christos if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
952 1.7 christos parent->left = mandoc_strdup("\\[lc]");
953 1.7 christos else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
954 1.7 christos parent->left = mandoc_strdup("\\[lf]");
955 1.7 christos else
956 1.7 christos parent->left =
957 1.7 christos mandoc_strndup(ep->start, ep->toksz);
958 1.2 christos }
959 1.2 christos break;
960 1.7 christos case EQN_TOK_PILE:
961 1.7 christos case EQN_TOK_LPILE:
962 1.7 christos case EQN_TOK_RPILE:
963 1.7 christos case EQN_TOK_CPILE:
964 1.7 christos case EQN_TOK_CCOL:
965 1.7 christos case EQN_TOK_LCOL:
966 1.7 christos case EQN_TOK_RCOL:
967 1.2 christos while (parent->args == parent->expectargs)
968 1.2 christos parent = parent->parent;
969 1.2 christos parent = eqn_box_alloc(ep, parent);
970 1.2 christos parent->type = EQN_PILE;
971 1.2 christos parent->expectargs = 1;
972 1.2 christos break;
973 1.7 christos case EQN_TOK_ABOVE:
974 1.2 christos for (cur = parent; cur != NULL; cur = cur->parent)
975 1.2 christos if (cur->type == EQN_PILE)
976 1.2 christos break;
977 1.2 christos if (cur == NULL) {
978 1.2 christos mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
979 1.7 christos ep->node->line, ep->node->pos, eqn_toks[tok]);
980 1.2 christos break;
981 1.2 christos }
982 1.2 christos parent = eqn_box_alloc(ep, cur);
983 1.2 christos parent->type = EQN_LIST;
984 1.2 christos break;
985 1.7 christos case EQN_TOK_MATRIX:
986 1.2 christos while (parent->args == parent->expectargs)
987 1.2 christos parent = parent->parent;
988 1.2 christos parent = eqn_box_alloc(ep, parent);
989 1.2 christos parent->type = EQN_MATRIX;
990 1.2 christos parent->expectargs = 1;
991 1.2 christos break;
992 1.7 christos case EQN_TOK_EOF:
993 1.7 christos return;
994 1.7 christos case EQN_TOK__MAX:
995 1.7 christos case EQN_TOK_FUNC:
996 1.7 christos case EQN_TOK_QUOTED:
997 1.7 christos case EQN_TOK_SYM:
998 1.7 christos p = ep->start;
999 1.7 christos assert(p != NULL);
1000 1.2 christos /*
1001 1.2 christos * If we already have something in the stack and we're
1002 1.2 christos * in an expression, then rewind til we're not any more.
1003 1.2 christos */
1004 1.2 christos while (parent->args == parent->expectargs)
1005 1.2 christos parent = parent->parent;
1006 1.2 christos cur = eqn_box_alloc(ep, parent);
1007 1.2 christos cur->type = EQN_TEXT;
1008 1.7 christos cur->text = p;
1009 1.7 christos switch (tok) {
1010 1.7 christos case EQN_TOK_FUNC:
1011 1.7 christos cur->font = EQNFONT_ROMAN;
1012 1.7 christos break;
1013 1.7 christos case EQN_TOK_QUOTED:
1014 1.7 christos if (cur->font == EQNFONT_NONE)
1015 1.7 christos cur->font = EQNFONT_ITALIC;
1016 1.7 christos break;
1017 1.7 christos case EQN_TOK_SYM:
1018 1.7 christos break;
1019 1.7 christos default:
1020 1.7 christos if (cur->font != EQNFONT_NONE || *p == '\0')
1021 1.2 christos break;
1022 1.7 christos cpn = p - 1;
1023 1.7 christos ccln = CCL_LET;
1024 1.7 christos split = NULL;
1025 1.7 christos for (;;) {
1026 1.7 christos /* Advance to next character. */
1027 1.7 christos cp = cpn++;
1028 1.7 christos ccl = ccln;
1029 1.7 christos ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
1030 1.7 christos isdigit((unsigned char)*cpn) ||
1031 1.7 christos (*cpn == '.' && (ccl == CCL_DIG ||
1032 1.7 christos isdigit((unsigned char)cpn[1]))) ?
1033 1.7 christos CCL_DIG : CCL_PUN;
1034 1.7 christos /* No boundary before first character. */
1035 1.7 christos if (cp < p)
1036 1.7 christos continue;
1037 1.7 christos cur->font = ccl == CCL_LET ?
1038 1.7 christos EQNFONT_ITALIC : EQNFONT_ROMAN;
1039 1.7 christos if (*cp == '\\')
1040 1.7 christos mandoc_escape(&cpn, NULL, NULL);
1041 1.7 christos /* No boundary after last character. */
1042 1.7 christos if (*cpn == '\0')
1043 1.7 christos break;
1044 1.7 christos if (ccln == ccl && *cp != ',' && *cpn != ',')
1045 1.7 christos continue;
1046 1.7 christos /* Boundary found, split the text. */
1047 1.7 christos if (parent->args == parent->expectargs) {
1048 1.7 christos /* Remove the text from the tree. */
1049 1.7 christos if (cur->prev == NULL)
1050 1.7 christos parent->first = cur->next;
1051 1.7 christos else
1052 1.7 christos cur->prev->next = NULL;
1053 1.7 christos parent->last = cur->prev;
1054 1.7 christos parent->args--;
1055 1.7 christos /* Set up a list instead. */
1056 1.7 christos split = eqn_box_alloc(ep, parent);
1057 1.7 christos split->type = EQN_LIST;
1058 1.7 christos /* Insert the word into the list. */
1059 1.7 christos split->first = split->last = cur;
1060 1.7 christos cur->parent = split;
1061 1.7 christos cur->prev = NULL;
1062 1.7 christos parent = split;
1063 1.7 christos }
1064 1.7 christos /* Append a new text box. */
1065 1.7 christos nbox = eqn_box_alloc(ep, parent);
1066 1.7 christos nbox->type = EQN_TEXT;
1067 1.7 christos nbox->text = mandoc_strdup(cpn);
1068 1.7 christos /* Truncate the old box. */
1069 1.7 christos p = mandoc_strndup(cur->text,
1070 1.7 christos cpn - cur->text);
1071 1.7 christos free(cur->text);
1072 1.7 christos cur->text = p;
1073 1.7 christos /* Setup to process the new box. */
1074 1.7 christos cur = nbox;
1075 1.7 christos p = nbox->text;
1076 1.7 christos cpn = p - 1;
1077 1.7 christos ccln = CCL_LET;
1078 1.2 christos }
1079 1.7 christos if (split != NULL)
1080 1.7 christos parent = split->parent;
1081 1.7 christos break;
1082 1.7 christos }
1083 1.2 christos break;
1084 1.7 christos default:
1085 1.7 christos abort();
1086 1.2 christos }
1087 1.2 christos goto next_tok;
1088 1.2 christos }
1089 1.2 christos
1090 1.1 joerg void
1091 1.1 joerg eqn_free(struct eqn_node *p)
1092 1.1 joerg {
1093 1.2 christos int i;
1094 1.2 christos
1095 1.2 christos for (i = 0; i < (int)p->defsz; i++) {
1096 1.2 christos free(p->defs[i].key);
1097 1.2 christos free(p->defs[i].val);
1098 1.2 christos }
1099 1.1 joerg
1100 1.2 christos free(p->data);
1101 1.2 christos free(p->defs);
1102 1.1 joerg free(p);
1103 1.1 joerg }
1104