Initial import from FreeBSD RELENG_4:
[games.git] / contrib / groff / src / preproc / eqn / box.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2002 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #include "eqn.h"
22 #include "pbox.h"
23
24 const char *current_roman_font;
25
26 char *gfont = 0;
27 char *grfont = 0;
28 char *gbfont = 0;
29 int gsize = 0;
30
31 int script_size_reduction = -1; // negative means reduce by a percentage 
32
33 int positive_space = -1;
34 int negative_space = -1;
35
36 int minimum_size = 5;
37
38 int fat_offset = 4;
39 int body_height = 85;
40 int body_depth = 35;
41
42 int over_hang = 0;
43 int accent_width = 31;
44 int delimiter_factor = 900;
45 int delimiter_shortfall = 50;
46
47 int null_delimiter_space = 12;
48 int script_space = 5;
49 int thin_space = 17;
50 int medium_space = 22;
51 int thick_space = 28;
52
53 int num1 = 70;
54 int num2 = 40;
55 // we don't use num3, because we don't have \atop
56 int denom1 = 70;
57 int denom2 = 36;
58 int axis_height = 26;           // in 100ths of an em
59 int sup1 = 42;
60 int sup2 = 37;
61 int sup3 = 28;
62 int default_rule_thickness = 4;
63 int sub1 = 20;
64 int sub2 = 23;
65 int sup_drop = 38;
66 int sub_drop = 5;
67 int x_height = 45;
68 int big_op_spacing1 = 11;
69 int big_op_spacing2 = 17;
70 int big_op_spacing3 = 20;
71 int big_op_spacing4 = 60;
72 int big_op_spacing5 = 10;
73
74 // These are for piles and matrices.
75
76 int baseline_sep = 140;         // = num1 + denom1
77 int shift_down = 26;            // = axis_height
78 int column_sep = 100;           // = em space
79 int matrix_side_sep = 17;       // = thin space
80
81 int nroff = 0;                  // should we grok ndefine or tdefine?
82
83 struct {
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 },
125   { 0, 0 }
126 };
127
128 void 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
138 int script_style(int style)
139 {
140   return style > SCRIPT_STYLE ? style - 2 : style;
141 }
142
143 int cramped_style(int style)
144 {
145   return (style & 1) ? style - 1 : style;
146 }
147
148 void 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
159 int 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
185 void set_script_reduction(int n)
186 {
187   script_size_reduction = n;
188 }
189
190 const char *get_gfont()
191 {
192   return gfont ? gfont : "I";
193 }
194
195 const char *get_grfont()
196 {
197   return grfont ? grfont : "R";
198 }
199
200 const char *get_gbfont()
201 {
202   return gbfont ? gbfont : "B";
203 }
204
205 void set_gfont(const char *s)
206 {
207   a_delete gfont;
208   gfont = strsave(s);
209 }
210
211 void set_grfont(const char *s)
212 {
213   a_delete grfont;
214   grfont = strsave(s);
215 }
216
217 void 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
226 void start_string()
227 {
228   printf(".nr " COMPATIBLE_REG " \\n(.C\n");
229   printf(".cp 0\n");
230   printf(".ds " LINE_STRING "\n");
231 }
232
233 void output_string()
234 {
235   printf("\\*(" LINE_STRING "\n");
236 }
237
238 void restore_compatibility()
239 {
240   printf(".cp \\n(" COMPATIBLE_REG "\n");
241 }
242
243 void do_text(const char *s)
244 {
245   printf(".eo\n");
246   printf(".as " LINE_STRING " \"%s\n", s);
247   printf(".ec\n");
248 }
249
250 void set_minimum_size(int n)
251 {
252   minimum_size = n;
253 }
254
255 void 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[.ps]*7+5/10>?%d)\n", minimum_size);
263 }
264
265 int box::next_uid = 0;
266
267 box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++)
268 {
269 }
270
271 box::~box()
272 {
273 }
274
275 void 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[.ps]\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 " .as1 " 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[.ps]'"
315          "\\s0"
316          "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.ps]'"
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(".as1 " 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 " .as1 " 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
347 void 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              ".as1 " LINE_STRING " \\x'-%dM'\n", positive_space);
355     if (negative_space > 0)
356       printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
357              ".as1 " 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 .as1 " 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 .as1 " LINE_STRING
368            " \\x'\\n[" DEPTH_FORMAT
369            "]u-%dM'\n",
370            uid, body_depth, uid, body_depth);
371   }
372 }
373
374 int 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
382 void box::compute_subscript_kern()
383 {
384   printf(".nr " SUB_KERN_FORMAT " 0\n", uid);
385 }
386
387 void box::compute_skew()
388 {
389   printf(".nr " SKEW_FORMAT " 0\n", uid);
390 }
391
392 void box::output()
393 {
394 }
395
396 void box::check_tabs(int)
397 {
398 }
399
400 int box::is_char()
401 {
402   return 0;
403 }
404
405 int box::left_is_italic()
406 {
407   return 0;
408 }
409
410 int box::right_is_italic()
411 {
412   return 0;
413 }
414
415 void box::hint(unsigned)
416 {
417 }
418   
419 void box::handle_char_type(int, int)
420 {
421 }
422
423
424 box_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
434 void 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
446 box_list::~box_list()
447 {
448   for (int i = 0; i < len; i++)
449     delete p[i];
450   a_delete p;
451 }
452
453 void 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
460 pointer_box::pointer_box(box *pp) : p(pp)
461 {
462   spacing_type = p->spacing_type;
463 }
464
465 pointer_box::~pointer_box()
466 {
467   delete p;
468 }
469
470 int 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
479 void 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
485 void 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
492 void pointer_box::check_tabs(int level)
493 {
494   p->check_tabs(level);
495 }
496
497 int 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
509 void simple_box::compute_subscript_kern()
510 {
511   // do nothing, we already computed it in do_metrics
512 }
513
514 void simple_box::compute_skew()
515 {
516   // do nothing, we already computed it in do_metrics
517 }
518
519 int box::is_simple()
520 {
521   return 0;
522 }
523
524 int simple_box::is_simple()
525 {
526   return 1;
527 }
528
529 quoted_text_box::quoted_text_box(char *s) : text(s)
530 {
531 }
532
533 quoted_text_box::~quoted_text_box()
534 {
535   a_delete text;
536 }
537
538 void quoted_text_box::output()
539 {
540   if (text)
541     fputs(text, stdout);
542 }
543
544 tab_box::tab_box() : disabled(0)
545 {
546 }
547
548 // We treat a tab_box as having width 0 for width computations.
549
550 void tab_box::output()
551 {
552   if (!disabled)
553     printf("\\t");
554 }
555
556 void 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
564 space_box::space_box()
565 {
566   spacing_type = SUPPRESS_TYPE;
567 }
568
569 void space_box::output()
570 {
571   printf("\\h'%dM'", thick_space);
572 }
573
574 half_space_box::half_space_box()
575 {
576   spacing_type = SUPPRESS_TYPE;
577 }
578
579 void half_space_box::output()
580 {
581   printf("\\h'%dM'", thin_space);
582 }
583
584 void 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
593 void quoted_text_box::debug_print()
594 {
595   fprintf(stderr, "\"%s\"", (text ? text : ""));
596 }
597
598 void half_space_box::debug_print()
599 {
600   fprintf(stderr, "^");
601 }
602
603 void space_box::debug_print()
604 {
605   fprintf(stderr, "~");
606 }
607
608 void tab_box::debug_print()
609 {
610   fprintf(stderr, "<tab>");
611 }