Merge branch 'vendor/OPENSSH'
[dragonfly.git] / contrib / groff / src / preproc / eqn / box.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2002, 2004, 2007, 2009
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
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 S {
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   if (output_format == troff) {
229     printf(".nr " COMPATIBLE_REG " \\n(.C\n");
230     printf(".cp 0\n");
231     printf(".ds " LINE_STRING "\n");
232   }
233 }
234
235 void output_string()
236 {
237   if (output_format == troff)
238     printf("\\*(" LINE_STRING "\n");
239   else if (output_format == mathml && !xhtml)
240     putchar('\n');
241 }
242
243 void restore_compatibility()
244 {
245   if (output_format == troff)
246     printf(".cp \\n(" COMPATIBLE_REG "\n");
247 }
248
249 void do_text(const char *s)
250 {
251   if (output_format == troff) {
252     printf(".eo\n");
253     printf(".as " LINE_STRING " \"%s\n", s);
254     printf(".ec\n");
255   }
256   else if (output_format == mathml) {
257     fputs(s, stdout);
258     if (xhtml && strlen(s) > 0)
259       printf("\n");
260   }
261 }
262
263 void set_minimum_size(int n)
264 {
265   minimum_size = n;
266 }
267
268 void set_script_size()
269 {
270   if (minimum_size < 0)
271     minimum_size = 0;
272   if (script_size_reduction >= 0)
273     printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size);
274   else
275     printf(".ps (u;\\n[.ps]*7+5/10>?%d)\n", minimum_size);
276 }
277
278 int box::next_uid = 0;
279
280 box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++)
281 {
282 }
283
284 box::~box()
285 {
286 }
287
288 void box::top_level()
289 {
290   box *b = this;
291   if (output_format == troff) {
292     // debug_print();
293     // putc('\n', stderr);
294     printf(".nr " SAVED_FONT_REG " \\n[.f]\n");
295     printf(".ft\n");
296     printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n");
297     printf(".ft %s\n", get_gfont());
298     printf(".nr " SAVED_SIZE_REG " \\n[.ps]\n");
299     if (gsize > 0) {
300       char buf[INT_DIGITS + 1];
301       sprintf(buf, "%d", gsize);
302       b = new size_box(strsave(buf), b);
303     }
304     current_roman_font = get_grfont();
305     // This catches tabs used within \Z (which aren't allowed).
306     b->check_tabs(0);
307     int r = b->compute_metrics(DISPLAY_STYLE);
308     printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n");
309     printf(".ft \\n[" SAVED_FONT_REG "]\n");
310     printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r);
311     if (r == FOUND_MARK) {
312       printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n");
313       printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid);
314     }
315     else if (r == FOUND_LINEUP)
316       printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n["
317              SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n");
318     else
319       assert(r == FOUND_NOTHING);
320     // If we use \R directly, the space will prevent it working in a
321     // macro argument; so we hide it in a string instead.
322     printf(".ds " SAVE_FONT_STRING " "
323            "\\R'" SAVED_INLINE_FONT_REG " \\En[.f]'"
324            "\\fP"
325            "\\R'" SAVED_INLINE_PREV_FONT_REG " \\En[.f]'"
326            "\\R'" SAVED_INLINE_SIZE_REG " \\En[.ps]'"
327            "\\s0"
328            "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\En[.ps]'"
329            "\n"
330            ".ds " RESTORE_FONT_STRING " "
331            "\\f[\\En[" SAVED_INLINE_PREV_FONT_REG "]]"
332            "\\f[\\En[" SAVED_INLINE_FONT_REG "]]"
333            "\\s'\\En[" SAVED_INLINE_PREV_SIZE_REG "]u'"
334            "\\s'\\En[" SAVED_INLINE_SIZE_REG "]u'"
335            "\n");
336     printf(".as1 " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]");
337     printf("\\f[%s]", get_gfont());
338     printf("\\s'\\En[" SAVED_SIZE_REG "]u'");
339     current_roman_font = get_grfont();
340     b->output();
341     printf("\\E*[" RESTORE_FONT_STRING "]\n");
342     if (r == FOUND_LINEUP)
343       printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n["
344              MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n["
345              WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n",
346              b->uid);
347     b->extra_space();
348     if (!inline_flag)
349       printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n["
350              DEPTH_FORMAT "]u-%dM>?0)\n",
351              b->uid, body_height, b->uid, body_depth);
352   }
353   else if (output_format == mathml) {
354     if (xhtml)
355       printf(".MATHML ");
356     printf("<math>");
357     b->output();
358     printf("</math>");
359   }
360   delete b;
361   next_uid = 0;
362 }
363
364 // gpic defines this register so as to make geqn not produce `\x's
365 #define EQN_NO_EXTRA_SPACE_REG "0x"
366
367 void box::extra_space()
368 {
369   printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
370          ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
371   if (positive_space >= 0 || negative_space >= 0) {
372     if (positive_space > 0)
373       printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
374              ".as1 " LINE_STRING " \\x'-%dM'\n", positive_space);
375     if (negative_space > 0)
376       printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
377              ".as1 " LINE_STRING " \\x'%dM'\n", negative_space);
378     positive_space = negative_space = -1;
379   }
380   else {
381     printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
382            ".if \\n[" HEIGHT_FORMAT "]>%dM .as1 " LINE_STRING
383            " \\x'-(\\n[" HEIGHT_FORMAT
384            "]u-%dM)'\n",
385            uid, body_height, uid, body_height);
386     printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
387            ".if \\n[" DEPTH_FORMAT "]>%dM .as1 " LINE_STRING
388            " \\x'\\n[" DEPTH_FORMAT
389            "]u-%dM'\n",
390            uid, body_depth, uid, body_depth);
391   }
392 }
393
394 int box::compute_metrics(int)
395 {
396   printf(".nr " WIDTH_FORMAT " 0\n", uid);
397   printf(".nr " HEIGHT_FORMAT " 0\n", uid);
398   printf(".nr " DEPTH_FORMAT " 0\n", uid);
399   return FOUND_NOTHING;
400 }
401
402 void box::compute_subscript_kern()
403 {
404   printf(".nr " SUB_KERN_FORMAT " 0\n", uid);
405 }
406
407 void box::compute_skew()
408 {
409   printf(".nr " SKEW_FORMAT " 0\n", uid);
410 }
411
412 void box::output()
413 {
414 }
415
416 void box::check_tabs(int)
417 {
418 }
419
420 int box::is_char()
421 {
422   return 0;
423 }
424
425 int box::left_is_italic()
426 {
427   return 0;
428 }
429
430 int box::right_is_italic()
431 {
432   return 0;
433 }
434
435 void box::hint(unsigned)
436 {
437 }
438   
439 void box::handle_char_type(int, int)
440 {
441 }
442
443
444 box_list::box_list(box *pp)
445 {
446   p = new box*[10];
447   for (int i = 0; i < 10; i++)
448     p[i] = 0;
449   maxlen = 10;
450   len = 1;
451   p[0] = pp;
452 }
453
454 void box_list::append(box *pp)
455 {
456   if (len + 1 > maxlen) {
457     box **oldp = p;
458     maxlen *= 2;
459     p = new box*[maxlen];
460     memcpy(p, oldp, sizeof(box*)*len);
461     a_delete oldp;
462   }
463   p[len++] = pp;
464 }
465
466 box_list::~box_list()
467 {
468   for (int i = 0; i < len; i++)
469     delete p[i];
470   a_delete p;
471 }
472
473 void box_list::list_check_tabs(int level)
474 {
475   for (int i = 0; i < len; i++)
476     p[i]->check_tabs(level);
477 }
478
479
480 pointer_box::pointer_box(box *pp) : p(pp)
481 {
482   spacing_type = p->spacing_type;
483 }
484
485 pointer_box::~pointer_box()
486 {
487   delete p;
488 }
489
490 int pointer_box::compute_metrics(int style)
491 {
492   int r = p->compute_metrics(style);
493   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
494   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
495   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
496   return r;
497 }
498
499 void pointer_box::compute_subscript_kern()
500 {
501   p->compute_subscript_kern();
502   printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid);
503 }
504
505 void pointer_box::compute_skew()
506 {
507   p->compute_skew();
508   printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n",
509          uid, p->uid);
510 }
511
512 void pointer_box::check_tabs(int level)
513 {
514   p->check_tabs(level);
515 }
516
517 int simple_box::compute_metrics(int)
518 {
519   printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid);
520   output();
521   printf(DELIMITER_CHAR "\n");
522   printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid);
523   printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid);
524   printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid);
525   printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid);
526   return FOUND_NOTHING;
527 }
528
529 void simple_box::compute_subscript_kern()
530 {
531   // do nothing, we already computed it in do_metrics
532 }
533
534 void simple_box::compute_skew()
535 {
536   // do nothing, we already computed it in do_metrics
537 }
538
539 int box::is_simple()
540 {
541   return 0;
542 }
543
544 int simple_box::is_simple()
545 {
546   return 1;
547 }
548
549 quoted_text_box::quoted_text_box(char *s) : text(s)
550 {
551 }
552
553 quoted_text_box::~quoted_text_box()
554 {
555   a_delete text;
556 }
557
558 void quoted_text_box::output()
559 {
560   if (text) {
561     if (output_format == troff)
562       fputs(text, stdout);
563     else if (output_format == mathml) {
564       fputs("<mtext>", stdout);
565       fputs(text, stdout);
566       fputs("</mtext>", stdout);
567     }
568   }
569 }
570
571 tab_box::tab_box() : disabled(0)
572 {
573 }
574
575 // We treat a tab_box as having width 0 for width computations.
576
577 void tab_box::output()
578 {
579   if (!disabled)
580     printf("\\t");
581 }
582
583 void tab_box::check_tabs(int level)
584 {
585   if (level > 0) {
586     error("tabs allowed only at outermost level");
587     disabled = 1;
588   }
589 }
590
591 space_box::space_box()
592 {
593   spacing_type = SUPPRESS_TYPE;
594 }
595
596 void space_box::output()
597 {
598   if (output_format == troff)
599     printf("\\h'%dM'", thick_space);
600   else if (output_format == mathml)
601     // &ThickSpace; doesn't display right under Firefox 1.5.
602     printf("<mtext>&ensp;</mtext>");
603 }
604
605 half_space_box::half_space_box()
606 {
607   spacing_type = SUPPRESS_TYPE;
608 }
609
610 void half_space_box::output()
611 {
612   if (output_format == troff)
613     printf("\\h'%dM'", thin_space);
614   else if (output_format == mathml)
615     printf("<mtext>&ThinSpace;</mtext>");
616 }
617
618 void box_list::list_debug_print(const char *sep)
619 {
620   p[0]->debug_print();
621   for (int i = 1; i < len; i++) {
622     fprintf(stderr, "%s", sep);
623     p[i]->debug_print();
624   }
625 }
626
627 void quoted_text_box::debug_print()
628 {
629   fprintf(stderr, "\"%s\"", (text ? text : ""));
630 }
631
632 void half_space_box::debug_print()
633 {
634   fprintf(stderr, "^");
635 }
636
637 void space_box::debug_print()
638 {
639   fprintf(stderr, "~");
640 }
641
642 void tab_box::debug_print()
643 {
644   fprintf(stderr, "<tab>");
645 }