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