2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000, 2003, 2004, 2007 - 2009
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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 2, or (at your option) any later
13 * This 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
18 * You should have received a copy of the GNU General Public License along
19 * with groff; see the file COPYING. If not, write to the Free Software
20 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "tbl-config.h"
28 #define BAR_HEIGHT ".25m"
29 #define DOUBLE_LINE_SEP "2p"
30 #define HALF_DOUBLE_LINE_SEP "1p"
32 #define BODY_DEPTH ".25m"
34 const int DEFAULT_COLUMN_SEPARATION
= 3;
36 #define DELIMITER_CHAR "\\[tbl]"
37 #define SEPARATION_FACTOR_REG PREFIX "sep"
38 #define BOTTOM_REG PREFIX "bot"
39 #define RESET_MACRO_NAME PREFIX "init"
40 #define LINESIZE_REG PREFIX "lps"
41 #define TOP_REG PREFIX "top"
42 #define CURRENT_ROW_REG PREFIX "crow"
43 #define LAST_PASSED_ROW_REG PREFIX "passed"
44 #define TRANSPARENT_STRING_NAME PREFIX "trans"
45 #define QUOTE_STRING_NAME PREFIX "quote"
46 #define SECTION_DIVERSION_NAME PREFIX "section"
47 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
48 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
49 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
50 #define KEEP_MACRO_NAME PREFIX "keep"
51 #define RELEASE_MACRO_NAME PREFIX "release"
52 #define SAVED_FONT_REG PREFIX "fnt"
53 #define SAVED_SIZE_REG PREFIX "sz"
54 #define SAVED_FILL_REG PREFIX "fll"
55 #define SAVED_INDENT_REG PREFIX "ind"
56 #define SAVED_CENTER_REG PREFIX "cent"
57 #define TABLE_DIVERSION_NAME PREFIX "table"
58 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
59 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
60 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
61 #define NEEDED_REG PREFIX "needed"
62 #define REPEATED_MARK_MACRO PREFIX "rmk"
63 #define REPEATED_VPT_MACRO PREFIX "rvpt"
64 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
65 #define SAVED_DN_REG PREFIX "dn"
67 // this must be one character
68 #define COMPATIBLE_REG PREFIX "c"
70 #define EXPAND_REG PREFIX "expand"
72 #define LEADER_REG PREFIX LEADER
74 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
75 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
76 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
77 #define SPAN_WIDTH_PREFIX PREFIX "w"
78 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
79 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
80 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
81 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
82 #define ROW_START_PREFIX PREFIX "rs"
83 #define COLUMN_START_PREFIX PREFIX "cl"
84 #define COLUMN_END_PREFIX PREFIX "ce"
85 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
86 #define ROW_TOP_PREFIX PREFIX "rt"
88 string
block_width_reg(int, int);
89 string
block_diversion_name(int, int);
90 string
block_height_reg(int, int);
91 string
span_width_reg(int, int);
92 string
span_left_numeric_width_reg(int, int);
93 string
span_right_numeric_width_reg(int, int);
94 string
span_alphabetic_width_reg(int, int);
95 string
column_separation_reg(int);
96 string
row_start_reg(int);
97 string
column_start_reg(int);
98 string
column_end_reg(int);
99 string
column_divide_reg(int);
100 string
row_top_reg(int);
102 void set_inline_modifier(const entry_modifier
*);
103 void restore_inline_modifier(const entry_modifier
*);
104 void set_modifier(const entry_modifier
*);
105 int find_decimal_point(const char *, char, const char *);
107 string an_empty_string
;
108 int location_force_filename
= 0;
110 void printfs(const char *,
111 const string
&arg1
= an_empty_string
,
112 const string
&arg2
= an_empty_string
,
113 const string
&arg3
= an_empty_string
,
114 const string
&arg4
= an_empty_string
,
115 const string
&arg5
= an_empty_string
);
117 void prints(const string
&);
119 inline void prints(char c
)
124 inline void prints(const char *s
)
129 void prints(const string
&s
)
132 fwrite(s
.contents(), 1, s
.length(), stdout
);
135 struct horizontal_span
{
136 horizontal_span
*next
;
139 horizontal_span(int, int, horizontal_span
*);
142 class single_line_entry
;
143 class double_line_entry
;
152 const char *input_filename
;
160 const entry_modifier
*mod
;
164 table_entry(const table
*, const entry_modifier
*);
165 virtual ~table_entry();
166 virtual int divert(int, const string
*, int *, int);
167 virtual void do_width();
168 virtual void do_depth();
169 virtual void print() = 0;
170 virtual void position_vertically() = 0;
171 virtual single_line_entry
*to_single_line_entry();
172 virtual double_line_entry
*to_double_line_entry();
173 virtual simple_entry
*to_simple_entry();
174 virtual int line_type();
175 virtual void note_double_vrule_on_right(int);
176 virtual void note_double_vrule_on_left(int);
183 simple_entry(const table
*, const entry_modifier
*);
185 void position_vertically();
186 simple_entry
*to_simple_entry();
187 virtual void add_tab();
188 virtual void simple_print(int);
192 : public simple_entry
195 empty_entry(const table
*, const entry_modifier
*);
200 : public simple_entry
205 void print_contents();
208 text_entry(const table
*, const entry_modifier
*, char *);
212 void text_entry::print_contents()
214 set_inline_modifier(mod
);
216 restore_inline_modifier(mod
);
219 class repeated_char_entry
223 repeated_char_entry(const table
*, const entry_modifier
*, char *);
224 void simple_print(int);
227 class simple_text_entry
231 simple_text_entry(const table
*, const entry_modifier
*, char *);
235 class left_text_entry
236 : public simple_text_entry
239 left_text_entry(const table
*, const entry_modifier
*, char *);
240 void simple_print(int);
244 class right_text_entry
245 : public simple_text_entry
248 right_text_entry(const table
*, const entry_modifier
*, char *);
249 void simple_print(int);
253 class center_text_entry
254 : public simple_text_entry
257 center_text_entry(const table
*, const entry_modifier
*, char *);
258 void simple_print(int);
262 class numeric_text_entry
268 numeric_text_entry(const table
*, const entry_modifier
*, char *, int);
270 void simple_print(int);
273 class alphabetic_text_entry
277 alphabetic_text_entry(const table
*, const entry_modifier
*, char *);
279 void simple_print(int);
284 : public simple_entry
287 char double_vrule_on_right
;
288 char double_vrule_on_left
;
291 line_entry(const table
*, const entry_modifier
*);
292 void note_double_vrule_on_right(int);
293 void note_double_vrule_on_left(int);
294 void simple_print(int) = 0;
297 class single_line_entry
301 single_line_entry(const table
*, const entry_modifier
*);
302 void simple_print(int);
303 single_line_entry
*to_single_line_entry();
307 class double_line_entry
311 double_line_entry(const table
*, const entry_modifier
*);
312 void simple_print(int);
313 double_line_entry
*to_double_line_entry();
317 class short_line_entry
318 : public simple_entry
321 short_line_entry(const table
*, const entry_modifier
*);
322 void simple_print(int);
326 class short_double_line_entry
327 : public simple_entry
330 short_double_line_entry(const table
*, const entry_modifier
*);
331 void simple_print(int);
341 void do_divert(int, int, const string
*, int *, int);
344 block_entry(const table
*, const entry_modifier
*, char *);
346 int divert(int, const string
*, int *, int);
348 void position_vertically();
352 class left_block_entry
356 left_block_entry(const table
*, const entry_modifier
*, char *);
360 class right_block_entry
364 right_block_entry(const table
*, const entry_modifier
*, char *);
368 class center_block_entry
372 center_block_entry(const table
*, const entry_modifier
*, char *);
376 class alphabetic_block_entry
380 alphabetic_block_entry(const table
*, const entry_modifier
*, char *);
382 int divert(int, const string
*, int *, int);
385 table_entry::table_entry(const table
*p
, const entry_modifier
*m
)
386 : next(0), input_lineno(-1), input_filename(0),
387 start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p
), mod(m
)
391 table_entry::~table_entry()
395 int table_entry::divert(int, const string
*, int *, int)
400 void table_entry::do_width()
404 single_line_entry
*table_entry::to_single_line_entry()
409 double_line_entry
*table_entry::to_double_line_entry()
414 simple_entry
*table_entry::to_simple_entry()
419 void table_entry::do_depth()
423 void table_entry::set_location()
425 set_troff_location(input_filename
, input_lineno
);
428 int table_entry::line_type()
433 void table_entry::note_double_vrule_on_right(int)
437 void table_entry::note_double_vrule_on_left(int)
441 simple_entry::simple_entry(const table
*p
, const entry_modifier
*m
)
446 void simple_entry::add_tab()
451 void simple_entry::simple_print(int)
456 void simple_entry::position_vertically()
458 if (start_row
!= end_row
)
459 switch (mod
->vertical_alignment
) {
460 case entry_modifier::TOP
:
461 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
463 case entry_modifier::CENTER
:
464 // Peform the motion in two stages so that the center is rounded
465 // vertically upwards even if net vertical motion is upwards.
466 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
467 printfs(".sp \\n[" BOTTOM_REG
"]u-\\n[%1]u-1v/2u\n",
468 row_start_reg(start_row
));
470 case entry_modifier::BOTTOM
:
471 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG
"]u-\\n[%1]u-1v\n",
472 row_start_reg(start_row
));
479 void simple_entry::print()
490 simple_entry
*simple_entry::to_simple_entry()
495 empty_entry::empty_entry(const table
*p
, const entry_modifier
*m
)
500 int empty_entry::line_type()
505 text_entry::text_entry(const table
*p
, const entry_modifier
*m
, char *s
)
506 : simple_entry(p
, m
), contents(s
)
510 text_entry::~text_entry()
515 repeated_char_entry::repeated_char_entry(const table
*p
,
516 const entry_modifier
*m
, char *s
)
517 : text_entry(p
, m
, s
)
521 void repeated_char_entry::simple_print(int)
523 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
524 set_inline_modifier(mod
);
525 printfs("\\l" DELIMITER_CHAR
"\\n[%1]u\\&",
526 span_width_reg(start_col
, end_col
));
528 prints(DELIMITER_CHAR
);
529 restore_inline_modifier(mod
);
532 simple_text_entry::simple_text_entry(const table
*p
,
533 const entry_modifier
*m
, char *s
)
534 : text_entry(p
, m
, s
)
538 void simple_text_entry::do_width()
541 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
542 span_width_reg(start_col
, end_col
));
544 prints(DELIMITER_CHAR
"\n");
547 left_text_entry::left_text_entry(const table
*p
,
548 const entry_modifier
*m
, char *s
)
549 : simple_text_entry(p
, m
, s
)
553 void left_text_entry::simple_print(int)
555 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
559 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
561 void left_text_entry::add_tab()
563 printfs(" \\n[%1]u", column_end_reg(end_col
));
566 right_text_entry::right_text_entry(const table
*p
,
567 const entry_modifier
*m
, char *s
)
568 : simple_text_entry(p
, m
, s
)
572 void right_text_entry::simple_print(int)
574 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
580 void right_text_entry::add_tab()
582 printfs(" \\n[%1]u", column_end_reg(end_col
));
585 center_text_entry::center_text_entry(const table
*p
,
586 const entry_modifier
*m
, char *s
)
587 : simple_text_entry(p
, m
, s
)
591 void center_text_entry::simple_print(int)
593 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
599 void center_text_entry::add_tab()
601 printfs(" \\n[%1]u", column_end_reg(end_col
));
604 numeric_text_entry::numeric_text_entry(const table
*p
,
605 const entry_modifier
*m
,
607 : text_entry(p
, m
, s
), dot_pos(pos
)
611 void numeric_text_entry::do_width()
615 printfs(".nr %1 0\\w" DELIMITER_CHAR
,
616 block_width_reg(start_row
, start_col
));
617 set_inline_modifier(mod
);
618 for (int i
= 0; i
< dot_pos
; i
++)
620 restore_inline_modifier(mod
);
621 prints(DELIMITER_CHAR
"\n");
622 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
623 span_left_numeric_width_reg(start_col
, end_col
),
624 block_width_reg(start_row
, start_col
));
627 printfs(".nr %1 0\n", block_width_reg(start_row
, start_col
));
628 if (contents
[dot_pos
] != '\0') {
630 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
631 span_right_numeric_width_reg(start_col
, end_col
));
632 set_inline_modifier(mod
);
633 prints(contents
+ dot_pos
);
634 restore_inline_modifier(mod
);
635 prints(DELIMITER_CHAR
"\n");
639 void numeric_text_entry::simple_print(int)
641 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
642 span_width_reg(start_col
, end_col
),
643 span_left_numeric_width_reg(start_col
, end_col
),
644 span_right_numeric_width_reg(start_col
, end_col
),
645 column_start_reg(start_col
),
646 block_width_reg(start_row
, start_col
));
650 alphabetic_text_entry::alphabetic_text_entry(const table
*p
,
651 const entry_modifier
*m
,
653 : text_entry(p
, m
, s
)
657 void alphabetic_text_entry::do_width()
660 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
661 span_alphabetic_width_reg(start_col
, end_col
));
663 prints(DELIMITER_CHAR
"\n");
666 void alphabetic_text_entry::simple_print(int)
668 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
669 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
670 span_width_reg(start_col
, end_col
),
671 span_alphabetic_width_reg(start_col
, end_col
));
675 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
677 void alphabetic_text_entry::add_tab()
679 printfs(" \\n[%1]u", column_end_reg(end_col
));
682 block_entry::block_entry(const table
*p
, const entry_modifier
*m
, char *s
)
683 : table_entry(p
, m
), contents(s
)
687 block_entry::~block_entry()
692 void block_entry::position_vertically()
694 if (start_row
!= end_row
)
695 switch(mod
->vertical_alignment
) {
696 case entry_modifier::TOP
:
697 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
699 case entry_modifier::CENTER
:
700 // Peform the motion in two stages so that the center is rounded
701 // vertically upwards even if net vertical motion is upwards.
702 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
703 printfs(".sp \\n[" BOTTOM_REG
"]u-\\n[%1]u-\\n[%2]u/2u\n",
704 row_start_reg(start_row
),
705 block_height_reg(start_row
, start_col
));
707 case entry_modifier::BOTTOM
:
708 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG
"]u-\\n[%1]u-\\n[%2]u\n",
709 row_start_reg(start_row
),
710 block_height_reg(start_row
, start_col
));
716 prints(".sp -.5v\n");
719 int block_entry::divert(int ncols
, const string
*mw
, int *sep
, int do_expand
)
721 do_divert(0, ncols
, mw
, sep
, do_expand
);
725 void block_entry::do_divert(int alphabetic
, int ncols
, const string
*mw
,
726 int *sep
, int do_expand
)
729 for (i
= start_col
; i
<= end_col
; i
++)
730 if (parent
->expand
[i
])
740 printfs(".di %1\n", block_diversion_name(start_row
, start_col
));
741 prints(".if \\n[" SAVED_FILL_REG
"] .fi\n"
744 for (i
= start_col
; i
<= end_col
; i
++)
745 if (mw
[i
].empty() && !parent
->expand
[i
])
748 // Every column spanned by this entry has a minimum width.
749 for (int j
= start_col
; j
<= end_col
; j
++) {
752 printfs("+%1n", as_string(sep
[j
- 1]));
755 if (parent
->expand
[j
])
756 prints("\\n[" EXPAND_REG
"]u");
758 printfs("(n;%1)", mw
[j
]);
760 printfs(">?\\n[%1]u", span_width_reg(start_col
, end_col
));
763 // Assign each column with a block entry 1/(n+1) of the line
764 // width, where n is the column count.
765 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
766 span_width_reg(start_col
, end_col
),
767 as_string(end_col
- start_col
+ 1),
768 as_string(ncols
+ 1));
772 prints(".cp \\n(" COMPATIBLE_REG
"\n");
776 prints(".br\n.di\n.cp 0\n");
777 if (!mod
->zero_width
) {
779 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
780 span_width_reg(start_col
, end_col
));
781 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
782 span_alphabetic_width_reg(start_col
, end_col
));
785 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
786 span_width_reg(start_col
, end_col
));
788 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row
, start_col
));
789 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row
, start_col
));
790 prints("." RESET_MACRO_NAME
"\n"
791 ".in \\n[" SAVED_INDENT_REG
"]u\n"
793 // the block might have contained .lf commands
794 location_force_filename
= 1;
797 void block_entry::do_depth()
799 printfs(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?(\\n[%1]+\\n[%2])\n",
800 row_start_reg(start_row
),
801 block_height_reg(start_row
, start_col
));
804 left_block_entry::left_block_entry(const table
*p
,
805 const entry_modifier
*m
, char *s
)
806 : block_entry(p
, m
, s
)
810 void left_block_entry::print()
812 printfs(".in +\\n[%1]u\n", column_start_reg(start_col
));
813 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
817 right_block_entry::right_block_entry(const table
*p
,
818 const entry_modifier
*m
, char *s
)
819 : block_entry(p
, m
, s
)
823 void right_block_entry::print()
825 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
826 column_start_reg(start_col
),
827 span_width_reg(start_col
, end_col
),
828 block_width_reg(start_row
, start_col
));
829 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
833 center_block_entry::center_block_entry(const table
*p
,
834 const entry_modifier
*m
, char *s
)
835 : block_entry(p
, m
, s
)
839 void center_block_entry::print()
841 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
842 column_start_reg(start_col
),
843 span_width_reg(start_col
, end_col
),
844 block_width_reg(start_row
, start_col
));
845 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
849 alphabetic_block_entry::alphabetic_block_entry(const table
*p
,
850 const entry_modifier
*m
,
852 : block_entry(p
, m
, s
)
856 int alphabetic_block_entry::divert(int ncols
, const string
*mw
, int *sep
,
859 do_divert(1, ncols
, mw
, sep
, do_expand
);
863 void alphabetic_block_entry::print()
865 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
866 column_start_reg(start_col
),
867 span_width_reg(start_col
, end_col
),
868 span_alphabetic_width_reg(start_col
, end_col
));
869 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
873 line_entry::line_entry(const table
*p
, const entry_modifier
*m
)
874 : simple_entry(p
, m
), double_vrule_on_right(0), double_vrule_on_left(0)
878 void line_entry::note_double_vrule_on_right(int is_corner
)
880 double_vrule_on_right
= is_corner
? 1 : 2;
883 void line_entry::note_double_vrule_on_left(int is_corner
)
885 double_vrule_on_left
= is_corner
? 1 : 2;
888 single_line_entry::single_line_entry(const table
*p
, const entry_modifier
*m
)
893 int single_line_entry::line_type()
898 void single_line_entry::simple_print(int dont_move
)
900 printfs("\\h'|\\n[%1]u",
901 column_divide_reg(start_col
));
902 if (double_vrule_on_left
) {
903 prints(double_vrule_on_left
== 1 ? "-" : "+");
904 prints(HALF_DOUBLE_LINE_SEP
);
908 prints("\\v'-" BAR_HEIGHT
"'");
909 printfs("\\s[\\n[" LINESIZE_REG
"]]" "\\D'l |\\n[%1]u",
910 column_divide_reg(end_col
+1));
911 if (double_vrule_on_right
) {
912 prints(double_vrule_on_left
== 1 ? "+" : "-");
913 prints(HALF_DOUBLE_LINE_SEP
);
917 prints("\\v'" BAR_HEIGHT
"'");
920 single_line_entry
*single_line_entry::to_single_line_entry()
925 double_line_entry::double_line_entry(const table
*p
, const entry_modifier
*m
)
930 int double_line_entry::line_type()
935 void double_line_entry::simple_print(int dont_move
)
938 prints("\\v'-" BAR_HEIGHT
"'");
939 printfs("\\h'|\\n[%1]u",
940 column_divide_reg(start_col
));
941 if (double_vrule_on_left
) {
942 prints(double_vrule_on_left
== 1 ? "-" : "+");
943 prints(HALF_DOUBLE_LINE_SEP
);
946 printfs("\\v'-" HALF_DOUBLE_LINE_SEP
"'"
947 "\\s[\\n[" LINESIZE_REG
"]]"
949 column_divide_reg(end_col
+1));
950 if (double_vrule_on_right
)
951 prints("-" HALF_DOUBLE_LINE_SEP
);
953 printfs("\\v'" DOUBLE_LINE_SEP
"'"
955 column_divide_reg(start_col
));
956 if (double_vrule_on_right
) {
957 prints(double_vrule_on_left
== 1 ? "+" : "-");
958 prints(HALF_DOUBLE_LINE_SEP
);
962 "\\v'-" HALF_DOUBLE_LINE_SEP
"'");
964 prints("\\v'" BAR_HEIGHT
"'");
967 double_line_entry
*double_line_entry::to_double_line_entry()
972 short_line_entry::short_line_entry(const table
*p
, const entry_modifier
*m
)
977 int short_line_entry::line_type()
982 void short_line_entry::simple_print(int dont_move
)
987 prints("\\v'-" BAR_HEIGHT
"'");
988 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
989 printfs("\\s[\\n[" LINESIZE_REG
"]]"
992 span_width_reg(start_col
, end_col
));
994 prints("\\v'" BAR_HEIGHT
"'");
999 short_double_line_entry::short_double_line_entry(const table
*p
,
1000 const entry_modifier
*m
)
1001 : simple_entry(p
, m
)
1005 int short_double_line_entry::line_type()
1010 void short_double_line_entry::simple_print(int dont_move
)
1013 prints("\\v'-.5v'");
1015 prints("\\v'-" BAR_HEIGHT
"'");
1016 printfs("\\h'|\\n[%2]u'"
1017 "\\v'-" HALF_DOUBLE_LINE_SEP
"'"
1018 "\\s[\\n[" LINESIZE_REG
"]]"
1020 "\\v'" DOUBLE_LINE_SEP
"'"
1021 "\\D'l |\\n[%2]u 0'"
1023 "\\v'-" HALF_DOUBLE_LINE_SEP
"'",
1024 span_width_reg(start_col
, end_col
),
1025 column_start_reg(start_col
));
1027 prints("\\v'" BAR_HEIGHT
"'");
1032 void set_modifier(const entry_modifier
*m
)
1034 if (!m
->font
.empty())
1035 printfs(".ft %1\n", m
->font
);
1036 if (m
->point_size
.val
!= 0) {
1038 if (m
->point_size
.inc
> 0)
1040 else if (m
->point_size
.inc
< 0)
1042 printfs("%1\n", as_string(m
->point_size
.val
));
1044 if (m
->vertical_spacing
.val
!= 0) {
1046 if (m
->vertical_spacing
.inc
> 0)
1048 else if (m
->vertical_spacing
.inc
< 0)
1050 printfs("%1\n", as_string(m
->vertical_spacing
.val
));
1052 if (!m
->macro
.empty())
1053 printfs(".%1\n", m
->macro
);
1056 void set_inline_modifier(const entry_modifier
*m
)
1058 if (!m
->font
.empty())
1059 printfs("\\f[%1]", m
->font
);
1060 if (m
->point_size
.val
!= 0) {
1062 if (m
->point_size
.inc
> 0)
1064 else if (m
->point_size
.inc
< 0)
1066 printfs("%1]", as_string(m
->point_size
.val
));
1069 prints("\\v'-.5v'");
1072 void restore_inline_modifier(const entry_modifier
*m
)
1074 if (!m
->font
.empty())
1075 prints("\\f[\\n[" SAVED_FONT_REG
"]]");
1076 if (m
->point_size
.val
!= 0)
1077 prints("\\s[\\n[" SAVED_SIZE_REG
"]]");
1086 int row
; // occurs before row `row'
1087 char printed
; // has it been printed?
1090 virtual void print(table
*) = 0;
1092 virtual int is_single_line() { return 0; };
1093 virtual int is_double_line() { return 0; };
1096 stuff::stuff(int r
) : next(0), row(r
), printed(0)
1109 const char *filename
;
1112 text_stuff(const string
&, int, const char *, int);
1114 void print(table
*);
1117 text_stuff::text_stuff(const string
&s
, int r
, const char *fn
, int ln
)
1118 : stuff(r
), contents(s
), filename(fn
), lineno(ln
)
1122 text_stuff::~text_stuff()
1126 void text_stuff::print(table
*)
1129 prints(".cp \\n(" COMPATIBLE_REG
"\n");
1130 set_troff_location(filename
, lineno
);
1133 location_force_filename
= 1; // it might have been a .lf command
1136 class single_hline_stuff
1140 single_hline_stuff(int);
1141 void print(table
*);
1142 int is_single_line();
1145 single_hline_stuff::single_hline_stuff(int r
) : stuff(r
)
1149 void single_hline_stuff::print(table
*tbl
)
1152 tbl
->print_single_hline(row
);
1155 int single_hline_stuff::is_single_line()
1160 class double_hline_stuff
1164 double_hline_stuff(int);
1165 void print(table
*);
1166 int is_double_line();
1169 double_hline_stuff::double_hline_stuff(int r
) : stuff(r
)
1173 void double_hline_stuff::print(table
*tbl
)
1176 tbl
->print_double_hline(row
);
1179 int double_hline_stuff::is_double_line()
1187 vertical_rule
*next
;
1195 vertical_rule(int, int, int, int, vertical_rule
*);
1197 void contribute_to_bottom_macro(table
*);
1201 vertical_rule::vertical_rule(int sr
, int er
, int c
, int dbl
,
1203 : next(p
), start_row(sr
), end_row(er
), col(c
), is_double(dbl
)
1207 vertical_rule::~vertical_rule()
1211 void vertical_rule::contribute_to_bottom_macro(table
*tbl
)
1213 printfs(".if \\n[" CURRENT_ROW_REG
"]>=%1",
1214 as_string(start_row
));
1215 if (end_row
!= tbl
->get_nrows() - 1)
1216 printfs("&(\\n[" CURRENT_ROW_REG
"]<%1)",
1217 as_string(end_row
));
1219 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG
"] .nr %2 \\n[#T]\n",
1220 as_string(start_row
),
1221 row_top_reg(start_row
));
1222 const char *offset_table
[3];
1224 offset_table
[0] = "-" HALF_DOUBLE_LINE_SEP
;
1225 offset_table
[1] = "+" HALF_DOUBLE_LINE_SEP
;
1226 offset_table
[2] = 0;
1229 offset_table
[0] = "";
1230 offset_table
[1] = 0;
1232 for (const char **offsetp
= offset_table
; *offsetp
; offsetp
++) {
1235 if (!bot_adjust
.empty())
1236 printfs("+%1", bot_adjust
);
1238 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG
"]]\\D'l 0 |\\n[%2]u-1v",
1239 column_divide_reg(col
),
1240 row_top_reg(start_row
),
1242 if (!bot_adjust
.empty())
1243 printfs("-(%1)", bot_adjust
);
1244 // don't perform the top adjustment if the top is actually #T
1245 if (!top_adjust
.empty())
1246 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG
"]))",
1248 as_string(start_row
));
1254 void vertical_rule::print()
1256 printfs("\\*[" TRANSPARENT_STRING_NAME
"]"
1257 ".if %1<=\\*[" QUOTE_STRING_NAME
"]\\n[" LAST_PASSED_ROW_REG
"] "
1258 ".nr %2 \\*[" QUOTE_STRING_NAME
"]\\n[#T]\n",
1259 as_string(start_row
),
1260 row_top_reg(start_row
));
1261 const char *offset_table
[3];
1263 offset_table
[0] = "-" HALF_DOUBLE_LINE_SEP
;
1264 offset_table
[1] = "+" HALF_DOUBLE_LINE_SEP
;
1265 offset_table
[2] = 0;
1268 offset_table
[0] = "";
1269 offset_table
[1] = 0;
1271 for (const char **offsetp
= offset_table
; *offsetp
; offsetp
++) {
1272 prints("\\*[" TRANSPARENT_STRING_NAME
"].sp -1\n"
1273 "\\*[" TRANSPARENT_STRING_NAME
"]\\v'" BODY_DEPTH
);
1274 if (!bot_adjust
.empty())
1275 printfs("+%1", bot_adjust
);
1277 printfs("\\h'\\n[%1]u%3'"
1278 "\\s[\\n[" LINESIZE_REG
"]]"
1279 "\\D'l 0 |\\*[" QUOTE_STRING_NAME
"]\\n[%2]u-1v",
1280 column_divide_reg(col
),
1281 row_top_reg(start_row
),
1283 if (!bot_adjust
.empty())
1284 printfs("-(%1)", bot_adjust
);
1285 // don't perform the top adjustment if the top is actually #T
1286 if (!top_adjust
.empty())
1287 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME
"]\\n["
1288 LAST_PASSED_ROW_REG
"]))",
1290 as_string(start_row
));
1296 table::table(int nc
, unsigned f
, int ls
, char dpc
)
1297 : nrows(0), ncolumns(nc
), linesize(ls
), decimal_point_char(dpc
),
1298 vrule_list(0), stuff_list(0), span_list(0),
1299 entry_list(0), entry_list_tailp(&entry_list
), entry(0),
1300 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1301 total_separation(0), allocated_rows(0), flags(f
)
1303 minimum_width
= new string
[ncolumns
];
1304 column_separation
= ncolumns
> 1 ? new int[ncolumns
- 1] : 0;
1305 equal
= new char[ncolumns
];
1306 expand
= new char[ncolumns
];
1308 for (i
= 0; i
< ncolumns
; i
++) {
1312 for (i
= 0; i
< ncolumns
- 1; i
++)
1313 column_separation
[i
] = DEFAULT_COLUMN_SEPARATION
;
1314 delim
[0] = delim
[1] = '\0';
1319 for (int i
= 0; i
< nrows
; i
++) {
1325 while (entry_list
) {
1326 table_entry
*tem
= entry_list
;
1327 entry_list
= entry_list
->next
;
1330 ad_delete(ncolumns
) minimum_width
;
1331 a_delete column_separation
;
1334 while (stuff_list
) {
1335 stuff
*tem
= stuff_list
;
1336 stuff_list
= stuff_list
->next
;
1339 while (vrule_list
) {
1340 vertical_rule
*tem
= vrule_list
;
1341 vrule_list
= vrule_list
->next
;
1344 a_delete row_is_all_lines
;
1346 horizontal_span
*tem
= span_list
;
1347 span_list
= span_list
->next
;
1352 void table::set_delim(char c1
, char c2
)
1358 void table::set_minimum_width(int c
, const string
&w
)
1360 assert(c
>= 0 && c
< ncolumns
);
1361 minimum_width
[c
] = w
;
1364 void table::set_column_separation(int c
, int n
)
1366 assert(c
>= 0 && c
< ncolumns
- 1);
1367 column_separation
[c
] = n
;
1370 void table::set_equal_column(int c
)
1372 assert(c
>= 0 && c
< ncolumns
);
1376 void table::set_expand_column(int c
)
1378 assert(c
>= 0 && c
< ncolumns
);
1382 void table::add_stuff(stuff
*p
)
1385 for (pp
= &stuff_list
; *pp
; pp
= &(*pp
)->next
)
1390 void table::add_text_line(int r
, const string
&s
, const char *filename
,
1393 add_stuff(new text_stuff(s
, r
, filename
, lineno
));
1396 void table::add_single_hline(int r
)
1398 add_stuff(new single_hline_stuff(r
));
1401 void table::add_double_hline(int r
)
1403 add_stuff(new double_hline_stuff(r
));
1406 void table::allocate(int r
)
1409 typedef table_entry
**PPtable_entry
; // work around g++ 1.36.1 bug
1410 if (r
>= allocated_rows
) {
1411 if (allocated_rows
== 0) {
1412 allocated_rows
= 16;
1413 if (allocated_rows
<= r
)
1414 allocated_rows
= r
+ 1;
1415 entry
= new PPtable_entry
[allocated_rows
];
1416 vline
= new char*[allocated_rows
];
1419 table_entry
***old_entry
= entry
;
1420 int old_allocated_rows
= allocated_rows
;
1421 allocated_rows
*= 2;
1422 if (allocated_rows
<= r
)
1423 allocated_rows
= r
+ 1;
1424 entry
= new PPtable_entry
[allocated_rows
];
1425 memcpy(entry
, old_entry
, sizeof(table_entry
**)*old_allocated_rows
);
1427 char **old_vline
= vline
;
1428 vline
= new char*[allocated_rows
];
1429 memcpy(vline
, old_vline
, sizeof(char*)*old_allocated_rows
);
1433 assert(allocated_rows
> r
);
1434 while (nrows
<= r
) {
1435 entry
[nrows
] = new table_entry
*[ncolumns
];
1437 for (i
= 0; i
< ncolumns
; i
++)
1438 entry
[nrows
][i
] = 0;
1439 vline
[nrows
] = new char[ncolumns
+1];
1440 for (i
= 0; i
< ncolumns
+1; i
++)
1441 vline
[nrows
][i
] = 0;
1447 void table::do_hspan(int r
, int c
)
1449 assert(r
>= 0 && c
>= 0 && r
< nrows
&& c
< ncolumns
);
1451 error("first column cannot be horizontally spanned");
1454 table_entry
*e
= entry
[r
][c
];
1456 assert(e
->start_row
<= r
&& r
<= e
->end_row
1457 && e
->start_col
<= c
&& c
<= e
->end_col
1458 && e
->end_row
- e
->start_row
> 0
1459 && e
->end_col
- e
->start_col
> 0);
1463 // e can be 0 if we had an empty entry or an error
1466 if (e
->start_row
!= r
) {
1470 error("impossible horizontal span at row %1, column %2", r
+ 1, c
+ 1);
1478 void table::do_vspan(int r
, int c
)
1480 assert(r
>= 0 && c
>= 0 && r
< nrows
&& c
< ncolumns
);
1482 error("first row cannot be vertically spanned");
1485 table_entry
*e
= entry
[r
][c
];
1487 assert(e
->start_row
<= r
&& r
<= e
->end_row
1488 && e
->start_col
<= c
&& c
<= e
->end_col
1489 && e
->end_row
- e
->start_row
> 0
1490 && e
->end_col
- e
->start_col
> 0);
1494 // e can be 0 if we had an empty entry or an error
1497 if (e
->start_col
!= c
) {
1500 error("impossible vertical span at row %1, column %2", r
+ 1, c
+ 1);
1503 for (int i
= c
; i
<= e
->end_col
; i
++) {
1504 assert(entry
[r
][i
] == 0);
1511 int find_decimal_point(const char *s
, char decimal_point_char
,
1514 if (s
== 0 || *s
== '\0')
1517 int in_delim
= 0; // is p within eqn delimiters?
1518 // tbl recognises \& even within eqn delimiters; I don't
1519 for (p
= s
; *p
; p
++)
1524 else if (*p
== delim
[0])
1526 else if (p
[0] == '\\' && p
[1] == '&')
1528 int possible_pos
= -1;
1530 for (p
= s
; *p
; p
++)
1535 else if (*p
== delim
[0])
1537 else if (p
[0] == decimal_point_char
&& csdigit(p
[1]))
1538 possible_pos
= p
- s
;
1539 if (possible_pos
>= 0)
1540 return possible_pos
;
1542 for (p
= s
; *p
; p
++)
1547 else if (*p
== delim
[0])
1549 else if (csdigit(*p
))
1550 possible_pos
= p
+ 1 - s
;
1551 return possible_pos
;
1554 void table::add_entry(int r
, int c
, const string
&str
, const entry_format
*f
,
1555 const char *fn
, int ln
)
1560 e
= new short_line_entry(this, f
);
1562 else if (str
== "\\=") {
1563 e
= new short_double_line_entry(this, f
);
1565 else if (str
== "_") {
1566 single_line_entry
*lefte
;
1567 if (c
> 0 && entry
[r
][c
-1] != 0 &&
1568 (lefte
= entry
[r
][c
-1]->to_single_line_entry()) != 0
1569 && lefte
->start_row
== r
1570 && lefte
->mod
->stagger
== f
->stagger
) {
1572 entry
[r
][c
] = lefte
;
1575 e
= new single_line_entry(this, f
);
1577 else if (str
== "=") {
1578 double_line_entry
*lefte
;
1579 if (c
> 0 && entry
[r
][c
-1] != 0 &&
1580 (lefte
= entry
[r
][c
-1]->to_double_line_entry()) != 0
1581 && lefte
->start_row
== r
1582 && lefte
->mod
->stagger
== f
->stagger
) {
1584 entry
[r
][c
] = lefte
;
1587 e
= new double_line_entry(this, f
);
1589 else if (str
== "\\^") {
1592 else if (str
.length() > 2 && str
[0] == '\\' && str
[1] == 'R') {
1593 if (str
.search('\n') >= 0)
1594 error_with_file_and_line(fn
, ln
, "bad repeated character");
1596 char *s
= str
.substring(2, str
.length() - 2).extract();
1597 e
= new repeated_char_entry(this, f
, s
);
1601 int is_block
= str
.search('\n') >= 0;
1605 assert(str
.empty());
1612 e
= new left_block_entry(this, f
, s
);
1614 e
= new left_text_entry(this, f
, s
);
1617 e
= new empty_entry(this, f
);
1623 e
= new center_block_entry(this, f
, s
);
1625 e
= new center_text_entry(this, f
, s
);
1628 e
= new empty_entry(this, f
);
1634 e
= new right_block_entry(this, f
, s
);
1636 e
= new right_text_entry(this, f
, s
);
1639 e
= new empty_entry(this, f
);
1641 case FORMAT_NUMERIC
:
1645 error_with_file_and_line(fn
, ln
, "can't have numeric text block");
1646 e
= new left_block_entry(this, f
, s
);
1649 int pos
= find_decimal_point(s
, decimal_point_char
, delim
);
1651 e
= new center_text_entry(this, f
, s
);
1653 e
= new numeric_text_entry(this, f
, s
, pos
);
1657 e
= new empty_entry(this, f
);
1659 case FORMAT_ALPHABETIC
:
1663 e
= new alphabetic_block_entry(this, f
, s
);
1665 e
= new alphabetic_text_entry(this, f
, s
);
1668 e
= new empty_entry(this, f
);
1674 if (str
.length() != 0)
1675 error_with_file_and_line(fn
, ln
,
1676 "non-empty data entry for `_' format ignored");
1677 e
= new single_line_entry(this, f
);
1679 case FORMAT_DOUBLE_HLINE
:
1680 if (str
.length() != 0)
1681 error_with_file_and_line(fn
, ln
,
1682 "non-empty data entry for `=' format ignored");
1683 e
= new double_line_entry(this, f
);
1690 table_entry
*preve
= entry
[r
][c
];
1694 error_with_file_and_line(fn
, ln
, "row %1, column %2 already spanned",
1699 e
->input_lineno
= ln
;
1700 e
->input_filename
= fn
;
1701 e
->start_row
= e
->end_row
= r
;
1702 e
->start_col
= e
->end_col
= c
;
1703 *entry_list_tailp
= e
;
1704 entry_list_tailp
= &e
->next
;
1710 // add vertical lines for row r
1712 void table::add_vlines(int r
, const char *v
)
1715 for (int i
= 0; i
< ncolumns
+1; i
++)
1721 table_entry
*p
= entry_list
;
1724 for (i
= p
->start_row
; i
<= p
->end_row
; i
++)
1725 for (j
= p
->start_col
; j
<= p
->end_col
; j
++)
1726 assert(entry
[i
][j
] == p
);
1733 location_force_filename
= 1;
1736 determine_row_type();
1738 if (!(flags
& CENTER
))
1739 prints(".if \\n[" SAVED_CENTER_REG
"] \\{");
1740 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1741 ".nr " SAVED_INDENT_REG
" \\n[.i]\n");
1742 if (!(flags
& CENTER
))
1745 define_bottom_macro();
1747 for (int i
= 0; i
< nrows
; i
++)
1752 void table::determine_row_type()
1754 row_is_all_lines
= new char[nrows
];
1755 for (int i
= 0; i
< nrows
; i
++) {
1758 int had_non_line
= 0;
1759 for (int c
= 0; c
< ncolumns
; c
++) {
1760 table_entry
*e
= entry
[i
][c
];
1762 if (e
->start_row
== e
->end_row
) {
1763 int t
= e
->line_type();
1787 row_is_all_lines
[i
] = 0;
1788 else if (had_double
)
1789 row_is_all_lines
[i
] = 2;
1790 else if (had_single
)
1791 row_is_all_lines
[i
] = 1;
1793 row_is_all_lines
[i
] = 0;
1797 int table::count_expand_columns()
1800 for (int i
= 0; i
< ncolumns
; i
++)
1806 void table::init_output()
1808 prints(".nr " COMPATIBLE_REG
" \\n(.C\n"
1811 printfs(".nr " LINESIZE_REG
" %1\n", as_string(linesize
));
1813 prints(".nr " LINESIZE_REG
" \\n[.s]\n");
1814 if (!(flags
& CENTER
))
1815 prints(".nr " SAVED_CENTER_REG
" \\n[.ce]\n");
1816 if (compatible_flag
)
1817 prints(".ds " LEADER_REG
" \\a\n");
1818 prints(".de " RESET_MACRO_NAME
"\n"
1830 ".nr " SAVED_INDENT_REG
" \\n[.i]\n"
1831 ".nr " SAVED_FONT_REG
" \\n[.f]\n"
1832 ".nr " SAVED_SIZE_REG
" \\n[.s]\n"
1833 ".nr " SAVED_FILL_REG
" \\n[.u]\n"
1835 ".nr " CURRENT_ROW_REG
" 0-1\n"
1836 ".nr " LAST_PASSED_ROW_REG
" 0-1\n"
1837 ".nr " SECTION_DIVERSION_FLAG_REG
" 0\n"
1838 ".ds " TRANSPARENT_STRING_NAME
"\n"
1839 ".ds " QUOTE_STRING_NAME
"\n"
1840 ".nr " NEED_BOTTOM_RULE_REG
" 1\n"
1841 ".nr " SUPPRESS_BOTTOM_REG
" 0\n"
1843 ".de " REPEATED_MARK_MACRO
"\n"
1845 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO
" \"\\$1\"\n"
1847 ".de " REPEATED_VPT_MACRO
"\n"
1849 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO
" \"\\$1\"\n"
1851 if (!(flags
& NOKEEP
))
1852 prints(".de " KEEP_MACRO_NAME
"\n"
1853 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME
" \\\\\n"
1854 ".ds " TRANSPARENT_STRING_NAME
" \\!\n"
1855 ".di " SECTION_DIVERSION_NAME
"\n"
1856 ".nr " SECTION_DIVERSION_FLAG_REG
" 1\n"
1860 ".de " RELEASE_MACRO_NAME
"\n"
1861 ".if \\n[" SECTION_DIVERSION_FLAG_REG
"] \\{"
1863 ".in \\n[" SAVED_INDENT_REG
"]u\n"
1864 ".nr " SAVED_DN_REG
" \\n[dn]\n"
1865 ".ds " QUOTE_STRING_NAME
"\n"
1866 ".ds " TRANSPARENT_STRING_NAME
"\n"
1867 ".nr " SECTION_DIVERSION_FLAG_REG
" 0\n"
1868 ".if \\n[.t]<=\\n[dn] \\{"
1871 ".nr " SUPPRESS_BOTTOM_REG
" 1\n"
1873 ".nr " SUPPRESS_BOTTOM_REG
" 0\n"
1876 ".if \\n[.t]<=\\n[" SAVED_DN_REG
"] "
1877 /* Since we turn off traps, it won't get into an infinite loop
1878 when we try and print it; it will just go off the bottom of the
1880 ".tm warning: page \\n%: table text block will not fit on one page\n"
1883 "." SECTION_DIVERSION_NAME
"\n"
1885 ".rm " SECTION_DIVERSION_NAME
"\n"
1888 ".nr " TABLE_DIVERSION_FLAG_REG
" 0\n"
1889 ".de " TABLE_KEEP_MACRO_NAME
"\n"
1890 ".if '\\n[.z]'' \\{"
1891 ".di " TABLE_DIVERSION_NAME
"\n"
1892 ".nr " TABLE_DIVERSION_FLAG_REG
" 1\n"
1895 ".de " TABLE_RELEASE_MACRO_NAME
"\n"
1896 ".if \\n[" TABLE_DIVERSION_FLAG_REG
"] \\{.br\n"
1898 ".nr " SAVED_DN_REG
" \\n[dn]\n"
1899 ".ne \\n[dn]u+\\n[.V]u\n"
1900 ".ie \\n[.t]<=\\n[" SAVED_DN_REG
"] "
1901 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1906 "." TABLE_DIVERSION_NAME
"\n"
1908 ".rm " TABLE_DIVERSION_NAME
"\n"
1916 string
block_width_reg(int r
, int c
)
1918 static char name
[sizeof(BLOCK_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1919 sprintf(name
, BLOCK_WIDTH_PREFIX
"%d,%d", r
, c
);
1920 return string(name
);
1923 string
block_diversion_name(int r
, int c
)
1925 static char name
[sizeof(BLOCK_DIVERSION_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1926 sprintf(name
, BLOCK_DIVERSION_PREFIX
"%d,%d", r
, c
);
1927 return string(name
);
1930 string
block_height_reg(int r
, int c
)
1932 static char name
[sizeof(BLOCK_HEIGHT_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1933 sprintf(name
, BLOCK_HEIGHT_PREFIX
"%d,%d", r
, c
);
1934 return string(name
);
1937 string
span_width_reg(int start_col
, int end_col
)
1939 static char name
[sizeof(SPAN_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1940 sprintf(name
, SPAN_WIDTH_PREFIX
"%d", start_col
);
1941 if (end_col
!= start_col
)
1942 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1943 return string(name
);
1946 string
span_left_numeric_width_reg(int start_col
, int end_col
)
1948 static char name
[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1949 sprintf(name
, SPAN_LEFT_NUMERIC_WIDTH_PREFIX
"%d", start_col
);
1950 if (end_col
!= start_col
)
1951 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1952 return string(name
);
1955 string
span_right_numeric_width_reg(int start_col
, int end_col
)
1957 static char name
[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1958 sprintf(name
, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX
"%d", start_col
);
1959 if (end_col
!= start_col
)
1960 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1961 return string(name
);
1964 string
span_alphabetic_width_reg(int start_col
, int end_col
)
1966 static char name
[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1967 sprintf(name
, SPAN_ALPHABETIC_WIDTH_PREFIX
"%d", start_col
);
1968 if (end_col
!= start_col
)
1969 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1970 return string(name
);
1973 string
column_separation_reg(int col
)
1975 static char name
[sizeof(COLUMN_SEPARATION_PREFIX
)+INT_DIGITS
];
1976 sprintf(name
, COLUMN_SEPARATION_PREFIX
"%d", col
);
1977 return string(name
);
1980 string
row_start_reg(int row
)
1982 static char name
[sizeof(ROW_START_PREFIX
)+INT_DIGITS
];
1983 sprintf(name
, ROW_START_PREFIX
"%d", row
);
1984 return string(name
);
1987 string
column_start_reg(int col
)
1989 static char name
[sizeof(COLUMN_START_PREFIX
)+INT_DIGITS
];
1990 sprintf(name
, COLUMN_START_PREFIX
"%d", col
);
1991 return string(name
);
1994 string
column_end_reg(int col
)
1996 static char name
[sizeof(COLUMN_END_PREFIX
)+INT_DIGITS
];
1997 sprintf(name
, COLUMN_END_PREFIX
"%d", col
);
1998 return string(name
);
2001 string
column_divide_reg(int col
)
2003 static char name
[sizeof(COLUMN_DIVIDE_PREFIX
)+INT_DIGITS
];
2004 sprintf(name
, COLUMN_DIVIDE_PREFIX
"%d", col
);
2005 return string(name
);
2008 string
row_top_reg(int row
)
2010 static char name
[sizeof(ROW_TOP_PREFIX
)+INT_DIGITS
];
2011 sprintf(name
, ROW_TOP_PREFIX
"%d", row
);
2012 return string(name
);
2015 void init_span_reg(int start_col
, int end_col
)
2017 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
2018 span_width_reg(start_col
, end_col
),
2019 span_alphabetic_width_reg(start_col
, end_col
),
2020 span_left_numeric_width_reg(start_col
, end_col
),
2021 span_right_numeric_width_reg(start_col
, end_col
));
2024 void compute_span_width(int start_col
, int end_col
)
2026 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
2027 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
2028 span_width_reg(start_col
, end_col
),
2029 span_left_numeric_width_reg(start_col
, end_col
),
2030 span_right_numeric_width_reg(start_col
, end_col
),
2031 span_alphabetic_width_reg(start_col
, end_col
));
2034 // Increase the widths of columns so that the width of any spanning entry
2035 // is not greater than the sum of the widths of the columns that it spans.
2036 // Ensure that the widths of columns remain equal.
2038 void table::divide_span(int start_col
, int end_col
)
2040 assert(end_col
> start_col
);
2041 printfs(".nr " NEEDED_REG
" \\n[%1]-(\\n[%2]",
2042 span_width_reg(start_col
, end_col
),
2043 span_width_reg(start_col
, start_col
));
2045 for (i
= start_col
+ 1; i
<= end_col
; i
++) {
2046 // The column separation may shrink with the expand option.
2047 if (!(flags
& EXPAND
))
2048 printfs("+%1n", as_string(column_separation
[i
- 1]));
2049 printfs("+\\n[%1]", span_width_reg(i
, i
));
2052 printfs(".nr " NEEDED_REG
" \\n[" NEEDED_REG
"]/%1\n",
2053 as_string(end_col
- start_col
+ 1));
2054 prints(".if \\n[" NEEDED_REG
"] \\{");
2055 for (i
= start_col
; i
<= end_col
; i
++)
2056 printfs(".nr %1 +\\n[" NEEDED_REG
"]\n",
2057 span_width_reg(i
, i
));
2059 for (i
= start_col
; i
<= end_col
&& !equal_flag
; i
++)
2060 if (equal
[i
] || expand
[i
])
2063 for (i
= 0; i
< ncolumns
; i
++)
2064 if (i
< start_col
|| i
> end_col
)
2065 printfs(".nr %1 +\\n[" NEEDED_REG
"]\n",
2066 span_width_reg(i
, i
));
2071 void table::sum_columns(int start_col
, int end_col
, int do_expand
)
2073 assert(end_col
> start_col
);
2075 for (i
= start_col
; i
<= end_col
; i
++)
2086 printfs(".nr %1 \\n[%2]",
2087 span_width_reg(start_col
, end_col
),
2088 span_width_reg(start_col
, start_col
));
2089 for (i
= start_col
+ 1; i
<= end_col
; i
++)
2090 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG
"])+\\n[%2]",
2091 as_string(column_separation
[i
- 1]),
2092 span_width_reg(i
, i
));
2096 horizontal_span::horizontal_span(int sc
, int ec
, horizontal_span
*p
)
2097 : next(p
), start_col(sc
), end_col(ec
)
2101 void table::build_span_list()
2104 table_entry
*p
= entry_list
;
2106 if (p
->end_col
!= p
->start_col
) {
2108 for (q
= span_list
; q
; q
= q
->next
)
2109 if (q
->start_col
== p
->start_col
2110 && q
->end_col
== p
->end_col
)
2113 span_list
= new horizontal_span(p
->start_col
, p
->end_col
, span_list
);
2117 // Now sort span_list primarily by order of end_row, and secondarily
2118 // by reverse order of start_row. This ensures that if we divide
2119 // spans using the order in span_list, we will get reasonable results.
2120 horizontal_span
*unsorted
= span_list
;
2123 horizontal_span
**pp
;
2124 for (pp
= &span_list
; *pp
; pp
= &(*pp
)->next
)
2125 if (unsorted
->end_col
< (*pp
)->end_col
2126 || (unsorted
->end_col
== (*pp
)->end_col
2127 && (unsorted
->start_col
> (*pp
)->start_col
)))
2129 horizontal_span
*tem
= unsorted
->next
;
2130 unsorted
->next
= *pp
;
2136 void table::compute_expand_width()
2139 int colcount
= count_expand_columns();
2140 prints(".nr " EXPAND_REG
" \\n[.l]-\\n[.i]");
2141 for (i
= 0; i
< ncolumns
; i
++)
2143 printfs("-\\n[%1]", span_width_reg(i
, i
));
2144 if (total_separation
)
2145 printfs("-%1n", as_string(total_separation
));
2147 prints(".if \\n[" EXPAND_REG
"]<0 \\{");
2148 entry_list
->set_location();
2149 if (!(flags
& NOWARN
))
2151 ". tm warning: file \"\\n[.F]\", near line \\n[.c]:\n"
2152 ". tm1 \" table wider than line width\n");
2154 ". nr " EXPAND_REG
" 0\n"
2157 printfs(".nr " EXPAND_REG
" \\n[" EXPAND_REG
"]/%1\n",
2158 as_string(colcount
));
2159 for (i
= 0; i
< ncolumns
; i
++)
2161 printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG
"]\n", span_width_reg(i
, i
));
2164 void table::compute_total_separation()
2166 if (flags
& (ALLBOX
| BOX
| DOUBLEBOX
))
2167 left_separation
= right_separation
= 1;
2169 for (int i
= 0; i
< nrows
; i
++) {
2170 if (vline
[i
][0] > 0)
2171 left_separation
= 1;
2172 if (vline
[i
][ncolumns
] > 0)
2173 right_separation
= 1;
2176 total_separation
= left_separation
+ right_separation
;
2178 for (i
= 0; i
< ncolumns
- 1; i
++)
2179 total_separation
+= column_separation
[i
];
2182 void table::compute_separation_factor()
2184 // Don't let the separation factor be negative.
2185 prints(".nr " SEPARATION_FACTOR_REG
" \\n[.l]-\\n[.i]");
2186 for (int i
= 0; i
< ncolumns
; i
++)
2187 printfs("-\\n[%1]", span_width_reg(i
, i
));
2188 printfs("/%1\n", as_string(total_separation
));
2189 prints(".ie \\n[" SEPARATION_FACTOR_REG
"]<=0 \\{");
2190 entry_list
->set_location();
2191 if (!(flags
& NOWARN
))
2193 ". tm warning: file \"\\n[.F]\", around line \\n[.c]:\n"
2194 ". tm1 \" column separation set to zero\n");
2195 prints(". nr " SEPARATION_FACTOR_REG
" 0\n"
2197 ".el .if \\n[" SEPARATION_FACTOR_REG
"]<1n \\{");
2198 entry_list
->set_location();
2199 if (!(flags
& NOWARN
))
2201 ". tm warning: file \"\\n[.F]\", around line \\n[.c]:\n"
2202 ". tm1 \" table squeezed horizontally to fit line length\n");
2206 void table::compute_column_positions()
2208 printfs(".nr %1 0\n", column_divide_reg(0));
2209 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG
"]\n",
2210 column_start_reg(0),
2211 as_string(left_separation
));
2214 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2215 column_end_reg(i
-1),
2216 column_start_reg(i
-1),
2217 span_width_reg(i
-1, i
-1));
2220 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG
"])\n",
2221 column_start_reg(i
),
2222 column_end_reg(i
-1),
2223 as_string(column_separation
[i
-1]));
2224 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2225 column_divide_reg(i
),
2226 column_end_reg(i
-1),
2227 column_start_reg(i
));
2229 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG
"])\n",
2230 column_divide_reg(ncolumns
),
2231 column_end_reg(i
-1),
2232 as_string(right_separation
));
2233 printfs(".nr TW \\n[%1]\n",
2234 column_divide_reg(ncolumns
));
2235 if (flags
& DOUBLEBOX
) {
2236 printfs(".nr %1 +" DOUBLE_LINE_SEP
"\n", column_divide_reg(0));
2237 printfs(".nr %1 -" DOUBLE_LINE_SEP
"\n", column_divide_reg(ncolumns
));
2241 void table::make_columns_equal()
2243 int first
= -1; // index of first equal column
2245 for (i
= 0; i
< ncolumns
; i
++)
2248 printfs(".nr %1 \\n[%1]", span_width_reg(i
, i
));
2252 printfs(">?\\n[%1]", span_width_reg(i
, i
));
2256 for (i
= first
+ 1; i
< ncolumns
; i
++)
2258 printfs(".nr %1 \\n[%2]\n",
2259 span_width_reg(i
, i
),
2260 span_width_reg(first
, first
));
2264 void table::compute_widths()
2269 // These values get refined later.
2270 prints(".nr " SEPARATION_FACTOR_REG
" 1n\n");
2271 for (i
= 0; i
< ncolumns
; i
++) {
2272 init_span_reg(i
, i
);
2273 if (!minimum_width
[i
].empty())
2274 printfs(".nr %1 (n;%2)\n", span_width_reg(i
, i
), minimum_width
[i
]);
2276 for (p
= span_list
; p
; p
= p
->next
)
2277 init_span_reg(p
->start_col
, p
->end_col
);
2278 // Compute all field widths except for blocks.
2280 for (q
= entry_list
; q
; q
= q
->next
)
2281 if (!q
->mod
->zero_width
)
2283 // Compute all span widths, not handling blocks yet.
2284 for (i
= 0; i
< ncolumns
; i
++)
2285 compute_span_width(i
, i
);
2286 for (p
= span_list
; p
; p
= p
->next
)
2287 compute_span_width(p
->start_col
, p
->end_col
);
2288 // Making columns equal normally increases the width of some columns.
2289 make_columns_equal();
2290 // Note that divide_span keeps equal width columns equal.
2291 // This function might increase the width of some columns, too.
2292 for (p
= span_list
; p
; p
= p
->next
)
2293 divide_span(p
->start_col
, p
->end_col
);
2294 compute_total_separation();
2295 for (p
= span_list
; p
; p
= p
->next
)
2296 sum_columns(p
->start_col
, p
->end_col
, 0);
2297 // Now handle unexpanded blocks.
2298 int had_spanning_block
= 0;
2299 int had_equal_block
= 0;
2300 for (q
= entry_list
; q
; q
= q
->next
)
2301 if (q
->divert(ncolumns
, minimum_width
,
2302 (flags
& EXPAND
) ? column_separation
: 0, 0)) {
2303 if (q
->end_col
> q
->start_col
)
2304 had_spanning_block
= 1;
2305 for (i
= q
->start_col
; i
<= q
->end_col
&& !had_equal_block
; i
++)
2307 had_equal_block
= 1;
2310 if (had_equal_block
)
2311 make_columns_equal();
2312 if (had_spanning_block
)
2313 for (p
= span_list
; p
; p
= p
->next
)
2314 divide_span(p
->start_col
, p
->end_col
);
2315 compute_expand_width();
2316 if ((flags
& EXPAND
) && total_separation
!= 0) {
2317 compute_separation_factor();
2318 for (p
= span_list
; p
; p
= p
->next
)
2319 sum_columns(p
->start_col
, p
->end_col
, 0);
2322 // Handle expanded blocks.
2323 for (p
= span_list
; p
; p
= p
->next
)
2324 sum_columns(p
->start_col
, p
->end_col
, 1);
2325 for (q
= entry_list
; q
; q
= q
->next
)
2326 if (q
->divert(ncolumns
, minimum_width
, 0, 1)) {
2327 if (q
->end_col
> q
->start_col
)
2328 had_spanning_block
= 1;
2330 // Adjust widths again.
2331 if (had_spanning_block
)
2332 for (p
= span_list
; p
; p
= p
->next
)
2333 divide_span(p
->start_col
, p
->end_col
);
2335 compute_column_positions();
2338 void table::print_single_hline(int r
)
2340 prints(".vs " LINE_SEP
">?\\n[.V]u\n"
2342 "\\v'" BODY_DEPTH
"'"
2343 "\\s[\\n[" LINESIZE_REG
"]]");
2345 prints("\\D'l |\\n[TW]u 0'");
2349 while (start_col
< ncolumns
2350 && entry
[r
][start_col
] != 0
2351 && entry
[r
][start_col
]->start_row
!= r
)
2354 for (end_col
= start_col
;
2356 && (entry
[r
][end_col
] == 0
2357 || entry
[r
][end_col
]->start_row
== r
);
2360 if (end_col
<= start_col
)
2362 printfs("\\h'|\\n[%1]u",
2363 column_divide_reg(start_col
));
2364 if ((r
> 0 && vline
[r
-1][start_col
] == 2)
2365 || (r
< nrows
&& vline
[r
][start_col
] == 2))
2366 prints("-" HALF_DOUBLE_LINE_SEP
);
2368 printfs("\\D'l |\\n[%1]u",
2369 column_divide_reg(end_col
));
2370 if ((r
> 0 && vline
[r
-1][end_col
] == 2)
2371 || (r
< nrows
&& vline
[r
][end_col
] == 2))
2372 prints("+" HALF_DOUBLE_LINE_SEP
);
2374 start_col
= end_col
;
2382 void table::print_double_hline(int r
)
2384 prints(".vs " LINE_SEP
"+" DOUBLE_LINE_SEP
2387 "\\v'" BODY_DEPTH
"'"
2388 "\\s[\\n[" LINESIZE_REG
"]]");
2390 prints("\\v'-" DOUBLE_LINE_SEP
"'"
2391 "\\D'l |\\n[TW]u 0'"
2392 "\\v'" DOUBLE_LINE_SEP
"'"
2394 "\\D'l |\\n[TW]u 0'");
2398 while (start_col
< ncolumns
2399 && entry
[r
][start_col
] != 0
2400 && entry
[r
][start_col
]->start_row
!= r
)
2403 for (end_col
= start_col
;
2405 && (entry
[r
][end_col
] == 0
2406 || entry
[r
][end_col
]->start_row
== r
);
2409 if (end_col
<= start_col
)
2411 const char *left_adjust
= 0;
2412 if ((r
> 0 && vline
[r
-1][start_col
] == 2)
2413 || (r
< nrows
&& vline
[r
][start_col
] == 2))
2414 left_adjust
= "-" HALF_DOUBLE_LINE_SEP
;
2415 const char *right_adjust
= 0;
2416 if ((r
> 0 && vline
[r
-1][end_col
] == 2)
2417 || (r
< nrows
&& vline
[r
][end_col
] == 2))
2418 right_adjust
= "+" HALF_DOUBLE_LINE_SEP
;
2419 printfs("\\v'-" DOUBLE_LINE_SEP
"'"
2421 column_divide_reg(start_col
));
2423 prints(left_adjust
);
2425 printfs("\\D'l |\\n[%1]u",
2426 column_divide_reg(end_col
));
2428 prints(right_adjust
);
2430 printfs("\\v'" DOUBLE_LINE_SEP
"'"
2432 column_divide_reg(start_col
));
2434 prints(left_adjust
);
2436 printfs("\\D'l |\\n[%1]u",
2437 column_divide_reg(end_col
));
2439 prints(right_adjust
);
2441 start_col
= end_col
;
2449 void table::compute_vrule_top_adjust(int start_row
, int col
, string
&result
)
2451 if (row_is_all_lines
[start_row
] && start_row
< nrows
- 1) {
2452 if (row_is_all_lines
[start_row
] == 2)
2453 result
= LINE_SEP
">?\\n[.V]u" "+" DOUBLE_LINE_SEP
;
2455 result
= LINE_SEP
">?\\n[.V]u";
2462 for (stuff
*p
= stuff_list
; p
&& p
->row
<= start_row
; p
= p
->next
)
2463 if (p
->row
== start_row
2464 && (p
->is_single_line() || p
->is_double_line()))
2469 table_entry
*e
= entry
[start_row
-1][col
-1];
2470 if (e
&& e
->start_row
== e
->end_row
) {
2471 if (e
->to_double_line_entry() != 0)
2473 else if (e
->to_single_line_entry() != 0)
2478 if (col
< ncolumns
) {
2479 table_entry
*e
= entry
[start_row
-1][col
];
2480 if (e
&& e
->start_row
== e
->end_row
) {
2481 if (e
->to_double_line_entry() != 0)
2483 else if (e
->to_single_line_entry() != 0)
2487 if (row_is_all_lines
[start_row
-1] == 0) {
2488 if (left
> 0 || right
> 0) {
2489 result
+= "-" BODY_DEPTH
"-" BAR_HEIGHT
;
2490 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2491 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2492 else if (left
== 2 && right
== 2)
2493 result
+= "+" HALF_DOUBLE_LINE_SEP
;
2496 else if (row_is_all_lines
[start_row
-1] == 2) {
2497 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2498 result
+= "-" DOUBLE_LINE_SEP
;
2499 else if (left
== 1 || right
== 1)
2500 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2504 void table::compute_vrule_bot_adjust(int end_row
, int col
, string
&result
)
2506 if (row_is_all_lines
[end_row
] && end_row
> 0) {
2512 for (p
= stuff_list
; p
&& p
->row
< end_row
+ 1; p
= p
->next
)
2514 if (p
&& p
->row
== end_row
+ 1 && p
->is_double_line()) {
2515 result
= "-" DOUBLE_LINE_SEP
;
2518 if ((p
!= 0 && p
->row
== end_row
+ 1)
2519 || end_row
== nrows
- 1) {
2523 if (row_is_all_lines
[end_row
+1] == 1)
2525 else if (row_is_all_lines
[end_row
+1] == 2)
2526 result
= LINE_SEP
"+" DOUBLE_LINE_SEP
;
2532 table_entry
*e
= entry
[end_row
+1][col
-1];
2533 if (e
&& e
->start_row
== e
->end_row
) {
2534 if (e
->to_double_line_entry() != 0)
2536 else if (e
->to_single_line_entry() != 0)
2541 if (col
< ncolumns
) {
2542 table_entry
*e
= entry
[end_row
+1][col
];
2543 if (e
&& e
->start_row
== e
->end_row
) {
2544 if (e
->to_double_line_entry() != 0)
2546 else if (e
->to_single_line_entry() != 0)
2550 if (row_is_all_lines
[end_row
+1] == 0) {
2551 if (left
> 0 || right
> 0) {
2552 result
= "1v-" BODY_DEPTH
"-" BAR_HEIGHT
;
2553 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2554 result
+= "+" HALF_DOUBLE_LINE_SEP
;
2555 else if (left
== 2 && right
== 2)
2556 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2559 else if (row_is_all_lines
[end_row
+1] == 2) {
2560 if (left
== 2 && right
== 2)
2561 result
+= "-" DOUBLE_LINE_SEP
;
2562 else if (left
!= 2 && right
!= 2 && (left
== 1 || right
== 1))
2563 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2567 void table::add_vertical_rule(int start_row
, int end_row
,
2568 int col
, int is_double
)
2570 vrule_list
= new vertical_rule(start_row
, end_row
, col
, is_double
,
2572 compute_vrule_top_adjust(start_row
, col
, vrule_list
->top_adjust
);
2573 compute_vrule_bot_adjust(end_row
, col
, vrule_list
->bot_adjust
);
2576 void table::build_vrule_list()
2579 if (flags
& ALLBOX
) {
2580 for (col
= 1; col
< ncolumns
; col
++) {
2583 while (start_row
< nrows
&& vline_spanned(start_row
, col
))
2585 if (start_row
>= nrows
)
2587 int end_row
= start_row
;
2588 while (end_row
< nrows
&& !vline_spanned(end_row
, col
))
2591 add_vertical_rule(start_row
, end_row
, col
, 0);
2592 start_row
= end_row
+ 1;
2596 if (flags
& (BOX
| ALLBOX
| DOUBLEBOX
)) {
2597 add_vertical_rule(0, nrows
- 1, 0, 0);
2598 add_vertical_rule(0, nrows
- 1, ncolumns
, 0);
2600 for (int end_row
= 0; end_row
< nrows
; end_row
++)
2601 for (col
= 0; col
< ncolumns
+1; col
++)
2602 if (vline
[end_row
][col
] > 0
2603 && !vline_spanned(end_row
, col
)
2604 && (end_row
== nrows
- 1
2605 || vline
[end_row
+1][col
] != vline
[end_row
][col
]
2606 || vline_spanned(end_row
+1, col
))) {
2608 for (start_row
= end_row
- 1;
2610 && vline
[start_row
][col
] == vline
[end_row
][col
]
2611 && !vline_spanned(start_row
, col
);
2615 add_vertical_rule(start_row
, end_row
, col
, vline
[end_row
][col
] > 1);
2617 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2619 for (int r
= p
->start_row
; r
<= p
->end_row
; r
++) {
2620 if (p
->col
> 0 && entry
[r
][p
->col
-1] != 0
2621 && entry
[r
][p
->col
-1]->end_col
== p
->col
-1) {
2622 int is_corner
= r
== p
->start_row
|| r
== p
->end_row
;
2623 entry
[r
][p
->col
-1]->note_double_vrule_on_right(is_corner
);
2625 if (p
->col
< ncolumns
&& entry
[r
][p
->col
] != 0
2626 && entry
[r
][p
->col
]->start_col
== p
->col
) {
2627 int is_corner
= r
== p
->start_row
|| r
== p
->end_row
;
2628 entry
[r
][p
->col
]->note_double_vrule_on_left(is_corner
);
2633 void table::define_bottom_macro()
2637 ".if !\\n[" SUPPRESS_BOTTOM_REG
"] \\{"
2638 "." REPEATED_VPT_MACRO
" 0\n"
2639 ".mk " SAVED_VERTICAL_POS_REG
"\n");
2640 if (flags
& (BOX
| ALLBOX
| DOUBLEBOX
)) {
2641 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG
"] \\{");
2642 print_single_hline(0);
2646 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2647 p
->contribute_to_bottom_macro(this);
2648 if (flags
& DOUBLEBOX
)
2649 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP
">?\\n[.V]u\n"
2650 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]"
2651 "\\D'l \\n[TW]u 0'\\s0\n"
2654 ".if \\n[" LAST_PASSED_ROW_REG
"]>=0 "
2655 ".nr " TOP_REG
" \\n[#T]-" DOUBLE_LINE_SEP
"\n"
2657 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]"
2658 "\\D'l 0 |\\n[" TOP_REG
"]u-1v'\\s0\n"
2660 "\\v'" BODY_DEPTH
"'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG
"]]"
2661 "\\D'l 0 |\\n[" TOP_REG
"]u-1v'\\s0\n");
2663 prints(".nr " LAST_PASSED_ROW_REG
" \\n[" CURRENT_ROW_REG
"]\n"
2664 ".sp |\\n[" SAVED_VERTICAL_POS_REG
"]u\n"
2665 "." REPEATED_VPT_MACRO
" 1\n"
2671 // is the vertical line before column c in row r horizontally spanned?
2673 int table::vline_spanned(int r
, int c
)
2675 assert(r
>= 0 && r
< nrows
&& c
>= 0 && c
< ncolumns
+ 1);
2676 return (c
!= 0 && c
!= ncolumns
&& entry
[r
][c
] != 0
2677 && entry
[r
][c
]->start_col
!= c
2678 // horizontally spanning lines don't count
2679 && entry
[r
][c
]->to_double_line_entry() == 0
2680 && entry
[r
][c
]->to_single_line_entry() == 0);
2683 int table::row_begins_section(int r
)
2685 assert(r
>= 0 && r
< nrows
);
2686 for (int i
= 0; i
< ncolumns
; i
++)
2687 if (entry
[r
][i
] && entry
[r
][i
]->start_row
!= r
)
2692 int table::row_ends_section(int r
)
2694 assert(r
>= 0 && r
< nrows
);
2695 for (int i
= 0; i
< ncolumns
; i
++)
2696 if (entry
[r
][i
] && entry
[r
][i
]->end_row
!= r
)
2701 void table::do_row(int r
)
2703 if (!(flags
& NOKEEP
) && row_begins_section(r
))
2704 prints("." KEEP_MACRO_NAME
"\n");
2707 for (p
= stuff_list
; p
&& p
->row
< r
; p
= p
->next
)
2709 for (stuff
*p1
= p
; p1
&& p1
->row
== r
; p1
= p1
->next
)
2710 if (!p1
->printed
&& (p1
->is_single_line() || p1
->is_double_line())) {
2714 if (!had_line
&& !row_is_all_lines
[r
])
2715 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2717 for (; p
&& p
->row
== r
; p
= p
->next
)
2720 if (!had_line
&& (p
->is_single_line() || p
->is_double_line())) {
2721 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2725 // change the row *after* printing the stuff list (which might contain .TH)
2726 printfs("\\*[" TRANSPARENT_STRING_NAME
"].nr " CURRENT_ROW_REG
" %1\n",
2728 if (!had_line
&& row_is_all_lines
[r
])
2729 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2730 // we might have had a .TH, for example, since we last tried
2731 if (!(flags
& NOKEEP
) && row_begins_section(r
))
2732 prints("." KEEP_MACRO_NAME
"\n");
2733 printfs(".mk %1\n", row_start_reg(r
));
2734 prints(".mk " BOTTOM_REG
"\n"
2735 "." REPEATED_VPT_MACRO
" 0\n");
2737 int row_is_blank
= 1;
2738 int first_start_row
= r
;
2739 for (c
= 0; c
< ncolumns
; c
++) {
2740 table_entry
*e
= entry
[r
][c
];
2742 if (e
->end_row
== r
) {
2744 if (e
->start_row
< first_start_row
)
2745 first_start_row
= e
->start_row
;
2752 prints(".nr " BOTTOM_REG
" +1v\n");
2753 if (row_is_all_lines
[r
]) {
2754 prints(".vs " LINE_SEP
);
2755 if (row_is_all_lines
[r
] == 2)
2756 prints("+" DOUBLE_LINE_SEP
);
2757 prints(">?\\n[.V]u\n.ls 1\n");
2759 prints("\\v'" BODY_DEPTH
);
2760 if (row_is_all_lines
[r
] == 2)
2761 prints("-" HALF_DOUBLE_LINE_SEP
);
2763 for (c
= 0; c
< ncolumns
; c
++) {
2764 table_entry
*e
= entry
[r
][c
];
2766 if (e
->end_row
== e
->start_row
)
2767 e
->to_simple_entry()->simple_print(1);
2774 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2775 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2777 for (int i
= row_is_all_lines
[r
] ? r
- 1 : r
;
2778 i
>= first_start_row
;
2780 simple_entry
*first
= 0;
2781 for (c
= 0; c
< ncolumns
; c
++) {
2782 table_entry
*e
= entry
[r
][c
];
2784 if (e
->end_row
== r
&& e
->start_row
== i
) {
2785 simple_entry
*simple
= e
->to_simple_entry();
2799 first
->position_vertically();
2800 first
->set_location();
2802 first
->simple_print(0);
2803 for (c
= first
->end_col
+ 1; c
< ncolumns
; c
++) {
2804 table_entry
*e
= entry
[r
][c
];
2806 if (e
->end_row
== r
&& e
->start_row
== i
) {
2807 simple_entry
*simple
= e
->to_simple_entry();
2809 if (e
->end_row
!= e
->start_row
) {
2811 simple
->position_vertically();
2814 simple
->simple_print(0);
2821 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2822 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2825 for (c
= 0; c
< ncolumns
; c
++) {
2826 table_entry
*e
= entry
[r
][c
];
2828 if (e
->end_row
== r
&& e
->to_simple_entry() == 0) {
2829 e
->position_vertically();
2831 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2832 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2837 prints("." REPEATED_VPT_MACRO
" 1\n"
2838 ".sp |\\n[" BOTTOM_REG
"]u\n"
2839 "\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
" 1\n");
2840 if (r
!= nrows
- 1 && (flags
& ALLBOX
)) {
2841 print_single_hline(r
+ 1);
2842 prints("\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
" 0\n");
2844 if (r
!= nrows
- 1) {
2845 if (p
&& p
->row
== r
+ 1
2846 && (p
->is_single_line() || p
->is_double_line())) {
2848 prints("\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
2851 int printed_one
= 0;
2852 for (vertical_rule
*vr
= vrule_list
; vr
; vr
= vr
->next
)
2853 if (vr
->end_row
== r
) {
2855 prints("." REPEATED_VPT_MACRO
" 0\n");
2861 prints("." REPEATED_VPT_MACRO
" 1\n");
2862 if (!(flags
& NOKEEP
) && row_ends_section(r
))
2863 prints("." RELEASE_MACRO_NAME
"\n");
2867 void table::do_top()
2869 prints(".fc \002\003\n");
2870 if (!(flags
& NOKEEP
) && (flags
& (BOX
| DOUBLEBOX
| ALLBOX
)))
2871 prints("." TABLE_KEEP_MACRO_NAME
"\n");
2872 if (flags
& DOUBLEBOX
) {
2874 ".vs " LINE_SEP
">?\\n[.V]u\n"
2875 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]\\D'l \\n[TW]u 0'\\s0\n"
2877 "." REPEATED_MARK_MACRO
" " TOP_REG
"\n"
2878 ".vs " DOUBLE_LINE_SEP
">?\\n[.V]u\n");
2879 printfs("\\v'" BODY_DEPTH
"'"
2880 "\\s[\\n[" LINESIZE_REG
"]]"
2882 "\\D'l |\\n[%2]u 0'"
2885 column_divide_reg(0),
2886 column_divide_reg(ncolumns
));
2890 else if (flags
& (ALLBOX
| BOX
)) {
2891 print_single_hline(0);
2893 //printfs(".mk %1\n", row_top_reg(0));
2896 void table::do_bottom()
2898 // print stuff after last row
2899 for (stuff
*p
= stuff_list
; p
; p
= p
->next
)
2900 if (p
->row
> nrows
- 1)
2902 if (!(flags
& NOKEEP
))
2903 prints("." RELEASE_MACRO_NAME
"\n");
2904 printfs(".mk %1\n", row_top_reg(nrows
));
2905 prints(".nr " NEED_BOTTOM_RULE_REG
" 1\n"
2908 if (!(flags
& NOKEEP
) && (flags
& (BOX
| DOUBLEBOX
| ALLBOX
)))
2909 prints("." TABLE_RELEASE_MACRO_NAME
"\n");
2910 if (flags
& DOUBLEBOX
)
2911 prints(".sp " DOUBLE_LINE_SEP
"\n");
2912 prints("." RESET_MACRO_NAME
"\n"
2914 ".cp \\n(" COMPATIBLE_REG
"\n");
2917 int table::get_nrows()
2922 const char *last_filename
= 0;
2924 void set_troff_location(const char *fn
, int ln
)
2926 if (!location_force_filename
&& last_filename
!= 0
2927 && strcmp(fn
, last_filename
) == 0)
2928 printfs(".lf %1\n", as_string(ln
));
2930 printfs(".lf %1 %2\n", as_string(ln
), fn
);
2932 location_force_filename
= 0;
2936 void printfs(const char *s
, const string
&arg1
, const string
&arg2
,
2937 const string
&arg3
, const string
&arg4
, const string
&arg5
)
2941 while ((c
= *s
++) != '\0') {