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