2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004, 2007, 2008,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
25 #define BAR_HEIGHT ".25m"
26 #define DOUBLE_LINE_SEP "2p"
27 #define HALF_DOUBLE_LINE_SEP "1p"
29 #define BODY_DEPTH ".25m"
31 const int DEFAULT_COLUMN_SEPARATION
= 3;
33 #define DELIMITER_CHAR "\\[tbl]"
34 #define SEPARATION_FACTOR_REG PREFIX "sep"
35 #define BOTTOM_REG PREFIX "bot"
36 #define RESET_MACRO_NAME PREFIX "init"
37 #define LINESIZE_REG PREFIX "lps"
38 #define TOP_REG PREFIX "top"
39 #define CURRENT_ROW_REG PREFIX "crow"
40 #define LAST_PASSED_ROW_REG PREFIX "passed"
41 #define TRANSPARENT_STRING_NAME PREFIX "trans"
42 #define QUOTE_STRING_NAME PREFIX "quote"
43 #define SECTION_DIVERSION_NAME PREFIX "section"
44 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
45 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
46 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
47 #define KEEP_MACRO_NAME PREFIX "keep"
48 #define RELEASE_MACRO_NAME PREFIX "release"
49 #define SAVED_FONT_REG PREFIX "fnt"
50 #define SAVED_SIZE_REG PREFIX "sz"
51 #define SAVED_FILL_REG PREFIX "fll"
52 #define SAVED_INDENT_REG PREFIX "ind"
53 #define SAVED_CENTER_REG PREFIX "cent"
54 #define TABLE_DIVERSION_NAME PREFIX "table"
55 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
56 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
57 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
58 #define NEEDED_REG PREFIX "needed"
59 #define REPEATED_MARK_MACRO PREFIX "rmk"
60 #define REPEATED_VPT_MACRO PREFIX "rvpt"
61 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
62 #define SAVED_DN_REG PREFIX "dn"
64 // this must be one character
65 #define COMPATIBLE_REG PREFIX "c"
67 #define EXPAND_REG PREFIX "expand"
69 #define LEADER_REG PREFIX LEADER
71 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
72 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
73 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
74 #define SPAN_WIDTH_PREFIX PREFIX "w"
75 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
76 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
77 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
78 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
79 #define ROW_START_PREFIX PREFIX "rs"
80 #define COLUMN_START_PREFIX PREFIX "cl"
81 #define COLUMN_END_PREFIX PREFIX "ce"
82 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
83 #define ROW_TOP_PREFIX PREFIX "rt"
85 string
block_width_reg(int, int);
86 string
block_diversion_name(int, int);
87 string
block_height_reg(int, int);
88 string
span_width_reg(int, int);
89 string
span_left_numeric_width_reg(int, int);
90 string
span_right_numeric_width_reg(int, int);
91 string
span_alphabetic_width_reg(int, int);
92 string
column_separation_reg(int);
93 string
row_start_reg(int);
94 string
column_start_reg(int);
95 string
column_end_reg(int);
96 string
column_divide_reg(int);
97 string
row_top_reg(int);
99 void set_inline_modifier(const entry_modifier
*);
100 void restore_inline_modifier(const entry_modifier
*);
101 void set_modifier(const entry_modifier
*);
102 int find_decimal_point(const char *, char, const char *);
104 string an_empty_string
;
105 int location_force_filename
= 0;
107 void printfs(const char *,
108 const string
&arg1
= an_empty_string
,
109 const string
&arg2
= an_empty_string
,
110 const string
&arg3
= an_empty_string
,
111 const string
&arg4
= an_empty_string
,
112 const string
&arg5
= an_empty_string
);
114 void prints(const string
&);
116 inline void prints(char c
)
121 inline void prints(const char *s
)
126 void prints(const string
&s
)
129 fwrite(s
.contents(), 1, s
.length(), stdout
);
132 struct horizontal_span
{
133 horizontal_span
*next
;
136 horizontal_span(int, int, horizontal_span
*);
139 class single_line_entry
;
140 class double_line_entry
;
147 const char *input_filename
;
154 const entry_modifier
*mod
;
157 table_entry(const table
*, const entry_modifier
*);
158 virtual ~table_entry();
159 virtual int divert(int, const string
*, int *, int);
160 virtual void do_width();
161 virtual void do_depth();
162 virtual void print() = 0;
163 virtual void position_vertically() = 0;
164 virtual single_line_entry
*to_single_line_entry();
165 virtual double_line_entry
*to_double_line_entry();
166 virtual simple_entry
*to_simple_entry();
167 virtual int line_type();
168 virtual void note_double_vrule_on_right(int);
169 virtual void note_double_vrule_on_left(int);
172 class simple_entry
: public table_entry
{
174 simple_entry(const table
*, const entry_modifier
*);
176 void position_vertically();
177 simple_entry
*to_simple_entry();
178 virtual void add_tab();
179 virtual void simple_print(int);
182 class empty_entry
: public simple_entry
{
184 empty_entry(const table
*, const entry_modifier
*);
188 class text_entry
: public simple_entry
{
191 void print_contents();
193 text_entry(const table
*, const entry_modifier
*, char *);
197 void text_entry::print_contents()
199 set_inline_modifier(mod
);
201 restore_inline_modifier(mod
);
204 class repeated_char_entry
: public text_entry
{
206 repeated_char_entry(const table
*, const entry_modifier
*, char *);
207 void simple_print(int);
210 class simple_text_entry
: public text_entry
{
212 simple_text_entry(const table
*, const entry_modifier
*, char *);
216 class left_text_entry
: public simple_text_entry
{
218 left_text_entry(const table
*, const entry_modifier
*, char *);
219 void simple_print(int);
223 class right_text_entry
: public simple_text_entry
{
225 right_text_entry(const table
*, const entry_modifier
*, char *);
226 void simple_print(int);
230 class center_text_entry
: public simple_text_entry
{
232 center_text_entry(const table
*, const entry_modifier
*, char *);
233 void simple_print(int);
237 class numeric_text_entry
: public text_entry
{
240 numeric_text_entry(const table
*, const entry_modifier
*, char *, int);
242 void simple_print(int);
245 class alphabetic_text_entry
: public text_entry
{
247 alphabetic_text_entry(const table
*, const entry_modifier
*, char *);
249 void simple_print(int);
253 class line_entry
: public simple_entry
{
255 char double_vrule_on_right
;
256 char double_vrule_on_left
;
258 line_entry(const table
*, const entry_modifier
*);
259 void note_double_vrule_on_right(int);
260 void note_double_vrule_on_left(int);
261 void simple_print(int) = 0;
264 class single_line_entry
: public line_entry
{
266 single_line_entry(const table
*, const entry_modifier
*);
267 void simple_print(int);
268 single_line_entry
*to_single_line_entry();
272 class double_line_entry
: public line_entry
{
274 double_line_entry(const table
*, const entry_modifier
*);
275 void simple_print(int);
276 double_line_entry
*to_double_line_entry();
280 class short_line_entry
: public simple_entry
{
282 short_line_entry(const table
*, const entry_modifier
*);
283 void simple_print(int);
287 class short_double_line_entry
: public simple_entry
{
289 short_double_line_entry(const table
*, const entry_modifier
*);
290 void simple_print(int);
294 class block_entry
: public table_entry
{
297 void do_divert(int, int, const string
*, int *, int);
299 block_entry(const table
*, const entry_modifier
*, char *);
301 int divert(int, const string
*, int *, int);
303 void position_vertically();
307 class left_block_entry
: public block_entry
{
309 left_block_entry(const table
*, const entry_modifier
*, char *);
313 class right_block_entry
: public block_entry
{
315 right_block_entry(const table
*, const entry_modifier
*, char *);
319 class center_block_entry
: public block_entry
{
321 center_block_entry(const table
*, const entry_modifier
*, char *);
325 class alphabetic_block_entry
: public block_entry
{
327 alphabetic_block_entry(const table
*, const entry_modifier
*, char *);
329 int divert(int, const string
*, int *, int);
332 table_entry::table_entry(const table
*p
, const entry_modifier
*m
)
333 : next(0), input_lineno(-1), input_filename(0),
334 start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p
), mod(m
)
338 table_entry::~table_entry()
342 int table_entry::divert(int, const string
*, int *, int)
347 void table_entry::do_width()
351 single_line_entry
*table_entry::to_single_line_entry()
356 double_line_entry
*table_entry::to_double_line_entry()
361 simple_entry
*table_entry::to_simple_entry()
366 void table_entry::do_depth()
370 void table_entry::set_location()
372 set_troff_location(input_filename
, input_lineno
);
375 int table_entry::line_type()
380 void table_entry::note_double_vrule_on_right(int)
384 void table_entry::note_double_vrule_on_left(int)
388 simple_entry::simple_entry(const table
*p
, const entry_modifier
*m
)
393 void simple_entry::add_tab()
398 void simple_entry::simple_print(int)
403 void simple_entry::position_vertically()
405 if (start_row
!= end_row
)
406 switch (mod
->vertical_alignment
) {
407 case entry_modifier::TOP
:
408 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
410 case entry_modifier::CENTER
:
411 // Peform the motion in two stages so that the center is rounded
412 // vertically upwards even if net vertical motion is upwards.
413 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
414 printfs(".sp \\n[" BOTTOM_REG
"]u-\\n[%1]u-1v/2u\n",
415 row_start_reg(start_row
));
417 case entry_modifier::BOTTOM
:
418 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG
"]u-\\n[%1]u-1v\n",
419 row_start_reg(start_row
));
426 void simple_entry::print()
437 simple_entry
*simple_entry::to_simple_entry()
442 empty_entry::empty_entry(const table
*p
, const entry_modifier
*m
)
447 int empty_entry::line_type()
452 text_entry::text_entry(const table
*p
, const entry_modifier
*m
, char *s
)
453 : simple_entry(p
, m
), contents(s
)
457 text_entry::~text_entry()
462 repeated_char_entry::repeated_char_entry(const table
*p
,
463 const entry_modifier
*m
, char *s
)
464 : text_entry(p
, m
, s
)
468 void repeated_char_entry::simple_print(int)
470 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
471 set_inline_modifier(mod
);
472 printfs("\\l" DELIMITER_CHAR
"\\n[%1]u\\&",
473 span_width_reg(start_col
, end_col
));
475 prints(DELIMITER_CHAR
);
476 restore_inline_modifier(mod
);
479 simple_text_entry::simple_text_entry(const table
*p
,
480 const entry_modifier
*m
, char *s
)
481 : text_entry(p
, m
, s
)
485 void simple_text_entry::do_width()
488 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
489 span_width_reg(start_col
, end_col
));
491 prints(DELIMITER_CHAR
"\n");
494 left_text_entry::left_text_entry(const table
*p
,
495 const entry_modifier
*m
, char *s
)
496 : simple_text_entry(p
, m
, s
)
500 void left_text_entry::simple_print(int)
502 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
506 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
508 void left_text_entry::add_tab()
510 printfs(" \\n[%1]u", column_end_reg(end_col
));
513 right_text_entry::right_text_entry(const table
*p
,
514 const entry_modifier
*m
, char *s
)
515 : simple_text_entry(p
, m
, s
)
519 void right_text_entry::simple_print(int)
521 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
527 void right_text_entry::add_tab()
529 printfs(" \\n[%1]u", column_end_reg(end_col
));
532 center_text_entry::center_text_entry(const table
*p
,
533 const entry_modifier
*m
, char *s
)
534 : simple_text_entry(p
, m
, s
)
538 void center_text_entry::simple_print(int)
540 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
546 void center_text_entry::add_tab()
548 printfs(" \\n[%1]u", column_end_reg(end_col
));
551 numeric_text_entry::numeric_text_entry(const table
*p
,
552 const entry_modifier
*m
,
554 : text_entry(p
, m
, s
), dot_pos(pos
)
558 void numeric_text_entry::do_width()
562 printfs(".nr %1 0\\w" DELIMITER_CHAR
,
563 block_width_reg(start_row
, start_col
));
564 set_inline_modifier(mod
);
565 for (int i
= 0; i
< dot_pos
; i
++)
567 restore_inline_modifier(mod
);
568 prints(DELIMITER_CHAR
"\n");
569 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
570 span_left_numeric_width_reg(start_col
, end_col
),
571 block_width_reg(start_row
, start_col
));
574 printfs(".nr %1 0\n", block_width_reg(start_row
, start_col
));
575 if (contents
[dot_pos
] != '\0') {
577 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
578 span_right_numeric_width_reg(start_col
, end_col
));
579 set_inline_modifier(mod
);
580 prints(contents
+ dot_pos
);
581 restore_inline_modifier(mod
);
582 prints(DELIMITER_CHAR
"\n");
586 void numeric_text_entry::simple_print(int)
588 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
589 span_width_reg(start_col
, end_col
),
590 span_left_numeric_width_reg(start_col
, end_col
),
591 span_right_numeric_width_reg(start_col
, end_col
),
592 column_start_reg(start_col
),
593 block_width_reg(start_row
, start_col
));
597 alphabetic_text_entry::alphabetic_text_entry(const table
*p
,
598 const entry_modifier
*m
,
600 : text_entry(p
, m
, s
)
604 void alphabetic_text_entry::do_width()
607 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
608 span_alphabetic_width_reg(start_col
, end_col
));
610 prints(DELIMITER_CHAR
"\n");
613 void alphabetic_text_entry::simple_print(int)
615 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
616 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
617 span_width_reg(start_col
, end_col
),
618 span_alphabetic_width_reg(start_col
, end_col
));
622 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
624 void alphabetic_text_entry::add_tab()
626 printfs(" \\n[%1]u", column_end_reg(end_col
));
629 block_entry::block_entry(const table
*p
, const entry_modifier
*m
, char *s
)
630 : table_entry(p
, m
), contents(s
)
634 block_entry::~block_entry()
639 void block_entry::position_vertically()
641 if (start_row
!= end_row
)
642 switch(mod
->vertical_alignment
) {
643 case entry_modifier::TOP
:
644 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
646 case entry_modifier::CENTER
:
647 // Peform the motion in two stages so that the center is rounded
648 // vertically upwards even if net vertical motion is upwards.
649 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
650 printfs(".sp \\n[" BOTTOM_REG
"]u-\\n[%1]u-\\n[%2]u/2u\n",
651 row_start_reg(start_row
),
652 block_height_reg(start_row
, start_col
));
654 case entry_modifier::BOTTOM
:
655 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG
"]u-\\n[%1]u-\\n[%2]u\n",
656 row_start_reg(start_row
),
657 block_height_reg(start_row
, start_col
));
663 prints(".sp -.5v\n");
666 int block_entry::divert(int ncols
, const string
*mw
, int *sep
, int do_expand
)
668 do_divert(0, ncols
, mw
, sep
, do_expand
);
672 void block_entry::do_divert(int alphabetic
, int ncols
, const string
*mw
,
673 int *sep
, int do_expand
)
676 for (i
= start_col
; i
<= end_col
; i
++)
677 if (parent
->expand
[i
])
687 printfs(".di %1\n", block_diversion_name(start_row
, start_col
));
688 prints(".if \\n[" SAVED_FILL_REG
"] .fi\n"
691 for (i
= start_col
; i
<= end_col
; i
++)
692 if (mw
[i
].empty() && !parent
->expand
[i
])
695 // Every column spanned by this entry has a minimum width.
696 for (int j
= start_col
; j
<= end_col
; j
++) {
699 printfs("+%1n", as_string(sep
[j
- 1]));
702 if (parent
->expand
[j
])
703 prints("\\n[" EXPAND_REG
"]u");
705 printfs("(n;%1)", mw
[j
]);
707 printfs(">?\\n[%1]u", span_width_reg(start_col
, end_col
));
710 // Assign each column with a block entry 1/(n+1) of the line
711 // width, where n is the column count.
712 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
713 span_width_reg(start_col
, end_col
),
714 as_string(end_col
- start_col
+ 1),
715 as_string(ncols
+ 1));
719 prints(".cp \\n(" COMPATIBLE_REG
"\n");
723 prints(".br\n.di\n.cp 0\n");
724 if (!mod
->zero_width
) {
726 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
727 span_width_reg(start_col
, end_col
));
728 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
729 span_alphabetic_width_reg(start_col
, end_col
));
732 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
733 span_width_reg(start_col
, end_col
));
735 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row
, start_col
));
736 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row
, start_col
));
737 prints("." RESET_MACRO_NAME
"\n"
738 ".in \\n[" SAVED_INDENT_REG
"]u\n"
740 // the block might have contained .lf commands
741 location_force_filename
= 1;
744 void block_entry::do_depth()
746 printfs(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?(\\n[%1]+\\n[%2])\n",
747 row_start_reg(start_row
),
748 block_height_reg(start_row
, start_col
));
751 left_block_entry::left_block_entry(const table
*p
,
752 const entry_modifier
*m
, char *s
)
753 : block_entry(p
, m
, s
)
757 void left_block_entry::print()
759 printfs(".in +\\n[%1]u\n", column_start_reg(start_col
));
760 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
764 right_block_entry::right_block_entry(const table
*p
,
765 const entry_modifier
*m
, char *s
)
766 : block_entry(p
, m
, s
)
770 void right_block_entry::print()
772 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
773 column_start_reg(start_col
),
774 span_width_reg(start_col
, end_col
),
775 block_width_reg(start_row
, start_col
));
776 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
780 center_block_entry::center_block_entry(const table
*p
,
781 const entry_modifier
*m
, char *s
)
782 : block_entry(p
, m
, s
)
786 void center_block_entry::print()
788 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
789 column_start_reg(start_col
),
790 span_width_reg(start_col
, end_col
),
791 block_width_reg(start_row
, start_col
));
792 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
796 alphabetic_block_entry::alphabetic_block_entry(const table
*p
,
797 const entry_modifier
*m
,
799 : block_entry(p
, m
, s
)
803 int alphabetic_block_entry::divert(int ncols
, const string
*mw
, int *sep
,
806 do_divert(1, ncols
, mw
, sep
, do_expand
);
810 void alphabetic_block_entry::print()
812 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
813 column_start_reg(start_col
),
814 span_width_reg(start_col
, end_col
),
815 span_alphabetic_width_reg(start_col
, end_col
));
816 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
820 line_entry::line_entry(const table
*p
, const entry_modifier
*m
)
821 : simple_entry(p
, m
), double_vrule_on_right(0), double_vrule_on_left(0)
825 void line_entry::note_double_vrule_on_right(int is_corner
)
827 double_vrule_on_right
= is_corner
? 1 : 2;
830 void line_entry::note_double_vrule_on_left(int is_corner
)
832 double_vrule_on_left
= is_corner
? 1 : 2;
835 single_line_entry::single_line_entry(const table
*p
, const entry_modifier
*m
)
840 int single_line_entry::line_type()
845 void single_line_entry::simple_print(int dont_move
)
847 printfs("\\h'|\\n[%1]u",
848 column_divide_reg(start_col
));
849 if (double_vrule_on_left
) {
850 prints(double_vrule_on_left
== 1 ? "-" : "+");
851 prints(HALF_DOUBLE_LINE_SEP
);
855 prints("\\v'-" BAR_HEIGHT
"'");
856 printfs("\\s[\\n[" LINESIZE_REG
"]]" "\\D'l |\\n[%1]u",
857 column_divide_reg(end_col
+1));
858 if (double_vrule_on_right
) {
859 prints(double_vrule_on_left
== 1 ? "+" : "-");
860 prints(HALF_DOUBLE_LINE_SEP
);
864 prints("\\v'" BAR_HEIGHT
"'");
867 single_line_entry
*single_line_entry::to_single_line_entry()
872 double_line_entry::double_line_entry(const table
*p
, const entry_modifier
*m
)
877 int double_line_entry::line_type()
882 void double_line_entry::simple_print(int dont_move
)
885 prints("\\v'-" BAR_HEIGHT
"'");
886 printfs("\\h'|\\n[%1]u",
887 column_divide_reg(start_col
));
888 if (double_vrule_on_left
) {
889 prints(double_vrule_on_left
== 1 ? "-" : "+");
890 prints(HALF_DOUBLE_LINE_SEP
);
893 printfs("\\v'-" HALF_DOUBLE_LINE_SEP
"'"
894 "\\s[\\n[" LINESIZE_REG
"]]"
896 column_divide_reg(end_col
+1));
897 if (double_vrule_on_right
)
898 prints("-" HALF_DOUBLE_LINE_SEP
);
900 printfs("\\v'" DOUBLE_LINE_SEP
"'"
902 column_divide_reg(start_col
));
903 if (double_vrule_on_right
) {
904 prints(double_vrule_on_left
== 1 ? "+" : "-");
905 prints(HALF_DOUBLE_LINE_SEP
);
909 "\\v'-" HALF_DOUBLE_LINE_SEP
"'");
911 prints("\\v'" BAR_HEIGHT
"'");
914 double_line_entry
*double_line_entry::to_double_line_entry()
919 short_line_entry::short_line_entry(const table
*p
, const entry_modifier
*m
)
924 int short_line_entry::line_type()
929 void short_line_entry::simple_print(int dont_move
)
934 prints("\\v'-" BAR_HEIGHT
"'");
935 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
936 printfs("\\s[\\n[" LINESIZE_REG
"]]"
939 span_width_reg(start_col
, end_col
));
941 prints("\\v'" BAR_HEIGHT
"'");
946 short_double_line_entry::short_double_line_entry(const table
*p
,
947 const entry_modifier
*m
)
952 int short_double_line_entry::line_type()
957 void short_double_line_entry::simple_print(int dont_move
)
962 prints("\\v'-" BAR_HEIGHT
"'");
963 printfs("\\h'|\\n[%2]u'"
964 "\\v'-" HALF_DOUBLE_LINE_SEP
"'"
965 "\\s[\\n[" LINESIZE_REG
"]]"
967 "\\v'" DOUBLE_LINE_SEP
"'"
970 "\\v'-" HALF_DOUBLE_LINE_SEP
"'",
971 span_width_reg(start_col
, end_col
),
972 column_start_reg(start_col
));
974 prints("\\v'" BAR_HEIGHT
"'");
979 void set_modifier(const entry_modifier
*m
)
981 if (!m
->font
.empty())
982 printfs(".ft %1\n", m
->font
);
983 if (m
->point_size
.val
!= 0) {
985 if (m
->point_size
.inc
> 0)
987 else if (m
->point_size
.inc
< 0)
989 printfs("%1\n", as_string(m
->point_size
.val
));
991 if (m
->vertical_spacing
.val
!= 0) {
993 if (m
->vertical_spacing
.inc
> 0)
995 else if (m
->vertical_spacing
.inc
< 0)
997 printfs("%1\n", as_string(m
->vertical_spacing
.val
));
999 if (!m
->macro
.empty())
1000 printfs(".%1\n", m
->macro
);
1003 void set_inline_modifier(const entry_modifier
*m
)
1005 if (!m
->font
.empty())
1006 printfs("\\f[%1]", m
->font
);
1007 if (m
->point_size
.val
!= 0) {
1009 if (m
->point_size
.inc
> 0)
1011 else if (m
->point_size
.inc
< 0)
1013 printfs("%1]", as_string(m
->point_size
.val
));
1016 prints("\\v'-.5v'");
1019 void restore_inline_modifier(const entry_modifier
*m
)
1021 if (!m
->font
.empty())
1022 prints("\\f[\\n[" SAVED_FONT_REG
"]]");
1023 if (m
->point_size
.val
!= 0)
1024 prints("\\s[\\n[" SAVED_SIZE_REG
"]]");
1031 int row
; // occurs before row `row'
1032 char printed
; // has it been printed?
1035 virtual void print(table
*) = 0;
1037 virtual int is_single_line() { return 0; };
1038 virtual int is_double_line() { return 0; };
1041 stuff::stuff(int r
) : next(0), row(r
), printed(0)
1049 struct text_stuff
: public stuff
{
1051 const char *filename
;
1054 text_stuff(const string
&, int, const char *, int);
1056 void print(table
*);
1059 text_stuff::text_stuff(const string
&s
, int r
, const char *fn
, int ln
)
1060 : stuff(r
), contents(s
), filename(fn
), lineno(ln
)
1064 text_stuff::~text_stuff()
1068 void text_stuff::print(table
*)
1071 prints(".cp \\n(" COMPATIBLE_REG
"\n");
1072 set_troff_location(filename
, lineno
);
1075 location_force_filename
= 1; // it might have been a .lf command
1078 struct single_hline_stuff
: public stuff
{
1079 single_hline_stuff(int);
1080 void print(table
*);
1081 int is_single_line();
1084 single_hline_stuff::single_hline_stuff(int r
) : stuff(r
)
1088 void single_hline_stuff::print(table
*tbl
)
1091 tbl
->print_single_hline(row
);
1094 int single_hline_stuff::is_single_line()
1099 struct double_hline_stuff
: stuff
{
1100 double_hline_stuff(int);
1101 void print(table
*);
1102 int is_double_line();
1105 double_hline_stuff::double_hline_stuff(int r
) : stuff(r
)
1109 void double_hline_stuff::print(table
*tbl
)
1112 tbl
->print_double_hline(row
);
1115 int double_hline_stuff::is_double_line()
1120 struct vertical_rule
{
1121 vertical_rule
*next
;
1129 vertical_rule(int, int, int, int, vertical_rule
*);
1131 void contribute_to_bottom_macro(table
*);
1135 vertical_rule::vertical_rule(int sr
, int er
, int c
, int dbl
,
1137 : next(p
), start_row(sr
), end_row(er
), col(c
), is_double(dbl
)
1141 vertical_rule::~vertical_rule()
1145 void vertical_rule::contribute_to_bottom_macro(table
*tbl
)
1147 printfs(".if \\n[" CURRENT_ROW_REG
"]>=%1",
1148 as_string(start_row
));
1149 if (end_row
!= tbl
->get_nrows() - 1)
1150 printfs("&(\\n[" CURRENT_ROW_REG
"]<%1)",
1151 as_string(end_row
));
1153 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG
"] .nr %2 \\n[#T]\n",
1154 as_string(start_row
),
1155 row_top_reg(start_row
));
1156 const char *offset_table
[3];
1158 offset_table
[0] = "-" HALF_DOUBLE_LINE_SEP
;
1159 offset_table
[1] = "+" HALF_DOUBLE_LINE_SEP
;
1160 offset_table
[2] = 0;
1163 offset_table
[0] = "";
1164 offset_table
[1] = 0;
1166 for (const char **offsetp
= offset_table
; *offsetp
; offsetp
++) {
1169 if (!bot_adjust
.empty())
1170 printfs("+%1", bot_adjust
);
1172 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG
"]]\\D'l 0 |\\n[%2]u-1v",
1173 column_divide_reg(col
),
1174 row_top_reg(start_row
),
1176 if (!bot_adjust
.empty())
1177 printfs("-(%1)", bot_adjust
);
1178 // don't perform the top adjustment if the top is actually #T
1179 if (!top_adjust
.empty())
1180 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG
"]))",
1182 as_string(start_row
));
1188 void vertical_rule::print()
1190 printfs("\\*[" TRANSPARENT_STRING_NAME
"]"
1191 ".if %1<=\\*[" QUOTE_STRING_NAME
"]\\n[" LAST_PASSED_ROW_REG
"] "
1192 ".nr %2 \\*[" QUOTE_STRING_NAME
"]\\n[#T]\n",
1193 as_string(start_row
),
1194 row_top_reg(start_row
));
1195 const char *offset_table
[3];
1197 offset_table
[0] = "-" HALF_DOUBLE_LINE_SEP
;
1198 offset_table
[1] = "+" HALF_DOUBLE_LINE_SEP
;
1199 offset_table
[2] = 0;
1202 offset_table
[0] = "";
1203 offset_table
[1] = 0;
1205 for (const char **offsetp
= offset_table
; *offsetp
; offsetp
++) {
1206 prints("\\*[" TRANSPARENT_STRING_NAME
"].sp -1\n"
1207 "\\*[" TRANSPARENT_STRING_NAME
"]\\v'" BODY_DEPTH
);
1208 if (!bot_adjust
.empty())
1209 printfs("+%1", bot_adjust
);
1211 printfs("\\h'\\n[%1]u%3'"
1212 "\\s[\\n[" LINESIZE_REG
"]]"
1213 "\\D'l 0 |\\*[" QUOTE_STRING_NAME
"]\\n[%2]u-1v",
1214 column_divide_reg(col
),
1215 row_top_reg(start_row
),
1217 if (!bot_adjust
.empty())
1218 printfs("-(%1)", bot_adjust
);
1219 // don't perform the top adjustment if the top is actually #T
1220 if (!top_adjust
.empty())
1221 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME
"]\\n["
1222 LAST_PASSED_ROW_REG
"]))",
1224 as_string(start_row
));
1230 table::table(int nc
, unsigned f
, int ls
, char dpc
)
1231 : nrows(0), ncolumns(nc
), linesize(ls
), decimal_point_char(dpc
),
1232 vrule_list(0), stuff_list(0), span_list(0),
1233 entry_list(0), entry_list_tailp(&entry_list
), entry(0),
1234 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1235 total_separation(0), allocated_rows(0), flags(f
)
1237 minimum_width
= new string
[ncolumns
];
1238 column_separation
= ncolumns
> 1 ? new int[ncolumns
- 1] : 0;
1239 equal
= new char[ncolumns
];
1240 expand
= new char[ncolumns
];
1242 for (i
= 0; i
< ncolumns
; i
++) {
1246 for (i
= 0; i
< ncolumns
- 1; i
++)
1247 column_separation
[i
] = DEFAULT_COLUMN_SEPARATION
;
1248 delim
[0] = delim
[1] = '\0';
1253 for (int i
= 0; i
< nrows
; i
++) {
1259 while (entry_list
) {
1260 table_entry
*tem
= entry_list
;
1261 entry_list
= entry_list
->next
;
1264 ad_delete(ncolumns
) minimum_width
;
1265 a_delete column_separation
;
1268 while (stuff_list
) {
1269 stuff
*tem
= stuff_list
;
1270 stuff_list
= stuff_list
->next
;
1273 while (vrule_list
) {
1274 vertical_rule
*tem
= vrule_list
;
1275 vrule_list
= vrule_list
->next
;
1278 a_delete row_is_all_lines
;
1280 horizontal_span
*tem
= span_list
;
1281 span_list
= span_list
->next
;
1286 void table::set_delim(char c1
, char c2
)
1292 void table::set_minimum_width(int c
, const string
&w
)
1294 assert(c
>= 0 && c
< ncolumns
);
1295 minimum_width
[c
] = w
;
1298 void table::set_column_separation(int c
, int n
)
1300 assert(c
>= 0 && c
< ncolumns
- 1);
1301 column_separation
[c
] = n
;
1304 void table::set_equal_column(int c
)
1306 assert(c
>= 0 && c
< ncolumns
);
1310 void table::set_expand_column(int c
)
1312 assert(c
>= 0 && c
< ncolumns
);
1316 void table::add_stuff(stuff
*p
)
1319 for (pp
= &stuff_list
; *pp
; pp
= &(*pp
)->next
)
1324 void table::add_text_line(int r
, const string
&s
, const char *filename
,
1327 add_stuff(new text_stuff(s
, r
, filename
, lineno
));
1330 void table::add_single_hline(int r
)
1332 add_stuff(new single_hline_stuff(r
));
1335 void table::add_double_hline(int r
)
1337 add_stuff(new double_hline_stuff(r
));
1340 void table::allocate(int r
)
1343 typedef table_entry
**PPtable_entry
; // work around g++ 1.36.1 bug
1344 if (r
>= allocated_rows
) {
1345 if (allocated_rows
== 0) {
1346 allocated_rows
= 16;
1347 if (allocated_rows
<= r
)
1348 allocated_rows
= r
+ 1;
1349 entry
= new PPtable_entry
[allocated_rows
];
1350 vline
= new char*[allocated_rows
];
1353 table_entry
***old_entry
= entry
;
1354 int old_allocated_rows
= allocated_rows
;
1355 allocated_rows
*= 2;
1356 if (allocated_rows
<= r
)
1357 allocated_rows
= r
+ 1;
1358 entry
= new PPtable_entry
[allocated_rows
];
1359 memcpy(entry
, old_entry
, sizeof(table_entry
**)*old_allocated_rows
);
1361 char **old_vline
= vline
;
1362 vline
= new char*[allocated_rows
];
1363 memcpy(vline
, old_vline
, sizeof(char*)*old_allocated_rows
);
1367 assert(allocated_rows
> r
);
1368 while (nrows
<= r
) {
1369 entry
[nrows
] = new table_entry
*[ncolumns
];
1371 for (i
= 0; i
< ncolumns
; i
++)
1372 entry
[nrows
][i
] = 0;
1373 vline
[nrows
] = new char[ncolumns
+1];
1374 for (i
= 0; i
< ncolumns
+1; i
++)
1375 vline
[nrows
][i
] = 0;
1381 void table::do_hspan(int r
, int c
)
1383 assert(r
>= 0 && c
>= 0 && r
< nrows
&& c
< ncolumns
);
1385 error("first column cannot be horizontally spanned");
1388 table_entry
*e
= entry
[r
][c
];
1390 assert(e
->start_row
<= r
&& r
<= e
->end_row
1391 && e
->start_col
<= c
&& c
<= e
->end_col
1392 && e
->end_row
- e
->start_row
> 0
1393 && e
->end_col
- e
->start_col
> 0);
1397 // e can be 0 if we had an empty entry or an error
1400 if (e
->start_row
!= r
) {
1404 error("impossible horizontal span at row %1, column %2", r
+ 1, c
+ 1);
1412 void table::do_vspan(int r
, int c
)
1414 assert(r
>= 0 && c
>= 0 && r
< nrows
&& c
< ncolumns
);
1416 error("first row cannot be vertically spanned");
1419 table_entry
*e
= entry
[r
][c
];
1421 assert(e
->start_row
<= r
&& r
<= e
->end_row
1422 && e
->start_col
<= c
&& c
<= e
->end_col
1423 && e
->end_row
- e
->start_row
> 0
1424 && e
->end_col
- e
->start_col
> 0);
1428 // e can be 0 if we had an empty entry or an error
1431 if (e
->start_col
!= c
) {
1434 error("impossible vertical span at row %1, column %2", r
+ 1, c
+ 1);
1437 for (int i
= c
; i
<= e
->end_col
; i
++) {
1438 assert(entry
[r
][i
] == 0);
1445 int find_decimal_point(const char *s
, char decimal_point_char
,
1448 if (s
== 0 || *s
== '\0')
1451 int in_delim
= 0; // is p within eqn delimiters?
1452 // tbl recognises \& even within eqn delimiters; I don't
1453 for (p
= s
; *p
; p
++)
1458 else if (*p
== delim
[0])
1460 else if (p
[0] == '\\' && p
[1] == '&')
1462 int possible_pos
= -1;
1464 for (p
= s
; *p
; p
++)
1469 else if (*p
== delim
[0])
1471 else if (p
[0] == decimal_point_char
&& csdigit(p
[1]))
1472 possible_pos
= p
- s
;
1473 if (possible_pos
>= 0)
1474 return possible_pos
;
1476 for (p
= s
; *p
; p
++)
1481 else if (*p
== delim
[0])
1483 else if (csdigit(*p
))
1484 possible_pos
= p
+ 1 - s
;
1485 return possible_pos
;
1488 void table::add_entry(int r
, int c
, const string
&str
, const entry_format
*f
,
1489 const char *fn
, int ln
)
1494 e
= new short_line_entry(this, f
);
1496 else if (str
== "\\=") {
1497 e
= new short_double_line_entry(this, f
);
1499 else if (str
== "_") {
1500 single_line_entry
*lefte
;
1501 if (c
> 0 && entry
[r
][c
-1] != 0 &&
1502 (lefte
= entry
[r
][c
-1]->to_single_line_entry()) != 0
1503 && lefte
->start_row
== r
1504 && lefte
->mod
->stagger
== f
->stagger
) {
1506 entry
[r
][c
] = lefte
;
1509 e
= new single_line_entry(this, f
);
1511 else if (str
== "=") {
1512 double_line_entry
*lefte
;
1513 if (c
> 0 && entry
[r
][c
-1] != 0 &&
1514 (lefte
= entry
[r
][c
-1]->to_double_line_entry()) != 0
1515 && lefte
->start_row
== r
1516 && lefte
->mod
->stagger
== f
->stagger
) {
1518 entry
[r
][c
] = lefte
;
1521 e
= new double_line_entry(this, f
);
1523 else if (str
== "\\^") {
1526 else if (str
.length() > 2 && str
[0] == '\\' && str
[1] == 'R') {
1527 if (str
.search('\n') >= 0)
1528 error_with_file_and_line(fn
, ln
, "bad repeated character");
1530 char *s
= str
.substring(2, str
.length() - 2).extract();
1531 e
= new repeated_char_entry(this, f
, s
);
1535 int is_block
= str
.search('\n') >= 0;
1539 assert(str
.empty());
1546 e
= new left_block_entry(this, f
, s
);
1548 e
= new left_text_entry(this, f
, s
);
1551 e
= new empty_entry(this, f
);
1557 e
= new center_block_entry(this, f
, s
);
1559 e
= new center_text_entry(this, f
, s
);
1562 e
= new empty_entry(this, f
);
1568 e
= new right_block_entry(this, f
, s
);
1570 e
= new right_text_entry(this, f
, s
);
1573 e
= new empty_entry(this, f
);
1575 case FORMAT_NUMERIC
:
1579 error_with_file_and_line(fn
, ln
, "can't have numeric text block");
1580 e
= new left_block_entry(this, f
, s
);
1583 int pos
= find_decimal_point(s
, decimal_point_char
, delim
);
1585 e
= new center_text_entry(this, f
, s
);
1587 e
= new numeric_text_entry(this, f
, s
, pos
);
1591 e
= new empty_entry(this, f
);
1593 case FORMAT_ALPHABETIC
:
1597 e
= new alphabetic_block_entry(this, f
, s
);
1599 e
= new alphabetic_text_entry(this, f
, s
);
1602 e
= new empty_entry(this, f
);
1608 if (str
.length() != 0)
1609 error_with_file_and_line(fn
, ln
,
1610 "non-empty data entry for `_' format ignored");
1611 e
= new single_line_entry(this, f
);
1613 case FORMAT_DOUBLE_HLINE
:
1614 if (str
.length() != 0)
1615 error_with_file_and_line(fn
, ln
,
1616 "non-empty data entry for `=' format ignored");
1617 e
= new double_line_entry(this, f
);
1624 table_entry
*preve
= entry
[r
][c
];
1628 error_with_file_and_line(fn
, ln
, "row %1, column %2 already spanned",
1633 e
->input_lineno
= ln
;
1634 e
->input_filename
= fn
;
1635 e
->start_row
= e
->end_row
= r
;
1636 e
->start_col
= e
->end_col
= c
;
1637 *entry_list_tailp
= e
;
1638 entry_list_tailp
= &e
->next
;
1644 // add vertical lines for row r
1646 void table::add_vlines(int r
, const char *v
)
1649 for (int i
= 0; i
< ncolumns
+1; i
++)
1655 table_entry
*p
= entry_list
;
1658 for (i
= p
->start_row
; i
<= p
->end_row
; i
++)
1659 for (j
= p
->start_col
; j
<= p
->end_col
; j
++)
1660 assert(entry
[i
][j
] == p
);
1667 location_force_filename
= 1;
1670 determine_row_type();
1672 if (!(flags
& CENTER
))
1673 prints(".if \\n[" SAVED_CENTER_REG
"] \\{");
1674 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1675 ".nr " SAVED_INDENT_REG
" \\n[.i]\n");
1676 if (!(flags
& CENTER
))
1679 define_bottom_macro();
1681 for (int i
= 0; i
< nrows
; i
++)
1686 void table::determine_row_type()
1688 row_is_all_lines
= new char[nrows
];
1689 for (int i
= 0; i
< nrows
; i
++) {
1692 int had_non_line
= 0;
1693 for (int c
= 0; c
< ncolumns
; c
++) {
1694 table_entry
*e
= entry
[i
][c
];
1696 if (e
->start_row
== e
->end_row
) {
1697 int t
= e
->line_type();
1721 row_is_all_lines
[i
] = 0;
1722 else if (had_double
)
1723 row_is_all_lines
[i
] = 2;
1724 else if (had_single
)
1725 row_is_all_lines
[i
] = 1;
1727 row_is_all_lines
[i
] = 0;
1731 int table::count_expand_columns()
1734 for (int i
= 0; i
< ncolumns
; i
++)
1740 void table::init_output()
1742 prints(".nr " COMPATIBLE_REG
" \\n(.C\n"
1745 printfs(".nr " LINESIZE_REG
" %1\n", as_string(linesize
));
1747 prints(".nr " LINESIZE_REG
" \\n[.s]\n");
1748 if (!(flags
& CENTER
))
1749 prints(".nr " SAVED_CENTER_REG
" \\n[.ce]\n");
1750 if (compatible_flag
)
1751 prints(".ds " LEADER_REG
" \\a\n");
1752 prints(".de " RESET_MACRO_NAME
"\n"
1764 ".nr " SAVED_INDENT_REG
" \\n[.i]\n"
1765 ".nr " SAVED_FONT_REG
" \\n[.f]\n"
1766 ".nr " SAVED_SIZE_REG
" \\n[.s]\n"
1767 ".nr " SAVED_FILL_REG
" \\n[.u]\n"
1769 ".nr " CURRENT_ROW_REG
" 0-1\n"
1770 ".nr " LAST_PASSED_ROW_REG
" 0-1\n"
1771 ".nr " SECTION_DIVERSION_FLAG_REG
" 0\n"
1772 ".ds " TRANSPARENT_STRING_NAME
"\n"
1773 ".ds " QUOTE_STRING_NAME
"\n"
1774 ".nr " NEED_BOTTOM_RULE_REG
" 1\n"
1775 ".nr " SUPPRESS_BOTTOM_REG
" 0\n"
1777 ".de " REPEATED_MARK_MACRO
"\n"
1779 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO
" \"\\$1\"\n"
1781 ".de " REPEATED_VPT_MACRO
"\n"
1783 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO
" \"\\$1\"\n"
1785 if (!(flags
& NOKEEP
))
1786 prints(".de " KEEP_MACRO_NAME
"\n"
1787 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME
" \\\\\n"
1788 ".ds " TRANSPARENT_STRING_NAME
" \\!\n"
1789 ".di " SECTION_DIVERSION_NAME
"\n"
1790 ".nr " SECTION_DIVERSION_FLAG_REG
" 1\n"
1794 ".de " RELEASE_MACRO_NAME
"\n"
1795 ".if \\n[" SECTION_DIVERSION_FLAG_REG
"] \\{"
1797 ".in \\n[" SAVED_INDENT_REG
"]u\n"
1798 ".nr " SAVED_DN_REG
" \\n[dn]\n"
1799 ".ds " QUOTE_STRING_NAME
"\n"
1800 ".ds " TRANSPARENT_STRING_NAME
"\n"
1801 ".nr " SECTION_DIVERSION_FLAG_REG
" 0\n"
1802 ".if \\n[.t]<=\\n[dn] \\{"
1805 ".nr " SUPPRESS_BOTTOM_REG
" 1\n"
1807 ".nr " SUPPRESS_BOTTOM_REG
" 0\n"
1810 ".if \\n[.t]<=\\n[" SAVED_DN_REG
"] "
1811 /* Since we turn off traps, it won't get into an infinite loop
1812 when we try and print it; it will just go off the bottom of the
1814 ".tm warning: page \\n%: table text block will not fit on one page\n"
1817 "." SECTION_DIVERSION_NAME
"\n"
1819 ".rm " SECTION_DIVERSION_NAME
"\n"
1822 ".nr " TABLE_DIVERSION_FLAG_REG
" 0\n"
1823 ".de " TABLE_KEEP_MACRO_NAME
"\n"
1824 ".if '\\n[.z]'' \\{"
1825 ".di " TABLE_DIVERSION_NAME
"\n"
1826 ".nr " TABLE_DIVERSION_FLAG_REG
" 1\n"
1829 ".de " TABLE_RELEASE_MACRO_NAME
"\n"
1830 ".if \\n[" TABLE_DIVERSION_FLAG_REG
"] \\{.br\n"
1832 ".nr " SAVED_DN_REG
" \\n[dn]\n"
1833 ".ne \\n[dn]u+\\n[.V]u\n"
1834 ".ie \\n[.t]<=\\n[" SAVED_DN_REG
"] "
1835 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1840 "." TABLE_DIVERSION_NAME
"\n"
1842 ".rm " TABLE_DIVERSION_NAME
"\n"
1850 string
block_width_reg(int r
, int c
)
1852 static char name
[sizeof(BLOCK_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1853 sprintf(name
, BLOCK_WIDTH_PREFIX
"%d,%d", r
, c
);
1854 return string(name
);
1857 string
block_diversion_name(int r
, int c
)
1859 static char name
[sizeof(BLOCK_DIVERSION_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1860 sprintf(name
, BLOCK_DIVERSION_PREFIX
"%d,%d", r
, c
);
1861 return string(name
);
1864 string
block_height_reg(int r
, int c
)
1866 static char name
[sizeof(BLOCK_HEIGHT_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1867 sprintf(name
, BLOCK_HEIGHT_PREFIX
"%d,%d", r
, c
);
1868 return string(name
);
1871 string
span_width_reg(int start_col
, int end_col
)
1873 static char name
[sizeof(SPAN_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1874 sprintf(name
, SPAN_WIDTH_PREFIX
"%d", start_col
);
1875 if (end_col
!= start_col
)
1876 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1877 return string(name
);
1880 string
span_left_numeric_width_reg(int start_col
, int end_col
)
1882 static char name
[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1883 sprintf(name
, SPAN_LEFT_NUMERIC_WIDTH_PREFIX
"%d", start_col
);
1884 if (end_col
!= start_col
)
1885 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1886 return string(name
);
1889 string
span_right_numeric_width_reg(int start_col
, int end_col
)
1891 static char name
[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1892 sprintf(name
, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX
"%d", start_col
);
1893 if (end_col
!= start_col
)
1894 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1895 return string(name
);
1898 string
span_alphabetic_width_reg(int start_col
, int end_col
)
1900 static char name
[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1901 sprintf(name
, SPAN_ALPHABETIC_WIDTH_PREFIX
"%d", start_col
);
1902 if (end_col
!= start_col
)
1903 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1904 return string(name
);
1907 string
column_separation_reg(int col
)
1909 static char name
[sizeof(COLUMN_SEPARATION_PREFIX
)+INT_DIGITS
];
1910 sprintf(name
, COLUMN_SEPARATION_PREFIX
"%d", col
);
1911 return string(name
);
1914 string
row_start_reg(int row
)
1916 static char name
[sizeof(ROW_START_PREFIX
)+INT_DIGITS
];
1917 sprintf(name
, ROW_START_PREFIX
"%d", row
);
1918 return string(name
);
1921 string
column_start_reg(int col
)
1923 static char name
[sizeof(COLUMN_START_PREFIX
)+INT_DIGITS
];
1924 sprintf(name
, COLUMN_START_PREFIX
"%d", col
);
1925 return string(name
);
1928 string
column_end_reg(int col
)
1930 static char name
[sizeof(COLUMN_END_PREFIX
)+INT_DIGITS
];
1931 sprintf(name
, COLUMN_END_PREFIX
"%d", col
);
1932 return string(name
);
1935 string
column_divide_reg(int col
)
1937 static char name
[sizeof(COLUMN_DIVIDE_PREFIX
)+INT_DIGITS
];
1938 sprintf(name
, COLUMN_DIVIDE_PREFIX
"%d", col
);
1939 return string(name
);
1942 string
row_top_reg(int row
)
1944 static char name
[sizeof(ROW_TOP_PREFIX
)+INT_DIGITS
];
1945 sprintf(name
, ROW_TOP_PREFIX
"%d", row
);
1946 return string(name
);
1949 void init_span_reg(int start_col
, int end_col
)
1951 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1952 span_width_reg(start_col
, end_col
),
1953 span_alphabetic_width_reg(start_col
, end_col
),
1954 span_left_numeric_width_reg(start_col
, end_col
),
1955 span_right_numeric_width_reg(start_col
, end_col
));
1958 void compute_span_width(int start_col
, int end_col
)
1960 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1961 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1962 span_width_reg(start_col
, end_col
),
1963 span_left_numeric_width_reg(start_col
, end_col
),
1964 span_right_numeric_width_reg(start_col
, end_col
),
1965 span_alphabetic_width_reg(start_col
, end_col
));
1968 // Increase the widths of columns so that the width of any spanning entry
1969 // is not greater than the sum of the widths of the columns that it spans.
1970 // Ensure that the widths of columns remain equal.
1972 void table::divide_span(int start_col
, int end_col
)
1974 assert(end_col
> start_col
);
1975 printfs(".nr " NEEDED_REG
" \\n[%1]-(\\n[%2]",
1976 span_width_reg(start_col
, end_col
),
1977 span_width_reg(start_col
, start_col
));
1979 for (i
= start_col
+ 1; i
<= end_col
; i
++) {
1980 // The column separation may shrink with the expand option.
1981 if (!(flags
& EXPAND
))
1982 printfs("+%1n", as_string(column_separation
[i
- 1]));
1983 printfs("+\\n[%1]", span_width_reg(i
, i
));
1986 printfs(".nr " NEEDED_REG
" \\n[" NEEDED_REG
"]/%1\n",
1987 as_string(end_col
- start_col
+ 1));
1988 prints(".if \\n[" NEEDED_REG
"] \\{");
1989 for (i
= start_col
; i
<= end_col
; i
++)
1990 printfs(".nr %1 +\\n[" NEEDED_REG
"]\n",
1991 span_width_reg(i
, i
));
1993 for (i
= start_col
; i
<= end_col
&& !equal_flag
; i
++)
1994 if (equal
[i
] || expand
[i
])
1997 for (i
= 0; i
< ncolumns
; i
++)
1998 if (i
< start_col
|| i
> end_col
)
1999 printfs(".nr %1 +\\n[" NEEDED_REG
"]\n",
2000 span_width_reg(i
, i
));
2005 void table::sum_columns(int start_col
, int end_col
, int do_expand
)
2007 assert(end_col
> start_col
);
2009 for (i
= start_col
; i
<= end_col
; i
++)
2020 printfs(".nr %1 \\n[%2]",
2021 span_width_reg(start_col
, end_col
),
2022 span_width_reg(start_col
, start_col
));
2023 for (i
= start_col
+ 1; i
<= end_col
; i
++)
2024 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG
"])+\\n[%2]",
2025 as_string(column_separation
[i
- 1]),
2026 span_width_reg(i
, i
));
2030 horizontal_span::horizontal_span(int sc
, int ec
, horizontal_span
*p
)
2031 : next(p
), start_col(sc
), end_col(ec
)
2035 void table::build_span_list()
2038 table_entry
*p
= entry_list
;
2040 if (p
->end_col
!= p
->start_col
) {
2042 for (q
= span_list
; q
; q
= q
->next
)
2043 if (q
->start_col
== p
->start_col
2044 && q
->end_col
== p
->end_col
)
2047 span_list
= new horizontal_span(p
->start_col
, p
->end_col
, span_list
);
2051 // Now sort span_list primarily by order of end_row, and secondarily
2052 // by reverse order of start_row. This ensures that if we divide
2053 // spans using the order in span_list, we will get reasonable results.
2054 horizontal_span
*unsorted
= span_list
;
2057 horizontal_span
**pp
;
2058 for (pp
= &span_list
; *pp
; pp
= &(*pp
)->next
)
2059 if (unsorted
->end_col
< (*pp
)->end_col
2060 || (unsorted
->end_col
== (*pp
)->end_col
2061 && (unsorted
->start_col
> (*pp
)->start_col
)))
2063 horizontal_span
*tem
= unsorted
->next
;
2064 unsorted
->next
= *pp
;
2070 void table::compute_expand_width()
2073 int colcount
= count_expand_columns();
2074 prints(".nr " EXPAND_REG
" \\n[.l]-\\n[.i]");
2075 for (i
= 0; i
< ncolumns
; i
++)
2077 printfs("-\\n[%1]", span_width_reg(i
, i
));
2078 if (total_separation
)
2079 printfs("-%1n", as_string(total_separation
));
2081 prints(".if \\n[" EXPAND_REG
"]<0 \\{");
2082 entry_list
->set_location();
2083 prints(".tm warning: around line \\n[.c]: table wider than line width\n"
2084 ".nr " EXPAND_REG
" 0\n"
2087 printfs(".nr " EXPAND_REG
" \\n[" EXPAND_REG
"]/%1\n",
2088 as_string(colcount
));
2089 for (i
= 0; i
< ncolumns
; i
++)
2091 printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG
"]\n", span_width_reg(i
, i
));
2094 void table::compute_total_separation()
2096 if (flags
& (ALLBOX
| BOX
| DOUBLEBOX
))
2097 left_separation
= right_separation
= 1;
2099 for (int i
= 0; i
< nrows
; i
++) {
2100 if (vline
[i
][0] > 0)
2101 left_separation
= 1;
2102 if (vline
[i
][ncolumns
] > 0)
2103 right_separation
= 1;
2106 total_separation
= left_separation
+ right_separation
;
2108 for (i
= 0; i
< ncolumns
- 1; i
++)
2109 total_separation
+= column_separation
[i
];
2112 void table::compute_separation_factor()
2114 // Don't let the separation factor be negative.
2115 prints(".nr " SEPARATION_FACTOR_REG
" \\n[.l]-\\n[.i]");
2116 for (int i
= 0; i
< ncolumns
; i
++)
2117 printfs("-\\n[%1]", span_width_reg(i
, i
));
2118 printfs("/%1\n", as_string(total_separation
));
2119 prints(".ie \\n[" SEPARATION_FACTOR_REG
"]<=0 \\{");
2120 entry_list
->set_location();
2121 prints(".tm warning: around line \\n[.c]: column separation set to zero\n"
2122 ".nr " SEPARATION_FACTOR_REG
" 0\n"
2124 ".el .if \\n[" SEPARATION_FACTOR_REG
"]<1n \\{");
2125 entry_list
->set_location();
2126 prints(".tm warning: around line \\n[.c]: table squeezed horizontally to fit line length\n"
2130 void table::compute_column_positions()
2132 printfs(".nr %1 0\n", column_divide_reg(0));
2133 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG
"]\n",
2134 column_start_reg(0),
2135 as_string(left_separation
));
2138 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2139 column_end_reg(i
-1),
2140 column_start_reg(i
-1),
2141 span_width_reg(i
-1, i
-1));
2144 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG
"])\n",
2145 column_start_reg(i
),
2146 column_end_reg(i
-1),
2147 as_string(column_separation
[i
-1]));
2148 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2149 column_divide_reg(i
),
2150 column_end_reg(i
-1),
2151 column_start_reg(i
));
2153 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG
"])\n",
2154 column_divide_reg(ncolumns
),
2155 column_end_reg(i
-1),
2156 as_string(right_separation
));
2157 printfs(".nr TW \\n[%1]\n",
2158 column_divide_reg(ncolumns
));
2159 if (flags
& DOUBLEBOX
) {
2160 printfs(".nr %1 +" DOUBLE_LINE_SEP
"\n", column_divide_reg(0));
2161 printfs(".nr %1 -" DOUBLE_LINE_SEP
"\n", column_divide_reg(ncolumns
));
2165 void table::make_columns_equal()
2167 int first
= -1; // index of first equal column
2169 for (i
= 0; i
< ncolumns
; i
++)
2172 printfs(".nr %1 \\n[%1]", span_width_reg(i
, i
));
2176 printfs(">?\\n[%1]", span_width_reg(i
, i
));
2180 for (i
= first
+ 1; i
< ncolumns
; i
++)
2182 printfs(".nr %1 \\n[%2]\n",
2183 span_width_reg(i
, i
),
2184 span_width_reg(first
, first
));
2188 void table::compute_widths()
2193 // These values get refined later.
2194 prints(".nr " SEPARATION_FACTOR_REG
" 1n\n");
2195 for (i
= 0; i
< ncolumns
; i
++) {
2196 init_span_reg(i
, i
);
2197 if (!minimum_width
[i
].empty())
2198 printfs(".nr %1 (n;%2)\n", span_width_reg(i
, i
), minimum_width
[i
]);
2200 for (p
= span_list
; p
; p
= p
->next
)
2201 init_span_reg(p
->start_col
, p
->end_col
);
2202 // Compute all field widths except for blocks.
2204 for (q
= entry_list
; q
; q
= q
->next
)
2205 if (!q
->mod
->zero_width
)
2207 // Compute all span widths, not handling blocks yet.
2208 for (i
= 0; i
< ncolumns
; i
++)
2209 compute_span_width(i
, i
);
2210 for (p
= span_list
; p
; p
= p
->next
)
2211 compute_span_width(p
->start_col
, p
->end_col
);
2212 // Making columns equal normally increases the width of some columns.
2213 make_columns_equal();
2214 // Note that divide_span keeps equal width columns equal.
2215 // This function might increase the width of some columns, too.
2216 for (p
= span_list
; p
; p
= p
->next
)
2217 divide_span(p
->start_col
, p
->end_col
);
2218 compute_total_separation();
2219 for (p
= span_list
; p
; p
= p
->next
)
2220 sum_columns(p
->start_col
, p
->end_col
, 0);
2221 // Now handle unexpanded blocks.
2222 int had_spanning_block
= 0;
2223 int had_equal_block
= 0;
2224 for (q
= entry_list
; q
; q
= q
->next
)
2225 if (q
->divert(ncolumns
, minimum_width
,
2226 (flags
& EXPAND
) ? column_separation
: 0, 0)) {
2227 if (q
->end_col
> q
->start_col
)
2228 had_spanning_block
= 1;
2229 for (i
= q
->start_col
; i
<= q
->end_col
&& !had_equal_block
; i
++)
2231 had_equal_block
= 1;
2234 if (had_equal_block
)
2235 make_columns_equal();
2236 if (had_spanning_block
)
2237 for (p
= span_list
; p
; p
= p
->next
)
2238 divide_span(p
->start_col
, p
->end_col
);
2239 compute_expand_width();
2240 if ((flags
& EXPAND
) && total_separation
!= 0) {
2241 compute_separation_factor();
2242 for (p
= span_list
; p
; p
= p
->next
)
2243 sum_columns(p
->start_col
, p
->end_col
, 0);
2246 // Handle expanded blocks.
2247 for (p
= span_list
; p
; p
= p
->next
)
2248 sum_columns(p
->start_col
, p
->end_col
, 1);
2249 for (q
= entry_list
; q
; q
= q
->next
)
2250 if (q
->divert(ncolumns
, minimum_width
, 0, 1)) {
2251 if (q
->end_col
> q
->start_col
)
2252 had_spanning_block
= 1;
2254 // Adjust widths again.
2255 if (had_spanning_block
)
2256 for (p
= span_list
; p
; p
= p
->next
)
2257 divide_span(p
->start_col
, p
->end_col
);
2259 compute_column_positions();
2262 void table::print_single_hline(int r
)
2264 prints(".vs " LINE_SEP
">?\\n[.V]u\n"
2266 "\\v'" BODY_DEPTH
"'"
2267 "\\s[\\n[" LINESIZE_REG
"]]");
2269 prints("\\D'l |\\n[TW]u 0'");
2273 while (start_col
< ncolumns
2274 && entry
[r
][start_col
] != 0
2275 && entry
[r
][start_col
]->start_row
!= r
)
2278 for (end_col
= start_col
;
2280 && (entry
[r
][end_col
] == 0
2281 || entry
[r
][end_col
]->start_row
== r
);
2284 if (end_col
<= start_col
)
2286 printfs("\\h'|\\n[%1]u",
2287 column_divide_reg(start_col
));
2288 if ((r
> 0 && vline
[r
-1][start_col
] == 2)
2289 || (r
< nrows
&& vline
[r
][start_col
] == 2))
2290 prints("-" HALF_DOUBLE_LINE_SEP
);
2292 printfs("\\D'l |\\n[%1]u",
2293 column_divide_reg(end_col
));
2294 if ((r
> 0 && vline
[r
-1][end_col
] == 2)
2295 || (r
< nrows
&& vline
[r
][end_col
] == 2))
2296 prints("+" HALF_DOUBLE_LINE_SEP
);
2298 start_col
= end_col
;
2306 void table::print_double_hline(int r
)
2308 prints(".vs " LINE_SEP
"+" DOUBLE_LINE_SEP
2311 "\\v'" BODY_DEPTH
"'"
2312 "\\s[\\n[" LINESIZE_REG
"]]");
2314 prints("\\v'-" DOUBLE_LINE_SEP
"'"
2315 "\\D'l |\\n[TW]u 0'"
2316 "\\v'" DOUBLE_LINE_SEP
"'"
2318 "\\D'l |\\n[TW]u 0'");
2322 while (start_col
< ncolumns
2323 && entry
[r
][start_col
] != 0
2324 && entry
[r
][start_col
]->start_row
!= r
)
2327 for (end_col
= start_col
;
2329 && (entry
[r
][end_col
] == 0
2330 || entry
[r
][end_col
]->start_row
== r
);
2333 if (end_col
<= start_col
)
2335 const char *left_adjust
= 0;
2336 if ((r
> 0 && vline
[r
-1][start_col
] == 2)
2337 || (r
< nrows
&& vline
[r
][start_col
] == 2))
2338 left_adjust
= "-" HALF_DOUBLE_LINE_SEP
;
2339 const char *right_adjust
= 0;
2340 if ((r
> 0 && vline
[r
-1][end_col
] == 2)
2341 || (r
< nrows
&& vline
[r
][end_col
] == 2))
2342 right_adjust
= "+" HALF_DOUBLE_LINE_SEP
;
2343 printfs("\\v'-" DOUBLE_LINE_SEP
"'"
2345 column_divide_reg(start_col
));
2347 prints(left_adjust
);
2349 printfs("\\D'l |\\n[%1]u",
2350 column_divide_reg(end_col
));
2352 prints(right_adjust
);
2354 printfs("\\v'" DOUBLE_LINE_SEP
"'"
2356 column_divide_reg(start_col
));
2358 prints(left_adjust
);
2360 printfs("\\D'l |\\n[%1]u",
2361 column_divide_reg(end_col
));
2363 prints(right_adjust
);
2365 start_col
= end_col
;
2373 void table::compute_vrule_top_adjust(int start_row
, int col
, string
&result
)
2375 if (row_is_all_lines
[start_row
] && start_row
< nrows
- 1) {
2376 if (row_is_all_lines
[start_row
] == 2)
2377 result
= LINE_SEP
">?\\n[.V]u" "+" DOUBLE_LINE_SEP
;
2379 result
= LINE_SEP
">?\\n[.V]u";
2386 for (stuff
*p
= stuff_list
; p
&& p
->row
<= start_row
; p
= p
->next
)
2387 if (p
->row
== start_row
2388 && (p
->is_single_line() || p
->is_double_line()))
2393 table_entry
*e
= entry
[start_row
-1][col
-1];
2394 if (e
&& e
->start_row
== e
->end_row
) {
2395 if (e
->to_double_line_entry() != 0)
2397 else if (e
->to_single_line_entry() != 0)
2402 if (col
< ncolumns
) {
2403 table_entry
*e
= entry
[start_row
-1][col
];
2404 if (e
&& e
->start_row
== e
->end_row
) {
2405 if (e
->to_double_line_entry() != 0)
2407 else if (e
->to_single_line_entry() != 0)
2411 if (row_is_all_lines
[start_row
-1] == 0) {
2412 if (left
> 0 || right
> 0) {
2413 result
+= "-" BODY_DEPTH
"-" BAR_HEIGHT
;
2414 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2415 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2416 else if (left
== 2 && right
== 2)
2417 result
+= "+" HALF_DOUBLE_LINE_SEP
;
2420 else if (row_is_all_lines
[start_row
-1] == 2) {
2421 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2422 result
+= "-" DOUBLE_LINE_SEP
;
2423 else if (left
== 1 || right
== 1)
2424 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2428 void table::compute_vrule_bot_adjust(int end_row
, int col
, string
&result
)
2430 if (row_is_all_lines
[end_row
] && end_row
> 0) {
2436 for (p
= stuff_list
; p
&& p
->row
< end_row
+ 1; p
= p
->next
)
2438 if (p
&& p
->row
== end_row
+ 1 && p
->is_double_line()) {
2439 result
= "-" DOUBLE_LINE_SEP
;
2442 if ((p
!= 0 && p
->row
== end_row
+ 1)
2443 || end_row
== nrows
- 1) {
2447 if (row_is_all_lines
[end_row
+1] == 1)
2449 else if (row_is_all_lines
[end_row
+1] == 2)
2450 result
= LINE_SEP
"+" DOUBLE_LINE_SEP
;
2456 table_entry
*e
= entry
[end_row
+1][col
-1];
2457 if (e
&& e
->start_row
== e
->end_row
) {
2458 if (e
->to_double_line_entry() != 0)
2460 else if (e
->to_single_line_entry() != 0)
2465 if (col
< ncolumns
) {
2466 table_entry
*e
= entry
[end_row
+1][col
];
2467 if (e
&& e
->start_row
== e
->end_row
) {
2468 if (e
->to_double_line_entry() != 0)
2470 else if (e
->to_single_line_entry() != 0)
2474 if (row_is_all_lines
[end_row
+1] == 0) {
2475 if (left
> 0 || right
> 0) {
2476 result
= "1v-" BODY_DEPTH
"-" BAR_HEIGHT
;
2477 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2478 result
+= "+" HALF_DOUBLE_LINE_SEP
;
2479 else if (left
== 2 && right
== 2)
2480 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2483 else if (row_is_all_lines
[end_row
+1] == 2) {
2484 if (left
== 2 && right
== 2)
2485 result
+= "-" DOUBLE_LINE_SEP
;
2486 else if (left
!= 2 && right
!= 2 && (left
== 1 || right
== 1))
2487 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2491 void table::add_vertical_rule(int start_row
, int end_row
,
2492 int col
, int is_double
)
2494 vrule_list
= new vertical_rule(start_row
, end_row
, col
, is_double
,
2496 compute_vrule_top_adjust(start_row
, col
, vrule_list
->top_adjust
);
2497 compute_vrule_bot_adjust(end_row
, col
, vrule_list
->bot_adjust
);
2500 void table::build_vrule_list()
2503 if (flags
& ALLBOX
) {
2504 for (col
= 1; col
< ncolumns
; col
++) {
2507 while (start_row
< nrows
&& vline_spanned(start_row
, col
))
2509 if (start_row
>= nrows
)
2511 int end_row
= start_row
;
2512 while (end_row
< nrows
&& !vline_spanned(end_row
, col
))
2515 add_vertical_rule(start_row
, end_row
, col
, 0);
2516 start_row
= end_row
+ 1;
2520 if (flags
& (BOX
| ALLBOX
| DOUBLEBOX
)) {
2521 add_vertical_rule(0, nrows
- 1, 0, 0);
2522 add_vertical_rule(0, nrows
- 1, ncolumns
, 0);
2524 for (int end_row
= 0; end_row
< nrows
; end_row
++)
2525 for (col
= 0; col
< ncolumns
+1; col
++)
2526 if (vline
[end_row
][col
] > 0
2527 && !vline_spanned(end_row
, col
)
2528 && (end_row
== nrows
- 1
2529 || vline
[end_row
+1][col
] != vline
[end_row
][col
]
2530 || vline_spanned(end_row
+1, col
))) {
2532 for (start_row
= end_row
- 1;
2534 && vline
[start_row
][col
] == vline
[end_row
][col
]
2535 && !vline_spanned(start_row
, col
);
2539 add_vertical_rule(start_row
, end_row
, col
, vline
[end_row
][col
] > 1);
2541 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2543 for (int r
= p
->start_row
; r
<= p
->end_row
; r
++) {
2544 if (p
->col
> 0 && entry
[r
][p
->col
-1] != 0
2545 && entry
[r
][p
->col
-1]->end_col
== p
->col
-1) {
2546 int is_corner
= r
== p
->start_row
|| r
== p
->end_row
;
2547 entry
[r
][p
->col
-1]->note_double_vrule_on_right(is_corner
);
2549 if (p
->col
< ncolumns
&& entry
[r
][p
->col
] != 0
2550 && entry
[r
][p
->col
]->start_col
== p
->col
) {
2551 int is_corner
= r
== p
->start_row
|| r
== p
->end_row
;
2552 entry
[r
][p
->col
]->note_double_vrule_on_left(is_corner
);
2557 void table::define_bottom_macro()
2561 ".if !\\n[" SUPPRESS_BOTTOM_REG
"] \\{"
2562 "." REPEATED_VPT_MACRO
" 0\n"
2563 ".mk " SAVED_VERTICAL_POS_REG
"\n");
2564 if (flags
& (BOX
| ALLBOX
| DOUBLEBOX
)) {
2565 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG
"] \\{");
2566 print_single_hline(0);
2570 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2571 p
->contribute_to_bottom_macro(this);
2572 if (flags
& DOUBLEBOX
)
2573 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP
">?\\n[.V]u\n"
2574 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]"
2575 "\\D'l \\n[TW]u 0'\\s0\n"
2578 ".if \\n[" LAST_PASSED_ROW_REG
"]>=0 "
2579 ".nr " TOP_REG
" \\n[#T]-" DOUBLE_LINE_SEP
"\n"
2581 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]"
2582 "\\D'l 0 |\\n[" TOP_REG
"]u-1v'\\s0\n"
2584 "\\v'" BODY_DEPTH
"'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG
"]]"
2585 "\\D'l 0 |\\n[" TOP_REG
"]u-1v'\\s0\n");
2587 prints(".nr " LAST_PASSED_ROW_REG
" \\n[" CURRENT_ROW_REG
"]\n"
2588 ".sp |\\n[" SAVED_VERTICAL_POS_REG
"]u\n"
2589 "." REPEATED_VPT_MACRO
" 1\n"
2595 // is the vertical line before column c in row r horizontally spanned?
2597 int table::vline_spanned(int r
, int c
)
2599 assert(r
>= 0 && r
< nrows
&& c
>= 0 && c
< ncolumns
+ 1);
2600 return (c
!= 0 && c
!= ncolumns
&& entry
[r
][c
] != 0
2601 && entry
[r
][c
]->start_col
!= c
2602 // horizontally spanning lines don't count
2603 && entry
[r
][c
]->to_double_line_entry() == 0
2604 && entry
[r
][c
]->to_single_line_entry() == 0);
2607 int table::row_begins_section(int r
)
2609 assert(r
>= 0 && r
< nrows
);
2610 for (int i
= 0; i
< ncolumns
; i
++)
2611 if (entry
[r
][i
] && entry
[r
][i
]->start_row
!= r
)
2616 int table::row_ends_section(int r
)
2618 assert(r
>= 0 && r
< nrows
);
2619 for (int i
= 0; i
< ncolumns
; i
++)
2620 if (entry
[r
][i
] && entry
[r
][i
]->end_row
!= r
)
2625 void table::do_row(int r
)
2627 if (!(flags
& NOKEEP
) && row_begins_section(r
))
2628 prints("." KEEP_MACRO_NAME
"\n");
2631 for (p
= stuff_list
; p
&& p
->row
< r
; p
= p
->next
)
2633 for (stuff
*p1
= p
; p1
&& p1
->row
== r
; p1
= p1
->next
)
2634 if (!p1
->printed
&& (p1
->is_single_line() || p1
->is_double_line())) {
2638 if (!had_line
&& !row_is_all_lines
[r
])
2639 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2641 for (; p
&& p
->row
== r
; p
= p
->next
)
2644 if (!had_line
&& (p
->is_single_line() || p
->is_double_line())) {
2645 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2649 // change the row *after* printing the stuff list (which might contain .TH)
2650 printfs("\\*[" TRANSPARENT_STRING_NAME
"].nr " CURRENT_ROW_REG
" %1\n",
2652 if (!had_line
&& row_is_all_lines
[r
])
2653 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2654 // we might have had a .TH, for example, since we last tried
2655 if (!(flags
& NOKEEP
) && row_begins_section(r
))
2656 prints("." KEEP_MACRO_NAME
"\n");
2657 printfs(".mk %1\n", row_start_reg(r
));
2658 prints(".mk " BOTTOM_REG
"\n"
2659 "." REPEATED_VPT_MACRO
" 0\n");
2661 int row_is_blank
= 1;
2662 int first_start_row
= r
;
2663 for (c
= 0; c
< ncolumns
; c
++) {
2664 table_entry
*e
= entry
[r
][c
];
2666 if (e
->end_row
== r
) {
2668 if (e
->start_row
< first_start_row
)
2669 first_start_row
= e
->start_row
;
2676 prints(".nr " BOTTOM_REG
" +1v\n");
2677 if (row_is_all_lines
[r
]) {
2678 prints(".vs " LINE_SEP
);
2679 if (row_is_all_lines
[r
] == 2)
2680 prints("+" DOUBLE_LINE_SEP
);
2681 prints(">?\\n[.V]u\n.ls 1\n");
2683 prints("\\v'" BODY_DEPTH
);
2684 if (row_is_all_lines
[r
] == 2)
2685 prints("-" HALF_DOUBLE_LINE_SEP
);
2687 for (c
= 0; c
< ncolumns
; c
++) {
2688 table_entry
*e
= entry
[r
][c
];
2690 if (e
->end_row
== e
->start_row
)
2691 e
->to_simple_entry()->simple_print(1);
2698 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2699 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2701 for (int i
= row_is_all_lines
[r
] ? r
- 1 : r
;
2702 i
>= first_start_row
;
2704 simple_entry
*first
= 0;
2705 for (c
= 0; c
< ncolumns
; c
++) {
2706 table_entry
*e
= entry
[r
][c
];
2708 if (e
->end_row
== r
&& e
->start_row
== i
) {
2709 simple_entry
*simple
= e
->to_simple_entry();
2723 first
->position_vertically();
2724 first
->set_location();
2726 first
->simple_print(0);
2727 for (c
= first
->end_col
+ 1; c
< ncolumns
; c
++) {
2728 table_entry
*e
= entry
[r
][c
];
2730 if (e
->end_row
== r
&& e
->start_row
== i
) {
2731 simple_entry
*simple
= e
->to_simple_entry();
2733 if (e
->end_row
!= e
->start_row
) {
2735 simple
->position_vertically();
2738 simple
->simple_print(0);
2745 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2746 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2749 for (c
= 0; c
< ncolumns
; c
++) {
2750 table_entry
*e
= entry
[r
][c
];
2752 if (e
->end_row
== r
&& e
->to_simple_entry() == 0) {
2753 e
->position_vertically();
2755 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2756 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2761 prints("." REPEATED_VPT_MACRO
" 1\n"
2762 ".sp |\\n[" BOTTOM_REG
"]u\n"
2763 "\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
" 1\n");
2764 if (r
!= nrows
- 1 && (flags
& ALLBOX
)) {
2765 print_single_hline(r
+ 1);
2766 prints("\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
" 0\n");
2768 if (r
!= nrows
- 1) {
2769 if (p
&& p
->row
== r
+ 1
2770 && (p
->is_single_line() || p
->is_double_line())) {
2772 prints("\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
2775 int printed_one
= 0;
2776 for (vertical_rule
*vr
= vrule_list
; vr
; vr
= vr
->next
)
2777 if (vr
->end_row
== r
) {
2779 prints("." REPEATED_VPT_MACRO
" 0\n");
2785 prints("." REPEATED_VPT_MACRO
" 1\n");
2786 if (!(flags
& NOKEEP
) && row_ends_section(r
))
2787 prints("." RELEASE_MACRO_NAME
"\n");
2791 void table::do_top()
2793 prints(".fc \002\003\n");
2794 if (!(flags
& NOKEEP
) && (flags
& (BOX
| DOUBLEBOX
| ALLBOX
)))
2795 prints("." TABLE_KEEP_MACRO_NAME
"\n");
2796 if (flags
& DOUBLEBOX
) {
2798 ".vs " LINE_SEP
">?\\n[.V]u\n"
2799 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]\\D'l \\n[TW]u 0'\\s0\n"
2801 "." REPEATED_MARK_MACRO
" " TOP_REG
"\n"
2802 ".vs " DOUBLE_LINE_SEP
">?\\n[.V]u\n");
2803 printfs("\\v'" BODY_DEPTH
"'"
2804 "\\s[\\n[" LINESIZE_REG
"]]"
2806 "\\D'l |\\n[%2]u 0'"
2809 column_divide_reg(0),
2810 column_divide_reg(ncolumns
));
2814 else if (flags
& (ALLBOX
| BOX
)) {
2815 print_single_hline(0);
2817 //printfs(".mk %1\n", row_top_reg(0));
2820 void table::do_bottom()
2822 // print stuff after last row
2823 for (stuff
*p
= stuff_list
; p
; p
= p
->next
)
2824 if (p
->row
> nrows
- 1)
2826 if (!(flags
& NOKEEP
))
2827 prints("." RELEASE_MACRO_NAME
"\n");
2828 printfs(".mk %1\n", row_top_reg(nrows
));
2829 prints(".nr " NEED_BOTTOM_RULE_REG
" 1\n"
2832 if (!(flags
& NOKEEP
) && (flags
& (BOX
| DOUBLEBOX
| ALLBOX
)))
2833 prints("." TABLE_RELEASE_MACRO_NAME
"\n");
2834 if (flags
& DOUBLEBOX
)
2835 prints(".sp " DOUBLE_LINE_SEP
"\n");
2836 prints("." RESET_MACRO_NAME
"\n"
2838 ".cp \\n(" COMPATIBLE_REG
"\n");
2841 int table::get_nrows()
2846 const char *last_filename
= 0;
2848 void set_troff_location(const char *fn
, int ln
)
2850 if (!location_force_filename
&& last_filename
!= 0
2851 && strcmp(fn
, last_filename
) == 0)
2852 printfs(".lf %1\n", as_string(ln
));
2854 printfs(".lf %1 %2\n", as_string(ln
), fn
);
2856 location_force_filename
= 0;
2860 void printfs(const char *s
, const string
&arg1
, const string
&arg2
,
2861 const string
&arg3
, const string
&arg4
, const string
&arg5
)
2865 while ((c
= *s
++) != '\0') {