Line data Source code
1 : /* Copyright 1989-2023 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include <config.h>
21 : #endif
22 :
23 : #include <stdcountof.h>
24 :
25 : #include <assert.h>
26 :
27 : #include "eqn.h"
28 : #include "pbox.h"
29 :
30 : enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 };
31 :
32 : // Small must be none-zero and must exist in each device.
33 : // Small will be put in the roman font, others are assumed to be
34 : // on the special font (so no font change will be necessary.)
35 :
36 : struct delimiter {
37 : const char *name;
38 : int flags;
39 : const char *small;
40 : const char *chain_format;
41 : const char *ext;
42 : const char *top;
43 : const char *mid;
44 : const char *bot;
45 : } delim_table[] = {
46 : {
47 : "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]",
48 : "\\[parenleftex]",
49 : "\\[parenlefttp]",
50 : 0,
51 : "\\[parenleftbt]",
52 : },
53 : {
54 : ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]",
55 : "\\[parenrightex]",
56 : "\\[parenrighttp]",
57 : 0,
58 : "\\[parenrightbt]",
59 : },
60 : {
61 : "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]",
62 : "\\[bracketleftex]",
63 : "\\[bracketlefttp]",
64 : 0,
65 : "\\[bracketleftbt]",
66 : },
67 : {
68 : "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]",
69 : "\\[bracketrightex]",
70 : "\\[bracketrighttp]",
71 : 0,
72 : "\\[bracketrightbt]",
73 : },
74 : {
75 : "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]",
76 : "\\[braceleftex]",
77 : "\\[bracelefttp]",
78 : "\\[braceleftmid]",
79 : "\\[braceleftbt]",
80 : },
81 : {
82 : "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]",
83 : "\\[bracerightex]",
84 : "\\[bracerighttp]",
85 : "\\[bracerightmid]",
86 : "\\[bracerightbt]",
87 : },
88 : {
89 : "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
90 : "\\[barex]",
91 : 0,
92 : 0,
93 : 0,
94 : },
95 : {
96 : "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]",
97 : "\\[bracketleftex]",
98 : 0,
99 : 0,
100 : "\\[bracketleftbt]",
101 : },
102 : {
103 : "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]",
104 : "\\[bracketrightex]",
105 : 0,
106 : 0,
107 : "\\[bracketrightbt]",
108 : },
109 : {
110 : "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]",
111 : "\\[bracketleftex]",
112 : "\\[bracketlefttp]",
113 : 0,
114 : 0,
115 : },
116 : {
117 : "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]",
118 : "\\[bracketrightex]",
119 : "\\[bracketrighttp]",
120 : 0,
121 : 0,
122 : },
123 : {
124 : "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
125 : "\\[bardblex]",
126 : 0,
127 : 0,
128 : 0,
129 : },
130 : {
131 : "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]",
132 : 0,
133 : 0,
134 : 0,
135 : 0,
136 : },
137 : {
138 : ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]",
139 : 0,
140 : 0,
141 : 0,
142 : 0,
143 : },
144 : {
145 : "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]",
146 : "\\[arrowvertex]",
147 : "\\[arrowverttp]",
148 : 0,
149 : 0,
150 : },
151 : {
152 : "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]",
153 : "\\[arrowvertex]",
154 : 0,
155 : 0,
156 : "\\[arrowvertbt]",
157 : },
158 : {
159 : "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]",
160 : "\\[arrowvertex]",
161 : "\\[arrowverttp]",
162 : 0,
163 : "\\[arrowvertbt]",
164 : },
165 : };
166 :
167 : // C++11: constexpr
168 : const int DELIM_TABLE_SIZE = int(countof(delim_table));
169 :
170 : class delim_box : public box {
171 : private:
172 : char *left;
173 : char *right;
174 : box *p;
175 : public:
176 : delim_box(char *, box *, char *);
177 : ~delim_box();
178 : int compute_metrics(int);
179 : void output();
180 : void diagnose_tab_stop_usage(int);
181 : void debug_print();
182 : };
183 :
184 3 : box *make_delim_box(char *l, box *pp, char *r)
185 : {
186 3 : if (l != 0 && *l == '\0') {
187 0 : delete[] l;
188 0 : l = 0;
189 : }
190 3 : if (r != 0 && *r == '\0') {
191 0 : delete[] r;
192 0 : r = 0;
193 : }
194 3 : return new delim_box(l, pp, r);
195 : }
196 :
197 3 : delim_box::delim_box(char *l, box *pp, char *r)
198 3 : : left(l), right(r), p(pp)
199 : {
200 3 : }
201 :
202 6 : delim_box::~delim_box()
203 : {
204 3 : delete[] left;
205 3 : delete[] right;
206 3 : delete p;
207 6 : }
208 :
209 3 : static void build_extensible(const char *ext, const char *top, const char *mid,
210 : const char *bot)
211 : {
212 3 : assert(ext != 0);
213 3 : printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
214 : ext);
215 3 : printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n");
216 3 : printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n");
217 3 : if (top) {
218 3 : printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
219 : ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
220 : top);
221 3 : printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n");
222 3 : printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n");
223 : }
224 3 : if (mid) {
225 3 : printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
226 : ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
227 : mid);
228 3 : printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n");
229 3 : printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n");
230 : }
231 3 : if (bot) {
232 3 : printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
233 : ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
234 : bot);
235 3 : printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n");
236 3 : printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n");
237 : }
238 3 : printf(".nr " TOTAL_HEIGHT_REG " 0");
239 3 : if (top)
240 3 : printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]");
241 3 : if (bot)
242 3 : printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]");
243 3 : if (mid)
244 3 : printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]");
245 3 : printf("\n");
246 : // determine how many extensible characters we need
247 3 : printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]");
248 3 : if (mid)
249 3 : printf("/2");
250 3 : printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n["
251 : EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n");
252 :
253 3 : printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n["
254 : EXT_DEPTH_REG "]*\\n[" TEMP_REG "]");
255 3 : if (mid)
256 3 : printf("*2");
257 3 : printf(")\n");
258 3 : printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
259 : "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n",
260 : get_param("axis_height"));
261 3 : if (top)
262 3 : printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'"
263 : "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
264 : "\\v'\\n[" TOP_DEPTH_REG "]u'\n",
265 : top);
266 :
267 : // this macro appends $2 copies of $3 to string $1
268 3 : printf(".de " REPEAT_APPEND_STRING_MACRO "\n"
269 : ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n"
270 : "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n"
271 : ".\\}\n"
272 : "..\n");
273 :
274 3 : printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] "
275 : "\\v'\\n[" EXT_HEIGHT_REG "]u'"
276 : "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
277 : "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
278 : ext);
279 :
280 3 : if (mid) {
281 3 : printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'"
282 : "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
283 : "\\v'\\n[" MID_DEPTH_REG "]u'\n",
284 : mid);
285 3 : printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING
286 : " \\n[" TEMP_REG "] "
287 : "\\v'\\n[" EXT_HEIGHT_REG "]u'"
288 : "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
289 : "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
290 : ext);
291 : }
292 3 : if (bot)
293 3 : printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'"
294 : "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
295 : "\\v'\\n[" BOT_DEPTH_REG "]u'\n",
296 : bot);
297 3 : printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n");
298 3 : }
299 :
300 3 : static void define_extensible_string(char *delim, int uid,
301 : left_or_right_t left_or_right)
302 : {
303 3 : printf(".ds " DELIM_STRING "\n");
304 3 : delimiter *d = delim_table;
305 3 : size_t delim_len = strlen(delim);
306 : int i;
307 15 : for (i = 0; i < DELIM_TABLE_SIZE; i++, d++)
308 15 : if (strncmp(delim, d->name, delim_len) == 0
309 3 : && (left_or_right & d->flags) != 0)
310 3 : break;
311 3 : if (i >= DELIM_TABLE_SIZE) {
312 0 : error("there is no '%1' delimiter", delim);
313 0 : printf(".nr " DELIM_WIDTH_REG " 0\n");
314 0 : return;
315 : }
316 :
317 3 : printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
318 : ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
319 : "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
320 : ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n"
321 : ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
322 : "\\{",
323 : current_roman_font, d->small, get_param("axis_height"),
324 : current_roman_font, d->small);
325 :
326 : char buf[256];
327 : // The format string in the sprintf below comes from a struct
328 : // initializer above, and is not subject to external influence.
329 : #pragma GCC diagnostic push
330 : #pragma GCC diagnostic ignored "-Wformat-nonliteral"
331 3 : sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]");
332 : #pragma GCC diagnostic pop
333 3 : printf(".nr " INDEX_REG " 0\n"
334 : ".de " TEMP_MACRO "\n"
335 : ".ie c%s \\{\\\n"
336 : ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n"
337 : ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
338 : "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n"
339 : ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n"
340 : ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
341 : "\\{.nr " INDEX_REG " +1\n"
342 : "." TEMP_MACRO "\n"
343 : ".\\}\\}\n"
344 : ".el .nr " INDEX_REG " 0-1\n"
345 : "..\n"
346 : "." TEMP_MACRO "\n",
347 : buf, buf, get_param("axis_height"), buf);
348 3 : if (d->ext) {
349 3 : printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext);
350 3 : build_extensible(d->ext, d->top, d->mid, d->bot);
351 3 : printf(".\\}\\}\n");
352 : }
353 3 : printf(".\\}\n");
354 3 : printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n");
355 3 : printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid);
356 3 : printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
357 : ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n",
358 : uid, uid, get_param("axis_height"));
359 3 : printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
360 : ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n",
361 : uid, uid, get_param("axis_height"));
362 : }
363 :
364 3 : int delim_box::compute_metrics(int style)
365 : {
366 3 : int r = p->compute_metrics(style);
367 3 : printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
368 3 : printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
369 3 : printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
370 3 : printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM"
371 : ">?(\\n[" DEPTH_FORMAT "]+%dM)\n",
372 3 : p->uid, get_param("axis_height"), p->uid,
373 : get_param("axis_height"));
374 3 : printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500"
375 : ">?(\\n[" DELTA_REG "]*2-%dM)\n",
376 : get_param("delimiter_factor"),
377 : get_param("delimiter_shortfall"));
378 3 : if (left) {
379 3 : define_extensible_string(left, uid, LEFT_DELIM);
380 3 : printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n",
381 3 : uid);
382 3 : if (r)
383 0 : printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n");
384 : }
385 3 : if (right) {
386 0 : define_extensible_string(right, uid, RIGHT_DELIM);
387 0 : printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n",
388 0 : uid);
389 : }
390 3 : return r;
391 : }
392 :
393 3 : void delim_box::output()
394 : {
395 3 : if (output_format == troff) {
396 3 : if (left)
397 3 : printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid);
398 3 : p->output();
399 3 : if (right)
400 0 : printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid);
401 : }
402 0 : else if (output_format == mathml) {
403 0 : printf("<mrow><mo>%s</mo>", left);
404 0 : p->output();
405 0 : printf("<mo>%s</mo></mrow>", right);
406 : }
407 3 : }
408 :
409 3 : void delim_box::diagnose_tab_stop_usage(int level)
410 : {
411 3 : p->diagnose_tab_stop_usage(level);
412 3 : }
413 :
414 0 : void delim_box::debug_print()
415 : {
416 0 : fprintf(stderr, "left \"%s\" { ", left ? left : "");
417 0 : p->debug_print();
418 0 : fprintf(stderr, " }");
419 0 : if (right)
420 0 : fprintf(stderr, " right \"%s\"", right);
421 0 : }
422 :
423 : // Local Variables:
424 : // fill-column: 72
425 : // mode: C++
426 : // End:
427 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|