1/*	$Id: eqn_term.c,v 1.13 2017/07/08 14:51:04 schwarze Exp $ */
2/*
3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2017 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 <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "mandoc.h"
28#include "out.h"
29#include "term.h"
30
31static	const enum termfont fontmap[EQNFONT__MAX] = {
32	TERMFONT_NONE, /* EQNFONT_NONE */
33	TERMFONT_NONE, /* EQNFONT_ROMAN */
34	TERMFONT_BOLD, /* EQNFONT_BOLD */
35	TERMFONT_BOLD, /* EQNFONT_FAT */
36	TERMFONT_UNDER /* EQNFONT_ITALIC */
37};
38
39static void	eqn_box(struct termp *, const struct eqn_box *);
40
41
42void
43term_eqn(struct termp *p, const struct eqn_box *bp)
44{
45
46	eqn_box(p, bp);
47	p->flags &= ~TERMP_NOSPACE;
48}
49
50static void
51eqn_box(struct termp *p, const struct eqn_box *bp)
52{
53	const struct eqn_box *child;
54	int delim;
55
56	/* Delimiters around this box? */
57
58	if ((bp->type == EQN_LIST && bp->expectargs > 1) ||
59	    (bp->type == EQN_PILE && (bp->prev || bp->next)) ||
60	    (bp->parent != NULL && (bp->parent->pos == EQNPOS_SQRT ||
61	    /* Diacritic followed by ^ or _. */
62	    ((bp->top != NULL || bp->bottom != NULL) &&
63	     bp->parent->type == EQN_SUBEXPR &&
64	     bp->parent->pos != EQNPOS_OVER && bp->next != NULL) ||
65	    /* Nested over, sub, sup, from, to. */
66	    (bp->type == EQN_SUBEXPR && bp->pos != EQNPOS_SQRT &&
67	     ((bp->parent->type == EQN_LIST && bp->expectargs == 1) ||
68	      (bp->parent->type == EQN_SUBEXPR &&
69	       bp->pos != EQNPOS_SQRT)))))) {
70		if (bp->parent->type == EQN_SUBEXPR && bp->prev != NULL)
71			p->flags |= TERMP_NOSPACE;
72		term_word(p, bp->left != NULL ? bp->left : "(");
73		p->flags |= TERMP_NOSPACE;
74		delim = 1;
75	} else
76		delim = 0;
77
78	/* Handle Fonts and text. */
79
80	if (bp->font != EQNFONT_NONE)
81		term_fontpush(p, fontmap[(int)bp->font]);
82
83	if (bp->text != NULL)
84		term_word(p, bp->text);
85
86	/* Special box types. */
87
88	if (bp->pos == EQNPOS_SQRT) {
89		term_word(p, "sqrt");
90		if (bp->first != NULL) {
91			p->flags |= TERMP_NOSPACE;
92			eqn_box(p, bp->first);
93		}
94	} else if (bp->type == EQN_SUBEXPR) {
95		child = bp->first;
96		eqn_box(p, child);
97		p->flags |= TERMP_NOSPACE;
98		term_word(p, bp->pos == EQNPOS_OVER ? "/" :
99		    (bp->pos == EQNPOS_SUP ||
100		     bp->pos == EQNPOS_TO) ? "^" : "_");
101		p->flags |= TERMP_NOSPACE;
102		child = child->next;
103		if (child != NULL) {
104			eqn_box(p, child);
105			if (bp->pos == EQNPOS_FROMTO ||
106			    bp->pos == EQNPOS_SUBSUP) {
107				p->flags |= TERMP_NOSPACE;
108				term_word(p, "^");
109				p->flags |= TERMP_NOSPACE;
110				child = child->next;
111				if (child != NULL)
112					eqn_box(p, child);
113			}
114		}
115	} else {
116		child = bp->first;
117		if (bp->type == EQN_MATRIX &&
118		    child != NULL &&
119		    child->type == EQN_LIST &&
120		    child->expectargs > 1)
121			child = child->first;
122		while (child != NULL) {
123			eqn_box(p,
124			    bp->type == EQN_PILE &&
125			    child->type == EQN_LIST &&
126			    child->expectargs > 1 &&
127			    child->args == 1 ?
128			    child->first : child);
129			child = child->next;
130		}
131	}
132
133	/* Handle Fonts and diacritics. */
134
135	if (bp->font != EQNFONT_NONE)
136		term_fontpop(p);
137	if (bp->top != NULL) {
138		p->flags |= TERMP_NOSPACE;
139		term_word(p, bp->top);
140	}
141	if (bp->bottom != NULL) {
142		p->flags |= TERMP_NOSPACE;
143		term_word(p, "_");
144	}
145
146	/* Right delimiter after this box? */
147
148	if (delim) {
149		p->flags |= TERMP_NOSPACE;
150		term_word(p, bp->right != NULL ? bp->right : ")");
151		if (bp->parent->type == EQN_SUBEXPR && bp->next != NULL)
152			p->flags |= TERMP_NOSPACE;
153	}
154}
155