xref: /illumos-gate/usr/src/cmd/mandoc/eqn_html.c (revision 9c88ac3ab4edaa5e8c0130ed1b4b376ea57c545a)
1 /*	$Id: eqn_html.c,v 1.17 2017/07/14 13:32:35 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 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 <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "mandoc.h"
29 #include "out.h"
30 #include "html.h"
31 
32 static void
33 eqn_box(struct html *p, const struct eqn_box *bp)
34 {
35 	struct tag	*post, *row, *cell, *t;
36 	const struct eqn_box *child, *parent;
37 	const char	*cp;
38 	size_t		 i, j, rows;
39 	enum htmltag	 tag;
40 	enum eqn_fontt	 font;
41 
42 	if (NULL == bp)
43 		return;
44 
45 	post = NULL;
46 
47 	/*
48 	 * Special handling for a matrix, which is presented to us in
49 	 * column order, but must be printed in row-order.
50 	 */
51 	if (EQN_MATRIX == bp->type) {
52 		if (NULL == bp->first)
53 			goto out;
54 		if (bp->first->type != EQN_LIST ||
55 		    bp->first->expectargs == 1) {
56 			eqn_box(p, bp->first);
57 			goto out;
58 		}
59 		if (NULL == (parent = bp->first->first))
60 			goto out;
61 		/* Estimate the number of rows, first. */
62 		if (NULL == (child = parent->first))
63 			goto out;
64 		for (rows = 0; NULL != child; rows++)
65 			child = child->next;
66 		/* Print row-by-row. */
67 		post = print_otag(p, TAG_MTABLE, "");
68 		for (i = 0; i < rows; i++) {
69 			parent = bp->first->first;
70 			row = print_otag(p, TAG_MTR, "");
71 			while (NULL != parent) {
72 				child = parent->first;
73 				for (j = 0; j < i; j++) {
74 					if (NULL == child)
75 						break;
76 					child = child->next;
77 				}
78 				cell = print_otag(p, TAG_MTD, "");
79 				/*
80 				 * If we have no data for this
81 				 * particular cell, then print a
82 				 * placeholder and continue--don't puke.
83 				 */
84 				if (NULL != child)
85 					eqn_box(p, child->first);
86 				print_tagq(p, cell);
87 				parent = parent->next;
88 			}
89 			print_tagq(p, row);
90 		}
91 		goto out;
92 	}
93 
94 	switch (bp->pos) {
95 	case EQNPOS_TO:
96 		post = print_otag(p, TAG_MOVER, "");
97 		break;
98 	case EQNPOS_SUP:
99 		post = print_otag(p, TAG_MSUP, "");
100 		break;
101 	case EQNPOS_FROM:
102 		post = print_otag(p, TAG_MUNDER, "");
103 		break;
104 	case EQNPOS_SUB:
105 		post = print_otag(p, TAG_MSUB, "");
106 		break;
107 	case EQNPOS_OVER:
108 		post = print_otag(p, TAG_MFRAC, "");
109 		break;
110 	case EQNPOS_FROMTO:
111 		post = print_otag(p, TAG_MUNDEROVER, "");
112 		break;
113 	case EQNPOS_SUBSUP:
114 		post = print_otag(p, TAG_MSUBSUP, "");
115 		break;
116 	case EQNPOS_SQRT:
117 		post = print_otag(p, TAG_MSQRT, "");
118 		break;
119 	default:
120 		break;
121 	}
122 
123 	if (bp->top || bp->bottom) {
124 		assert(NULL == post);
125 		if (bp->top && NULL == bp->bottom)
126 			post = print_otag(p, TAG_MOVER, "");
127 		else if (bp->top && bp->bottom)
128 			post = print_otag(p, TAG_MUNDEROVER, "");
129 		else if (bp->bottom)
130 			post = print_otag(p, TAG_MUNDER, "");
131 	}
132 
133 	if (EQN_PILE == bp->type) {
134 		assert(NULL == post);
135 		if (bp->first != NULL &&
136 		    bp->first->type == EQN_LIST &&
137 		    bp->first->expectargs > 1)
138 			post = print_otag(p, TAG_MTABLE, "");
139 	} else if (bp->type == EQN_LIST && bp->expectargs > 1 &&
140 	    bp->parent && bp->parent->type == EQN_PILE) {
141 		assert(NULL == post);
142 		post = print_otag(p, TAG_MTR, "");
143 		print_otag(p, TAG_MTD, "");
144 	}
145 
146 	if (bp->text != NULL) {
147 		assert(post == NULL);
148 		tag = TAG_MI;
149 		cp = bp->text;
150 		if (isdigit((unsigned char)cp[0]) ||
151 		    (cp[0] == '.' && isdigit((unsigned char)cp[1]))) {
152 			tag = TAG_MN;
153 			while (*++cp != '\0') {
154 				if (*cp != '.' &&
155 				    isdigit((unsigned char)*cp) == 0) {
156 					tag = TAG_MI;
157 					break;
158 				}
159 			}
160 		} else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) {
161 			tag = TAG_MO;
162 			while (*cp != '\0') {
163 				if (cp[0] == '\\' && cp[1] != '\0') {
164 					cp++;
165 					mandoc_escape(&cp, NULL, NULL);
166 				} else if (isalnum((unsigned char)*cp)) {
167 					tag = TAG_MI;
168 					break;
169 				} else
170 					cp++;
171 			}
172 		}
173 		font = bp->font;
174 		if (bp->text[0] != '\0' &&
175 		    (((tag == TAG_MN || tag == TAG_MO) &&
176 		      font == EQNFONT_ROMAN) ||
177 		     (tag == TAG_MI && font == (bp->text[1] == '\0' ?
178 		      EQNFONT_ITALIC : EQNFONT_ROMAN))))
179 			font = EQNFONT_NONE;
180 		switch (font) {
181 		case EQNFONT_NONE:
182 			post = print_otag(p, tag, "");
183 			break;
184 		case EQNFONT_ROMAN:
185 			post = print_otag(p, tag, "?", "fontstyle", "normal");
186 			break;
187 		case EQNFONT_BOLD:
188 		case EQNFONT_FAT:
189 			post = print_otag(p, tag, "?", "fontweight", "bold");
190 			break;
191 		case EQNFONT_ITALIC:
192 			post = print_otag(p, tag, "?", "fontstyle", "italic");
193 			break;
194 		default:
195 			abort();
196 		}
197 		print_text(p, bp->text);
198 	} else if (NULL == post) {
199 		if (NULL != bp->left || NULL != bp->right)
200 			post = print_otag(p, TAG_MFENCED, "??",
201 			    "open", bp->left == NULL ? "" : bp->left,
202 			    "close", bp->right == NULL ? "" : bp->right);
203 		if (NULL == post)
204 			post = print_otag(p, TAG_MROW, "");
205 		else
206 			print_otag(p, TAG_MROW, "");
207 	}
208 
209 	eqn_box(p, bp->first);
210 
211 out:
212 	if (NULL != bp->bottom) {
213 		t = print_otag(p, TAG_MO, "");
214 		print_text(p, bp->bottom);
215 		print_tagq(p, t);
216 	}
217 	if (NULL != bp->top) {
218 		t = print_otag(p, TAG_MO, "");
219 		print_text(p, bp->top);
220 		print_tagq(p, t);
221 	}
222 
223 	if (NULL != post)
224 		print_tagq(p, post);
225 
226 	eqn_box(p, bp->next);
227 }
228 
229 void
230 print_eqn(struct html *p, const struct eqn_box *bp)
231 {
232 	struct tag	*t;
233 
234 	if (bp->first == NULL)
235 		return;
236 
237 	t = print_otag(p, TAG_MATH, "c", "eqn");
238 
239 	p->flags |= HTML_NONOSPACE;
240 	eqn_box(p, bp);
241 	p->flags &= ~HTML_NONOSPACE;
242 
243 	print_tagq(p, t);
244 }
245