Updated GNU utilities
[unix-history] / gnu / usr.bin / groff / eqn / box.cc
CommitLineData
8c4ebc23
JH
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file COPYING. If not, write to the Free Software
19Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include "eqn.h"
22#include "pbox.h"
23
24const char *current_roman_font;
25
26char *gfont = 0;
27char *grfont = 0;
28char *gbfont = 0;
29int gsize = 0;
30
31int script_size_reduction = -1; // negative means reduce by a percentage
32
33int positive_space = -1;
34int negative_space = -1;
35
36int minimum_size = 5;
37
38int fat_offset = 4;
39int body_height = 85;
40int body_depth = 35;
41
42int over_hang = 0;
43int accent_width = 31;
44int delimiter_factor = 900;
45int delimiter_shortfall = 50;
46
47int null_delimiter_space = 12;
48int script_space = 5;
49int thin_space = 17;
50int medium_space = 22;
51int thick_space = 28;
52
53int num1 = 70;
54int num2 = 40;
55// we don't use num3, because we don't have \atop
56int denom1 = 70;
57int denom2 = 36;
58int axis_height = 26; // in 100ths of an em
59int sup1 = 42;
60int sup2 = 37;
61int sup3 = 28;
62int default_rule_thickness = 4;
63int sub1 = 20;
64int sub2 = 23;
65int sup_drop = 38;
66int sub_drop = 5;
67int x_height = 45;
68int big_op_spacing1 = 11;
69int big_op_spacing2 = 17;
70int big_op_spacing3 = 20;
71int big_op_spacing4 = 60;
72int big_op_spacing5 = 10;
73
74// These are for piles and matrices.
75
76int baseline_sep = 140; // = num1 + denom1
77int shift_down = 26; // = axis_height
78int column_sep = 100; // = em space
79int matrix_side_sep = 17; // = thin space
80
81int nroff = 0; // should we grok ndefine or tdefine?
82
83struct {
84 const char *name;
85 int *ptr;
86} param_table[] = {
87"fat_offset", &fat_offset,
88"over_hang", &over_hang,
89"accent_width", &accent_width,
90"delimiter_factor", &delimiter_factor,
91"delimiter_shortfall", &delimiter_shortfall,
92"null_delimiter_space", &null_delimiter_space,
93"script_space", &script_space,
94"thin_space", &thin_space,
95"medium_space", &medium_space,
96"thick_space", &thick_space,
97"num1", &num1,
98"num2", &num2,
99"denom1", &denom1,
100"denom2", &denom2,
101"axis_height", &axis_height,
102"sup1", &sup1,
103"sup2", &sup2,
104"sup3", &sup3,
105"default_rule_thickness", &default_rule_thickness,
106"sub1", &sub1,
107"sub2", &sub2,
108"sup_drop", &sup_drop,
109"sub_drop", &sub_drop,
110"x_height", &x_height,
111"big_op_spacing1", &big_op_spacing1,
112"big_op_spacing2", &big_op_spacing2,
113"big_op_spacing3", &big_op_spacing3,
114"big_op_spacing4", &big_op_spacing4,
115"big_op_spacing5", &big_op_spacing5,
116"minimum_size", &minimum_size,
117"baseline_sep", &baseline_sep,
118"shift_down", &shift_down,
119"column_sep", &column_sep,
120"matrix_side_sep", &matrix_side_sep,
121"draw_lines", &draw_flag,
122"body_height", &body_height,
123"body_depth", &body_depth,
124"nroff", &nroff,
1250, 0
126};
127
128void set_param(const char *name, int value)
129{
130 for (int i = 0; param_table[i].name != 0; i++)
131 if (strcmp(param_table[i].name, name) == 0) {
132 *param_table[i].ptr = value;
133 return;
134 }
135 error("unrecognised parameter `%1'", name);
136}
137
138int script_style(int style)
139{
140 return style > SCRIPT_STYLE ? style - 2 : style;
141}
142
143int cramped_style(int style)
144{
145 return (style & 1) ? style - 1 : style;
146}
147
148void set_space(int n)
149{
150 if (n < 0)
151 negative_space = -n;
152 else
153 positive_space = n;
154}
155
156// Return 0 if the specified size is bad.
157// The caller is responsible for giving the error message.
158
159int set_gsize(const char *s)
160{
161 const char *p = (*s == '+' || *s == '-') ? s + 1 : s;
162 char *end;
163 long n = strtol(p, &end, 10);
164 if (n <= 0 || *end != '\0' || n > INT_MAX)
165 return 0;
166 if (p > s) {
167 if (!gsize)
168 gsize = 10;
169 if (*s == '+') {
170 if (gsize > INT_MAX - n)
171 return 0;
172 gsize += int(n);
173 }
174 else {
175 if (gsize - n <= 0)
176 return 0;
177 gsize -= int(n);
178 }
179 }
180 else
181 gsize = int(n);
182 return 1;
183}
184
185void set_script_reduction(int n)
186{
187 script_size_reduction = n;
188}
189
190const char *get_gfont()
191{
192 return gfont ? gfont : "I";
193}
194
195const char *get_grfont()
196{
197 return grfont ? grfont : "R";
198}
199
200const char *get_gbfont()
201{
202 return gbfont ? gbfont : "B";
203}
204
205void set_gfont(const char *s)
206{
207 a_delete gfont;
208 gfont = strsave(s);
209}
210
211void set_grfont(const char *s)
212{
213 a_delete grfont;
214 grfont = strsave(s);
215}
216
217void set_gbfont(const char *s)
218{
219 a_delete gbfont;
220 gbfont = strsave(s);
221}
222
223// this must be precisely 2 characters in length
224#define COMPATIBLE_REG "0C"
225
226void start_string()
227{
228 printf(".nr " COMPATIBLE_REG " \\n(.C\n");
229 printf(".cp 0\n");
230 printf(".ds " LINE_STRING "\n");
231}
232
233void output_string()
234{
235 printf("\\*[" LINE_STRING "]\n");
236}
237
238void restore_compatibility()
239{
240 printf(".cp \\n(" COMPATIBLE_REG "\n");
241}
242
243void do_text(const char *s)
244{
245 printf(".eo\n");
246 printf(".as " LINE_STRING " \"%s\n", s);
247 printf(".ec\n");
248}
249
250void set_minimum_size(int n)
251{
252 minimum_size = n;
253}
254
255void set_script_size()
256{
257 if (minimum_size < 0)
258 minimum_size = 0;
259 if (script_size_reduction >= 0)
260 printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size);
261 else
262 printf(".ps (u;\\n[.s]*7+5/10>?%d)*1z\n", minimum_size);
263}
264
265int box::next_uid = 0;
266
267box::box() : uid(next_uid++), spacing_type(ORDINARY_TYPE)
268{
269}
270
271box::~box()
272{
273}
274
275void box::top_level()
276{
277 // debug_print();
278 // putc('\n', stderr);
279 box *b = this;
280 printf(".nr " SAVED_FONT_REG " \\n[.f]\n");
281 printf(".ft\n");
282 printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n");
283 printf(".ft %s\n", get_gfont());
284 printf(".nr " SAVED_SIZE_REG " \\n[.s]z\n");
285 if (gsize > 0) {
286 char buf[INT_DIGITS + 1];
287 sprintf(buf, "%d", gsize);
288 b = new size_box(strsave(buf), b);
289 }
290 current_roman_font = get_grfont();
291 // This catches tabs used within \Z (which aren't allowed).
292 b->check_tabs(0);
293 int r = b->compute_metrics(DISPLAY_STYLE);
294 printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n");
295 printf(".ft \\n[" SAVED_FONT_REG "]\n");
296 printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r);
297 if (r == FOUND_MARK) {
298 printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n");
299 printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid);
300 }
301 else if (r == FOUND_LINEUP)
302 printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n["
303 SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n");
304 else
305 assert(r == FOUND_NOTHING);
306 // The problem here is that the argument to \f is read in copy mode,
307 // so we cannot use \E there; so we hide it in a string instead.
308 // Another problem is that if we use \R directly, then the space will
309 // prevent it working in a macro argument.
310 printf(".ds " SAVE_FONT_STRING " "
311 "\\R'" SAVED_INLINE_FONT_REG " \\\\n[.f]'"
312 "\\fP"
313 "\\R'" SAVED_INLINE_PREV_FONT_REG " \\\\n[.f]'"
314 "\\R'" SAVED_INLINE_SIZE_REG " \\\\n[.s]z'"
315 "\\s0"
316 "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.s]z'"
317 "\n"
318 ".ds " RESTORE_FONT_STRING " "
319 "\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]"
320 "\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]"
321 "\\s'\\\\n[" SAVED_INLINE_PREV_SIZE_REG "]u'"
322 "\\s'\\\\n[" SAVED_INLINE_SIZE_REG "]u'"
323 "\n");
324 printf(".as " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]");
325 printf("\\f[%s]", get_gfont());
326 printf("\\s'\\En[" SAVED_SIZE_REG "]u'");
327 current_roman_font = get_grfont();
328 b->output();
329 printf("\\E*[" RESTORE_FONT_STRING "]\n");
330 if (r == FOUND_LINEUP)
331 printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n["
332 MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n["
333 WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n",
334 b->uid);
335 b->extra_space();
336 if (!inline_flag)
337 printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n["
338 DEPTH_FORMAT "]u-%dM>?0)\n",
339 b->uid, body_height, b->uid, body_depth);
340 delete b;
341 next_uid = 0;
342}
343
344// gpic defines this register so as to make geqn not produce `\x's
345#define EQN_NO_EXTRA_SPACE_REG "0x"
346
347void box::extra_space()
348{
349 printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
350 ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
351 if (positive_space >= 0 || negative_space >= 0) {
352 if (positive_space > 0)
353 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
354 ".as " LINE_STRING " \\x'-%dM'\n", positive_space);
355 if (negative_space > 0)
356 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
357 ".as " LINE_STRING " \\x'%dM'\n", negative_space);
358 positive_space = negative_space = -1;
359 }
360 else {
361 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
362 ".if \\n[" HEIGHT_FORMAT "]>%dM .as " LINE_STRING
363 " \\x'-(\\n[" HEIGHT_FORMAT
364 "]u-%dM)'\n",
365 uid, body_height, uid, body_height);
366 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
367 ".if \\n[" DEPTH_FORMAT "]>%dM .as " LINE_STRING
368 " \\x'\\n[" DEPTH_FORMAT
369 "]u-%dM'\n",
370 uid, body_depth, uid, body_depth);
371 }
372}
373
374int box::compute_metrics(int)
375{
376 printf(".nr " WIDTH_FORMAT " 0\n", uid);
377 printf(".nr " HEIGHT_FORMAT " 0\n", uid);
378 printf(".nr " DEPTH_FORMAT " 0\n", uid);
379 return FOUND_NOTHING;
380}
381
382void box::compute_subscript_kern()
383{
384 printf(".nr " SUB_KERN_FORMAT " 0\n", uid);
385}
386
387void box::compute_skew()
388{
389 printf(".nr " SKEW_FORMAT " 0\n", uid);
390}
391
392void box::output()
393{
394}
395
396void box::check_tabs(int)
397{
398}
399
400int box::is_char()
401{
402 return 0;
403}
404
405int box::left_is_italic()
406{
407 return 0;
408}
409
410int box::right_is_italic()
411{
412 return 0;
413}
414
415void box::hint(unsigned)
416{
417}
418
419void box::handle_char_type(int, int)
420{
421}
422
423
424box_list::box_list(box *pp)
425{
426 p = new box*[10];
427 for (int i = 0; i < 10; i++)
428 p[i] = 0;
429 maxlen = 10;
430 len = 1;
431 p[0] = pp;
432}
433
434void box_list::append(box *pp)
435{
436 if (len + 1 > maxlen) {
437 box **oldp = p;
438 maxlen *= 2;
439 p = new box*[maxlen];
440 memcpy(p, oldp, sizeof(box*)*len);
441 a_delete oldp;
442 }
443 p[len++] = pp;
444}
445
446box_list::~box_list()
447{
448 for (int i = 0; i < len; i++)
449 delete p[i];
450 a_delete p;
451}
452
453void box_list::list_check_tabs(int level)
454{
455 for (int i = 0; i < len; i++)
456 p[i]->check_tabs(level);
457}
458
459
460pointer_box::pointer_box(box *pp) : p(pp)
461{
462 spacing_type = p->spacing_type;
463}
464
465pointer_box::~pointer_box()
466{
467 delete p;
468}
469
470int pointer_box::compute_metrics(int style)
471{
472 int r = p->compute_metrics(style);
473 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
474 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
475 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
476 return r;
477}
478
479void pointer_box::compute_subscript_kern()
480{
481 p->compute_subscript_kern();
482 printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid);
483}
484
485void pointer_box::compute_skew()
486{
487 p->compute_skew();
488 printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n",
489 uid, p->uid);
490}
491
492void pointer_box::check_tabs(int level)
493{
494 p->check_tabs(level);
495}
496
497int simple_box::compute_metrics(int)
498{
499 printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid);
500 output();
501 printf(DELIMITER_CHAR "\n");
502 printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid);
503 printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid);
504 printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid);
505 printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid);
506 return FOUND_NOTHING;
507}
508
509void simple_box::compute_subscript_kern()
510{
511 // do nothing, we already computed it in do_metrics
512}
513
514void simple_box::compute_skew()
515{
516 // do nothing, we already computed it in do_metrics
517}
518
519int box::is_simple()
520{
521 return 0;
522}
523
524int simple_box::is_simple()
525{
526 return 1;
527}
528
529quoted_text_box::quoted_text_box(char *s) : text(s)
530{
531}
532
533quoted_text_box::~quoted_text_box()
534{
535 a_delete text;
536}
537
538void quoted_text_box::output()
539{
540 if (text)
541 fputs(text, stdout);
542}
543
544tab_box::tab_box() : disabled(0)
545{
546}
547
548// We treat a tab_box as having width 0 for width computations.
549
550void tab_box::output()
551{
552 if (!disabled)
553 printf("\\t");
554}
555
556void tab_box::check_tabs(int level)
557{
558 if (level > 0) {
559 error("tabs allowed only at outermost level");
560 disabled = 1;
561 }
562}
563
564space_box::space_box()
565{
566 spacing_type = SUPPRESS_TYPE;
567}
568
569void space_box::output()
570{
571 printf("\\h'%dM'", thick_space);
572}
573
574half_space_box::half_space_box()
575{
576 spacing_type = SUPPRESS_TYPE;
577}
578
579void half_space_box::output()
580{
581 printf("\\h'%dM'", thin_space);
582}
583
584void box_list::list_debug_print(const char *sep)
585{
586 p[0]->debug_print();
587 for (int i = 1; i < len; i++) {
588 fprintf(stderr, "%s", sep);
589 p[i]->debug_print();
590 }
591}
592
593void quoted_text_box::debug_print()
594{
595 fprintf(stderr, "\"%s\"", (text ? text : ""));
596}
597
598void half_space_box::debug_print()
599{
600 fprintf(stderr, "^");
601}
602
603void space_box::debug_print()
604{
605 fprintf(stderr, "~");
606}
607
608void tab_box::debug_print()
609{
610 fprintf(stderr, "<tab>");
611}